Repository navigation
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.