Repository navigation
Releases: CyLabAcademy/cmgr
Release list
Release v3.1.0
Changes
New Features
-
New
seccomp.profilechallenge option names a JSON profile sitting beside
problem.md, replacing cmgr's embedded policy for that challenge's runtime
containers. Build, artifact, and solver containers keep the embedded policy.
Profiles are validated at load time and contribute to the challenge's source
checksum, so editing one triggers a rebuild. A per-hostoverridesblock does
not inherit a top-level profile. Adds acontainerOptions.seccompcolumn,
migrated automatically on open; challenges selecting no profile are unaffected.- New
examples/seccomp/: the embedded policy as a copyabledefault.json
baseline,execstack.jsonandno-ptrace.json, aprofile-diff.pytool that
reports only a profile's semantic delta from the baseline, and a README on
what the default policy already permits.examples/execstack/is a worked
challenge for theREAD_IMPLIES_EXECcase.
- New
-
Artifact archives from build containers are now validated on ingest instead of
repacked verbatim: path traversal, absolute paths, symlinks, hardlinks, device
nodes and duplicate entries are rejected, ownership and xattrs are stripped,
and entry count, per-file and total decompressed size are bounded (including
tar metadata records). Archives stage to a temp file and rename into place, so
a failed build can no longer replace a good archive with a partial one. Limits
are tunable via CMGR_MAX_ARTIFACT_FILES, CMGR_MAX_ARTIFACT_BYTES, and
CMGR_MAX_ARTIFACT_FILE_BYTES. Ported from ArmyCyberInstitute/cmgr bdbf839.
Updates
Dependencies: dropped github.com/docker/cli in favor of an in-tree NanoCPU
parser, migrated gopkg.in/yaml.v2 to go.yaml.in/yaml/v3, and bumped
go-sqlite3, goldmark, and moby/moby/client.
Release v3.0.0
Changes
Breaking
-
Challenges that are not services no longer get instances.
cmgrnow derives a
delivery_typefor every challenge (service,artifact_only, orflag_only) from
whether it publishes ports and which challenge type it uses. Onlyservicechallenges
run instances: builds of artifact-only and flag-only challenges always report an empty
instanceslist, andPOST /builds/<id>(start instance) fails for them. Previously
every build launched a placeholder container.- API consumers must branch on
delivery_type. An empty instance list is only a
deployment problem whendelivery_typeisserviceandinstance_countis
positive; on-demand builds (instance_countof-1) legitimately have zero
instances between user sessions. - Placeholder instances left over from older versions are torn down on the next schema
update or challenge rebuild. cmgr testandcmgr playtestskip the start/stop steps for non-service challenges
and run the solver directly against the build.
- API consumers must branch on
-
Docker image tags are now content-addressed. Tags changed from
<challenge>:<build_id>-<host>to<challenge>:s<seed>-<checksum>-<host>, where the
checksum covers the challenge source and flag format. Build IDs are local database
handles and no longer appear in tags, so rebuilt content gets a new tag instead of
mutating one in place (making challenge images safe to push to a registry). Anything
that reconstructs image tags from build IDs must be updated. Existing images are
retagged automatically by a one-time, resumable migration on first start. -
Built-in challenge types now build on
ubuntu:24.04(wasubuntu:20.04). This
affectsremote-make,static-make, and the solver image. Existing challenges may
need updating for OpenSSL 3.x, PEP 668 (pipnow runs with
--break-system-packages), and renamed distro packages.
New features
flag-onlychallenge type for challenges that are nothing but a submission prompt
— no service, no downloads (knowledge checks, second flags exported by a sibling
challenge). The build's only product ismetadata.json; aMakefileis optional, and
theSEEDbuild argument allows per-build shuffling of option lists. Exempt from
cmgr test --require-solve, since the flag is the answer. See
examples/flag-only/.POST /builds/<id>/checkruns the automated solver against a non-service build
without an instance, mirroring the existing instance check. Service builds are
rejected.cmgr test --require-solveuses the same path.cmgr update --prune-olduntags the image generation each rebuild displaces from
its rollback slot. Off by default, since anothercmgrdatabase on the same Docker
daemon may reference those tags. Removal is refcount-guarded; the current and previous
generations are always retained.cmgr test --keep-imagesretains build images until the whole run finishes so
challenges can share base layers — a large speedup when testing a directory of
challenges. Used by CI.- Builds now report
checksumandprev_checksum(the retained rollback generation)
through the API, and images carrycmgr.managed/cmgr.challengelabels. cmgr updateprints a delivery-type summary, so a challenge that is unexpectedly
artifact-only (a forgotten# PUBLISH) is visible before deployment. A challenge that
publishes neither ports nor artifacts logs a warning.
Fixes
- Docker build failures are no longer swallowed. Modern daemons emit an
errorkey
beforeerrorDetail; the old anchored pattern missed it and surfaced every build,
push, and pull failure as a misleading "No such image". cmgr updatenow warns when a challenge's on-disk source has drifted but its builds
are already complete, closing the gap where updating a schema left stale images in
place.- Artifact extraction works against images with no
CMD, no need totail /dev/nullanymore. - Solvers for artifact-only challenges run on Docker's default bridge, preserving
outbound network access. - Line endings are normalized to LF via
.gitattributes.
Examples and CI
- Example challenges standardized on
ubuntu:24.04and hardened; themultireference
was deduplicated and thestatic-makelockbox builds against OpenSSL 3.0. - The
disksexamples share a slimmedlibguestfsbase image, cutting build time
substantially. - CI checks out before
setup-goso the Go module cache actually hits, and runs the
integration tests with--keep-images.
Dependencies
- Bumped
docker/cli,moby/moby(api + client),mattn/go-sqlite3,yuin/goldmark,
JohannesKaufmann/html-to-markdown,golang.org/x/net, andgolang.org/x/systo
their latest releases.
Release v2.1.3
hotfix for on-demand instance pruning query
Release v2.1.2
Since v2.1.1 (2026-06-02)
Fixes older db migrations
Release v2.1.1
Since v2.1.0 (2026-05-27)
A small performance and build-targets release.
Performance
- Skip port read-back on explicit ports (#67) — when a challenge launches with explicit
CMGR_PORTS, the host ports are already reserved and bound beforeContainerCreate, so the post-startContainerInspect/backoff loop could only re-read values cmgr already knows. That loop is now gated onportLow == 0and skipped on the explicit-port path, removing the inspect round-trips and backoff sleeps from the hot launch path while networking settles. Docker-assigned ephemeral ports still read back as before, and the rebuild flow (which clearsinstance.Portsbefore restarting) is correctly detected as "not yet known" so it continues to read the bound port back. - Cache Docker
OSTypeonce at init (#67) — the daemon'sOSType(used only to decide whether to apply the Linux seccomp profile) is immutable for the daemon's lifetime. It's now fetched once ininitDockerinstead of via a heavyInfocall on every build, launch, and solve.
Hardening
- Container ports with no reverse-port-map entry are now logged and skipped during read-back instead of being written under an empty key, which previously could drop the real mapping or clobber other unmapped ports (#67).
Build & CI
- Dropped the Intel Mac (
darwin_amd64) release target (99fc129) — published binaries now coverlinux_amd64anddarwin_arm64(Apple Silicon). Intel Mac and Linux ARM builds are no longer published; build from source for those platforms.
Testing
- Added unit coverage for the explicit-port skip logic and the unmapped-port edge case (
cmgr/database_operations_test.go, +158 lines).
Documentation
- README install note updated to reflect the published platform targets and source-build guidance for unsupported ones.
Release v2.1.0
Since v2.0.0 (2024-05-06)
Features
- Markdown challenge text (#62, #63, #64) — challenge
description,hint, anddetailsnow flow through as Markdown end-to-end. HTML islands are detected withnet/htmland converted via html-to-markdown v2; LaTeX passes through cleanly. Templated metadata ({{ ... }}) uses custom delimiters so it survives the Markdown round-trip. - Concurrent instance launches (#44) — replaced the port-assignment mutex with a semaphore, allowing two instances to launch in parallel and pipelining work against the Docker daemon.
- Atomic SQLite port reservations (part of #20) — port allocation now happens inside a single SQL transaction, eliminating races under concurrent launches.
- Autoprune for lingering instances (#19) — instance rows orphaned by external container reaping are now pruned on each launch (configurable via
PRUNE_AGE), preventing port-exhaustion spirals. - User-supplied env vars on container launch (#14) —
cmgrdnow accepts a requireduser_idand an optional env-var map, propagated equally to every container in a multi-container challenge. LINUX_IMMUTABLEcapability (#6) — opt-in via challenge metadata.
Performance
Bundled in #20 (numbers below are pre-launch pre-compute time):
| DB size | Before #19 | With #19 | With #20 |
|---|---|---|---|
| 20 | 0.31 ms | 0.32 ms | 0.33 ms |
| 200 | 0.44 ms | 0.42 ms | 0.29 ms |
| 20 000 | 24.91 ms | 26.27 ms | 12.43 ms |
| 50 000 | 60.51 ms | 64.76 ms | 31.43 ms |
- SQLite WAL mode enabled by default, with
_synchronous=NORMALand_busy_timeout=50msDSN tuning. - Replaced the in-memory port map with a compact bitset in
getFreePort. - Added indexes for cascade deletes and
portAssignments(port). - Fixed an N+1 query pattern in
lookupBuildInstances(batched via JOIN, wrapped in a read-only transaction).
Fixes
- On-demand challenge rebuilds (#17) — rebuilds previously restarted on-demand instances with
nilenv vars, silently dropping injected values likeCMGR_USER_ID. They are now torn down on rebuild and must beStart()ed again with proper env. InstanceMetadata.CreatedAtis now*time.Timeso JSON output is RFC3339.- Migration
ALTER TABLEguarded byPRAGMA table_info(idempotent migration on re-run). - Periodic prune log demoted from info to debug.
- Errors in the migration block are no longer swallowed.
Build & CI
- Docker module v25 → v29 (#12, #42) — adapted to breaking v29 API changes (
network.ParsePortwith error handling,netip.ParseAddrerror checks,pushRespclose fix). - Go 1.26 in CI;
go buildruns only on master push. - macOS-latest runners used for arm64 builds.
- GitHub Actions pinned to commit SHAs with a cooldown window for security.
- Dependabot enabled (weekly).
Testing
- Comprehensive unit test suite added (#16) — covers autoprune (
Prune/checkPrune),lookupBuildInstances,usedPortBitset/getFreePort, cache isolation forlookupBuildMetadata/lookupChallengeMetadata, concurrent port reservations, HTML-island boundaries, and LaTeX passthrough.
Documentation
- README and Swagger updated for the env-var launch feature; new
examples/runtime_env_varschallenge. - Documented that env vars broadcast equally to all containers in a multi-container challenge.
- CLI usage info for
PRUNE_AGE.
Dependencies
Routine bumps via Dependabot: docker/cli, moby/moby/client, mattn/go-sqlite3, yuin/goldmark, jmoiron/sqlx, containerd/containerd, go.opentelemetry.io/otel, golang.org/x/net.
Release v2.0.0
Changes
- Challenge types other than
remote-makeandstatic-makeare no longer supported.- Existing challenges of other types can be converted to
customby exporting their Dockerfile (cmgr dockerfile <type>).
- Existing challenges of other types can be converted to
- JSON challenge specifications (
problem.json) are no longer supported. Any existing challenges using this format must be converted to useproblem.mdinstead.
Release v1.2.1
Changes
- Fixed an issue preventing ARM64 builds in GitHub release artifacts from working correctly.
- Linux ARM64 builds are no longer available.
Release v1.2.0
Changes
- The
cmgr update-schemacommand andPOST /schemas/{schema_name}cmgrd endpoint now function as upserts: if the specified schema does not yet exist, a warning is logged and it is created. This allows clients to easily apply updates to schemas without needing to consider whether they already exist (ca62a57)
Release v1.1.1
Changes
- Fixes bugs related to the use of
{{url}}and{{lookup}}templates in the Description field, as introduced in v1.1.0 (50de11d)