Repository navigation
feat(docker): add Network Plumbing world with internal attempt-owned networks - #34
Merged
Merged
Conversation
…networks
Summary
Adds Docker World 3, "Network Plumbing" (Missions 35–39), and the
network support it needs. Players learn that containers can reach each
other only over a network they share. They read network membership,
connect and disconnect existing containers, create a private network,
and finish by segmenting a flat network. Every lab network is internal,
owned by the attempt, labeled, and removed afterwards.
This is Stage 3a of the Docker expansion roadmap. Volumes remain future
work because they attach only at container creation, so they depend on
a limited `docker run`.
Player-facing changes
- New teaching commands, parsed by OpsQuest and never passed through:
`docker network ls`, `inspect NETWORK`, `create NETWORK`,
`rm NETWORK`, `connect NETWORK ALIAS`, and `disconnect NETWORK ALIAS`.
None accept options, and the built-in bridge, host, none, and default
networks are rejected by name.
- `docker inspect ALIAS` now lists a container's logical networks under
NetworkSettings.Networks. `docker network inspect` shows a network's
logical ID, driver, internal flag, and attached containers, with no
engine IDs or generated names.
- `opsquest doctor` now reports "orphaned lab resources", because
leftover networks are counted alongside containers.
- Five new missions (80–140 XP):
- Network Census (beginner): find the network two services share.
- Can't Reach the DB (beginner): connect api to the database network
without exposing the database to the web tier.
- Need to Know (intermediate): disconnect a batch job from a network
it should never have joined.
- Private Channel (intermediate): create a network and move two
services onto it.
- Segmentation (advanced): split a flat network into tiers, then
retire a stale network and the container holding it open.
Mission schema
- A Docker setup can declare up to 8 `networks` by logical name.
Fixtures join up to 4 of them through a new `networks` field. A
fixture without networks keeps `--network none`, so existing missions
behave exactly as before.
- New route-independent conditions:
- `docker_containers_share_network` and `docker_containers_isolated`
take exactly two `containers` and pass only while both still exist.
- `docker_network_absent` takes a declared `network`.
A player who solves a mission by creating their own network also
passes.
Safety and cleanup
- OpsQuest creates every network with fixed options: the bridge driver
and `--internal`, so there is no route to the host network or the
internet. Networks carry the same five ownership labels, advisory
owner labels, and generated names (`opsquest-<session>-nNN`) as
containers.
- Players can create at most 4 networks per attempt. They can only
attach this attempt's containers to this attempt's networks, resolved
to exact IDs.
- `docker network rm` is refused while any container is attached, even
a stopped one. Docker would allow it and leave that container unable
to start.
- Engine errors are rewritten so network and container IDs and
generated names never reach the player.
- `Close` removes containers first, then networks, verifies ownership
labels on each exact ID, and keeps unresolved networks for a retry.
- The orphan janitor now also sweeps networks, after containers, using
the same label, generated-name, owner-process, and 24-hour age
checks.
- The internal flag is not the only control. Players cannot run
commands inside containers (there is no exec, and fixtures run fixed
programs), so no player-controlled traffic starts inside a lab
network. This is documented in the safety model.
Compatibility
- Schema changes are additive, and all existing missions load unchanged.
Older binaries reject the new catalog under strict loading.
- Persisted profiles need no migration: completions are keyed by stable
mission IDs.
- `docker ps` output is unchanged. Container inspect gains a
NetworkSettings.Networks list.
- Catalog: 39 missions (23 Linux, 16 Docker in three worlds).
Documentation
Updates README, AGENTS.md, the docs overview, Docker guide, mission map
(Missions 35–39 and a World 3 section), learning-journey diagram,
safety model (lab networks, new limits, threat rows), mission authoring
guide, mission checklist, and roadmap. The roadmap now lists volumes
and reachability probes as the next increments.
Testing
- make check-all: passed, including race detection and the smoke test
- make docs-check: passed in strict mode
- GOOS=windows and GOOS=linux go vet ./internal/dockerlab: passed
- git diff --check: clean
- OPSQUEST_DOCKER_TEST=1 go test ./internal/dockerlab -run Integration
on Docker Desktop (engine 29.7.2): all 6 integration tests passed.
They cover network connect and internal isolation, the full
Segmentation route, the orphaned network sweep, and, for the first
time against a real engine, Stage 1 health probes, crash loops,
`--tail`, and `rm`.
- After the run: 0 OpsQuest-labeled containers or networks remained,
and `opsquest doctor` reported no orphaned lab resources.
New unit coverage: network creation arguments and order, every command
with its success, edge, and failure cases, parser rejection before any
Docker call, cleanup order and ownership refusal, setup-failure
cleanup, janitor network sweeping, error redaction, schema validation
and deep copies, and canonical, alternative, and incomplete routes for
all five missions.
Aleksandergreg
enabled auto-merge (squash)
September 30, 2026 18:36
Aleksandergreg
pushed a commit
that referenced
this pull request
Sep 30, 2026
🤖 I have created a release *beep* *boop* --- ## [0.8.0](v0.7.1...v0.8.0) (2026-09-30) ### Features * **docker:** add Container Triage world and orphaned lab cleanup ([56dbfe2](56dbfe2)) * **docker:** add Container Triage world and orphaned lab cleanup ([#32](#32)) ([56dbfe2](56dbfe2)) * **docker:** add Network Plumbing world with internal attempt-owned networks ([#34](#34)) ([b4b1ba9](b4b1ba9)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Aleksandergreg
added a commit
that referenced
this pull request
Sep 30, 2026
…#35) ## Problem `isMissingNetwork` treated any Docker output containing "not found" as a missing network. When the selected Docker context is unavailable, the CLI prints `context "x": context not found`, and that was misread as a missing network: - `docker_network_absent` passed while the network still existed. - `Close` dropped the network from its retryable cleanup set, so all lab networks leaked once the context recovered. This fix was committed on `feat/docker-networks` after #34 was squash-merged, so it is not in `main` or in release 0.8.0. ## Fix A network counts as missing only when Docker names the exact reference: - `network <ref> not found` (current engines, verified on Docker 29.7.2); - `No such network: <ref>` (older engines). Every caller passes its exact reference: inspect, `Close`, and the orphan janitor. Context, transport, and CLI failures now propagate as errors. ## Tests - Classification table built from real Docker 29.7.2 messages, including the exact `context not found` output. - Context failure and recovery: validation returns an error, and a retried `Close` removes every network after recovery. - The janitor's network removal path. All three fail against the old substring match. ## Validation (on `main` + this commit) - `make check-all`: passed, including race detection and the smoke test - `OPSQUEST_DOCKER_TEST=1 go test ./internal/dockerlab -run Integration`: all 6 passed on Docker Desktop 29.7.2 - 0 OpsQuest-labeled containers or networks left afterwards
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds Docker World 3, "Network Plumbing" (Missions 35–39), and the network support it needs. Players learn that containers can reach each other only over a network they share. They read network membership, connect and disconnect existing containers, create a private network, and finish by segmenting a flat network. Every lab network is internal, owned by the attempt, labeled, and removed afterwards.
This is Stage 3a of the Docker expansion roadmap. Volumes remain future work because they attach only at container creation, so they depend on a limited
docker run.Player-facing changes
docker network ls,inspect NETWORK,create NETWORK,rm NETWORK,connect NETWORK ALIAS, anddisconnect NETWORK ALIAS. None accept options, and the built-in bridge, host, none, and default networks are rejected by name.docker inspect ALIASnow lists a container's logical networks under NetworkSettings.Networks.docker network inspectshows a network's logical ID, driver, internal flag, and attached containers, with no engine IDs or generated names.opsquest doctornow reports "orphaned lab resources", because leftover networks are counted alongside containers.Mission schema
networksby logical name. Fixtures join up to 4 of them through a newnetworksfield. A fixture without networks keeps--network none, so existing missions behave exactly as before.docker_containers_share_networkanddocker_containers_isolatedtake exactly twocontainersand pass only while both still exist.docker_network_absenttakes a declarednetwork. A player who solves a mission by creating their own network also passes.Safety and cleanup
--internal, so there is no route to the host network or the internet. Networks carry the same five ownership labels, advisory owner labels, and generated names (opsquest-<session>-nNN) as containers.docker network rmis refused while any container is attached, even a stopped one. Docker would allow it and leave that container unable to start.Closeremoves containers first, then networks, verifies ownership labels on each exact ID, and keeps unresolved networks for a retry.Compatibility
docker psoutput is unchanged. Container inspect gains a NetworkSettings.Networks list.Documentation
Updates README, AGENTS.md, the docs overview, Docker guide, mission map (Missions 35–39 and a World 3 section), learning-journey diagram, safety model (lab networks, new limits, threat rows), mission authoring guide, mission checklist, and roadmap. The roadmap now lists volumes and reachability probes as the next increments.
Testing
--tail, andrm.opsquest doctorreported no orphaned lab resources.New unit coverage: network creation arguments and order, every command with its success, edge, and failure cases, parser rejection before any Docker call, cleanup order and ownership refusal, setup-failure cleanup, janitor network sweeping, error redaction, schema validation and deep copies, and canonical, alternative, and incomplete routes for all five missions.
Description
Please provide a summary of the change and explain what problem it solves. Include any relevant motivation and context.
Related Issue:
If applicable, please reference the issue number that this pull request fixes:
Fixes #[issue-number]
Type of Change
Please check the type(s) of changes your code introduces:
How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce the testing environment.
Include any details regarding your testing environment, test configuration, or steps to reproduce.
Checklist
Before creating this pull request, please ensure that your code meets the following requirements:
Screenshots (if applicable)
If your pull request includes visual changes, please include screenshots or animated GIFs to help explain your changes.
Additional Context
Add any other context or screenshots about your pull request here.