lab-runtime-docker v0.2.1
subsystem education.ctf
capsule://capsule-examples/lab-runtime-docker@0.2.1
Runs per-student lab containers on a single Docker host and exposes a
browser-accessible terminal. One lab belongs to exactly one student,
for the duration of one challenge attempt.
Owns
- container lifecycle (create / pause / destroy)
- per-lab metadata persisted to disk
- browser terminal proxy
- lab.created / lab.destroyed events
Does not own
- authentication of students (see auth-core)
- scoring or grading (see ai-report and the scoreboard capsule)
- long-term storage of student artifacts
AI orientation
You are looking at the lab-runtime capsule. It owns container lifecycle
for student CTF labs. It does NOT own authentication — assume an upstream
auth-core capsule has already authorized the caller and passed a verified
`student_id` in the request context. Lab metadata is world-readable to
instructors, so it must never contain secrets.
Avoid
- Calling the Docker socket from HTTP handlers directly; always go through src/runtime/docker_client.py.
- Storing student secrets in lab metadata.
- Re-implementing auth: introspect the bearer token via auth-core.
Extension points
lab-image-resolveratsrc/runtime/images.py:resolve_image- Given (challenge_id, student_id) return a Docker image reference.
Must be deterministic for a given challenge_id; do not branch on
student_id (would break instructor reproduction of student state).
Provides
http_api:lab-controlcli:labctlevent:lab.createdevent:lab.destroyed
Requires
http_api:auth-introspectfromauth-core(>=0.2 <1.0)env:DOCKER_HOST— Docker daemon socket; defaults to unix:///var/run/docker.sock.
Dependencies
Capsules
auth-core>=0.2.0 <1.0.0
Runtime
docker>=24python>=3.11
Invariants (must always hold)
- A lab instance must always belong to exactly one student.
- A student must not access another student's lab container.
- Destroying a lab must revoke all browser-terminal sessions for it.
- Lab metadata must not contain plaintext secrets.
Handoff — work in progress
Objective. Emit lab.created events that the ai-report capsule can consume.
Completed
- Event schema drafted at schemas/lab-created.json.
- Emitter stub in src/runtime/events.py.
Remaining
- Hook emitter into lifecycle.create_lab.
- Add a functional test that asserts the event payload shape.
Next agent should
- Start at src/runtime/lifecycle.py:create_lab and emit the event after the container reaches RUNNING.
Do not touch
- src/runtime/docker_client.py — recently stabilised, no changes needed for this objective.
Open questions
- Should events be fire-and-forget or persisted to an outbox table?
Glossary
lab- a single container instance owned by exactly one student
challenge- a reusable CTF problem definition; spawns labs