# Milestone 8 historical execution record Archived on 2026-10-24 during documentation workflow cleanup. This is a historical snapshot, including superseded and contradictory status statements. Use [PLANS.md](../../PLANS.md) for current state. Evidence below is preserved as previously reported; no checks were rerun by this archival operation. ### Milestone 8 — Alpine Testcontainers integration Status: in progress (2026-10-22; user removed IPv6 capability and traffic testing from M8. The selected disposable daemon remains the test environment; M8.4–M8.5 and real-container acceptance remain unrun). Prerequisites: milestones 1–7 accepted; pinned Rust toolchain; an explicitly selected disposable Docker-compatible Linux environment with kernel WireGuard, scoped NET_ADMIN and IPv4 support; an Alpine-compatible test executable. Missing prerequisites are blockers/incomplete evidence, never passing tests. #### Outcome and scope Add an opt-in Rust Testcontainers suite using small Alpine server/client images. Exercise the actual application, encrypted vault/persistence/delivery APIs, and M5's real rustix Generic Netlink reader inside the server container against real kernel WireGuard interfaces. Establish real tunnels, generate IPv4 traffic, and prove that saved changes remain separate from runtime application. The user explicitly excluded IPv6 capability and traffic tests on 2026-10-22. Production dual-stack behavior in SPEC.md is unchanged; real IPv6 behavior is therefore unverified by this milestone. Read SPEC.md Operations and states, Connection delivery, Backups and restoration, Client screen and statistics, and acceptance criteria; ADRs 0001–0003; TESTING.md, ENVIRONMENT.md, RUNTIME.md, DELIVERY.md, VAULT_FORMAT.md, PERSISTENCE.md, BACKUP_FORMAT.md, DEPENDENCIES.md and current application/worker code. This is real-WireGuard integration, not real-systemd acceptance. Alpine fixtures have no systemd; a test-only apply operation may configure the interface explicitly. Actual zbus service job behavior, production restart and VM acceptance remain M9. M7's synthetic TUI/PTY evidence remains separate; M8 does not require live terminal screens, phone QR decoding, public Internet access, external DNS, production configuration, new product commands, or a production apply mode. #### Existing code and integration constraints - M1–M7 are accepted. The normal executable now starts the management TUI; do not invoke it with host/production settings as a shortcut for a test driver. - Application has open-from-store, setup/import, creation, Commit, delivery/reissue, deletion, backup/list/restore and confirmed restart flows. Use these real APIs, not a parallel shell-only client manager that would leave application behavior untested. Public-key identity and source preservation remain authoritative. - Real observation is `runtime::transport::observe_interface`, currently crate- private, with the validated decoder and bounded rustix transport. Do not use only `wg show` output or a fake observation as evidence that this reader works. - `Application::restart` always invokes the actual zbus path, while injected restart functions are private. Plan an in-crate test seam so a test-only fixture service operation can apply peers and the real observation/persistence path can verify them. Preserve backup ordering and per-attempt authority from M5. - Prefer cfg(test) in-container test scenarios/driver code, compiled for musl and selected explicitly, to reach private adapters without exposing an unauthenticated public apply API. If another arrangement is needed, document its compile-time isolation before implementation. No environment variable may switch production restart to a fake service or bypass password/backup/root checks. - The TUI ApplicationWorker already has synthetic workflow tests. An optional in-container worker scenario may test its real statistics reader, but network acceptance must not depend on automating every terminal key sequence again. - No Testcontainers crate, container image recipe or integration driver is present. Add approved `testcontainers` as a dev-dependency with compatible pinned version and minimal features. Prefer its supported synchronous facade if it avoids a direct executor dependency. Other direct helpers, Docker client crates or Tokio still require prior agreement; transitive dependencies are not new direct approvals. Use Cargo.lock and inspect official APIs for the selected version. - Native tools use system Rust 1.97.1; musl readiness was previously unverified. Recheck rather than assuming rust-toolchain.toml installed targets or a linker. #### Context and implementation steps Use GPT-6-Sol/medium coordination and GPT-6-Luna/high implementation, fresh review and repairs. Execute bounded tasks sequentially with one writer and explicit file ownership. The coordinator alone updates milestone acceptance and evidence. | Task | Owner | Files and deliverable | | --- | --- | --- | | M8.1 | implementer | `docs/TESTING.md`/ENVIRONMENT additions and test support: exact opt-in architecture, prerequisites, image/build and driver contracts | | M8.2 | implementer | `tests/containers/alpine/Dockerfile`, fixture-only setup scripts, musl build recipe: small reproducible cached image and executable compatibility checks | | M8.3 | implementer | `tests/testcontainers_wireguard.rs`, `tests/support/` modules and Cargo files: host orchestration, private network/container ownership, prerequisite checks, bounded execution and cleanup | | M8.4 | implementer | in-crate cfg(test) scenarios/private adapters, test driver coordination: real netlink reads, fixture apply and application state verification inside server | | M8.5 | implementer | container scenarios and TESTING/README documentation: required lifecycle, restore, delivery/import/failure assertions and executed evidence | | M8.6 | reviewer, repairer | Independent isolation, real-adapter coverage and negative-test review; bounded fixes and real-suite re-review | | M8.7 | coordinator | Default and explicit container gates, redacted results, prerequisites/limitations, acceptance and M9 handoff | M8.1 defines two execution locations: host harness uses Testcontainers to manage containers; server-side code uses the real application and kernel reader in that container's namespaces. Document how driver sessions persist across staged create, Commit, first delivery and apply phases, and how the client is configured from the actual delivered file. Do not lose the creation session between Commit and first issue or reconstruct password-free authority on reopen. Choose a named ignored integration target/scenarios or another explicit opt-in mechanism that default cargo test never starts. The host suite must also opt into all in-container scenario processes. Ensure --ignored does not select unrelated privileged tests. Fail an explicitly requested suite nonzero on missing required prerequisites; never return early with a successful test result labelled skipped. M8.2 pins an Alpine base release/digest and records the resolved image identity and package versions. Install only wireguard-tools, iproute2 and minimal fixture requirements. Use BusyBox capabilities when sufficient; justify any extra package in the test recipe. Runtime images contain no Cargo registry/compiler or systemd. Build/cache image layers and compatible driver artifacts before repeated tests; use fresh mutable containers/volumes for each scenario, not reusable dirty servers. Keep build context small with explicit copies/ignore rules, no .git, host Backup, credentials or private configuration. Never bake generated keys into image layers. Build server/client driver code for x86_64-unknown-linux-musl or in a pinned Alpine builder matching the toolchain. Inspect dynamic requirements and actually execute a harmless readiness command inside Alpine. Do not rely on a glibc host binary, target name or successful host compilation as runtime compatibility proof. Only build required test targets; avoid pulling compiler/debug layers into the small runtime fixture. No global package installation or toolchain update is implicitly authorized by a missing cross-build prerequisite. M8.3 checks Docker API access and explicit disposable-environment selection. Do not use a configured remote daemon without establishing that it is the selected non-production test environment. Probe WireGuard interface creation inside a disposable probe container, never by creating host interfaces or loading modules. Container tools alone do not supply the host kernel module. Refuse to repair prerequisites via SYS_MODULE, privileged mode or host sysctl changes automatically. Create a dedicated uniquely named IPv4 network with a test subnet that does not overlap the tunnel ranges. Run server/client as container root with scoped NET_ADMIN in their own network/PID namespaces; no host networking, namespace joins to host, host D-Bus, host /etc/wireguard, Docker socket mount, or production binds. No host UDP publication is needed for container-to-container endpoints. Use container-internal reachable endpoint/port values, not localhost mapped ports. All settings/server/vault/output paths are private fixture directories created with the application's required permissions. Use Testcontainers copy/exec APIs or an explicitly safe bounded transport for fixture data. Secret-bearing configs/passwords never enter command arguments, image metadata, public environment variables or raw stdout/stderr logs. Any necessary copied plaintext exists only in private temporary/container storage. Generate keys per scenario. Reports carry public keys, phase/status and redacted categories, not full configurations, QR matrices or `wg show all dump` output. Bound image/start/driver/readiness/traffic/teardown operations. Poll explicit conditions with monotonic deadlines, not fixed sleeps. Use unique ownership and RAII/explicit cleanup for containers, networks and volumes on failures as well as success; cleanup must not mask the original failure or delete unrelated resources. Do not default to retain-failed-containers with secrets. Report leftover owned resources if daemon failure prevents cleanup. M8.4 keeps fixture `ip`/`wg` calls solely in setup or test-only apply code. It may use wg-quick strip or equivalent explicit fixture setup, but must not execute arbitrary imported hooks. Handle optional DNS/MTU fields in delivered configs via a documented fixture setup, without mutating the delivered payload just to pass. Avoid an unneeded resolvconf dependency by configuring networking explicitly when appropriate. No subprocess apply path is added to production. Configure endpoint reachability outside the tunnel before full-tunnel policy so an encrypted UDP endpoint is not recursively routed into its own VPN. Set full IPv4 routes within the client's isolated namespace only. Exercise traffic to server tunnel addresses; an external Internet gateway/DNS is not required and successful tunnel reachability does not claim public Internet egress works. Ensure test success cannot come from the Docker underlay: target tunnel-only addresses, use an explicit interface/route assertion, and verify public counters through the actual rustix reader. Test-only service apply reports must be labelled fixture results, never actual systemd job evidence. If fixture restart orchestration verifies and releases application reservations, use the existing private adapter seam with a real backup, fixture apply, actual netlink read/comparison and journaled verification. Do not simply call mark_applied or clear reservations to satisfy assertions. A direct fixture apply outside that orchestration proves only kernel state change; it cannot by itself prove the production successful-restart state transition. M8.5 implements these required scenarios, splitting tests where isolation helps: 1. **Baseline and reader interoperability.** Bring up a server plus an existing client with synthetic configuration, establish real IPv4 tunnel traffic, and observe the configured interface's complete peer set, routes, nonzero fresh handshake and increasing counters through the actual Rust reader. Capture before/after counters, not exact packet-byte totals. Empty/not-yet-handshaken observations must not be misreported as a reader failure or online heuristic. 2. **Create, Commit, deliver, then apply.** Use Application to prepare a new client, Commit and save the actual first-delivery file (QR optional). Assert saved server/vault change, mandatory pre-Commit archive, unchanged runtime peers and explicit activation warning. Configure a fresh client from delivered bytes; prove it cannot reach tunnel-only targets before apply. Apply through the test-only boundary, then require real handshake and IPv4 traffic. 3. **Saved deletion versus live access.** Delete and Commit an active client; assert current vault record removal, existing backup retention, and unchanged runtime peer/access before apply. Reopen saved state to verify the IP remains reserved. After fixture restart plus real verification/persistence, assert peer removal, failed fresh tunnel traffic and address release. Use a finite negative-check window and establish a working baseline so unrelated setup failure cannot masquerade as successful revocation. 4. **Backup/restore separation.** Save a known client set, mutate/Commit, preview and restore the archive without providing a vault password. Assert restored settings/server/vault/state and precautionary backup, plus unchanged kernel state until explicit fixture apply. Then verify the restored peer set and renewed tunnel traffic. Restoration alone never releases pending addresses. 5. **Import, reissue and file safety.** Import a valid generated existing client plus invalid/conflicting candidates from private source files. Assert original bytes unchanged, anomalies visible and conflicts blocked. Reopen and reissue a matched client through password-authorized output; verify wrong password fails and a valid delivered file can configure a real tunnel. Preserve first- issue/password boundaries and do not add plaintext source fallback. 6. **Failure prevents application.** Produce a deterministic backup failure in the container (not chmod-only when root can bypass it) and assert Commit/fixture restart does not install/apply prepared changes or release addresses. Cover default reader deadline/missing-interface behavior inside its namespace without changing host state. Keep already exhaustive malformed/archive/failure-unit cases in ordinary tests rather than duplicating them in every costly container. For all negative reachability assertions, stop/restart client traffic as needed and use new requests; stale timestamps/counters or buffered packets cannot prove continued/revoked access. Use a fixture endpoint/traffic generator with its own bounded error classification. Record that IPv6 traffic is intentionally untested. #### Acceptance and validation - [ ] M8-A1: documented opt-in commands run an actual Testcontainers suite with pinned/cached Alpine runtime images and compatible in-container driver; default tests/help/version do not start Docker, require root or query host interfaces. - [ ] M8-A2: explicit prerequisite checks reject unavailable daemon/kernel/capability or incompatible artifacts as incomplete/failure; no production or automatic privileged/module/sysctl workaround is used. - [ ] M8-A3: isolated real peers establish IPv4 traffic, and the actual rustix reader reports complete public peer/routes, fresh handshake and traffic increments without relying solely on wg text or fake decoded observations. - [ ] M8-A4: new client creation/Commit/file delivery use real Application and vault APIs; the delivered configuration works only after explicit fixture application, and Commit itself never changes runtime state. - [ ] M8-A5: saved deletion removes only current secret, keeps archives and retains reservation/live peer until explicit apply; actual observation and journaled verification precede reservation release. Fresh traffic is revoked afterward. - [ ] M8-A6: password-free restore replaces the saved set but does not apply it; fixture apply restores real tunnel behavior. Source files remain unchanged and imported conflict/wrong-password outputs remain blocked. - [ ] M8-A7: deterministic mandatory-backup failure prevents protected write/apply; negative traffic and missing-interface results cannot pass due to unrelated broken setup or a silent empty/partial observation. - [ ] M8-A8: fixture adapter cannot be selected in production or bypass root/password/ backup controls; no public alternate restart API or runtime environment bypass is introduced. Alpine results explicitly exclude actual zbus/systemd coverage. - [ ] M8-A9: secret-free diagnostics, private copied files, limits/deadlines and cleanup work on both success and failure; only test-owned resources are removed. - [ ] M8-A10: integrated default and real-container gates pass; a clean repeated run reuses image layers while starting fresh mutable scenarios and produces the same behavioral outcomes. Independent review closes actionable isolation/coverage issues. Planned host commands once the proposed test target and documented image/driver build steps exist, from repository root: ```sh cargo fmt --all -- --check cargo clippy --locked --all-targets -- -D warnings cargo test --locked cargo build --locked --release cargo test --locked --test testcontainers_wireguard -- --ignored --test-threads=1 ./target/release/wireguard-admin --help ./target/release/wireguard-admin --version git diff --check ``` Document and execute exact image-build and in-container driver-build commands in TESTING.md before the explicit suite command; include prerequisites and output artifact paths. The in-container target must exist before claiming those commands work. A musl cargo test --no-run build is one possible cfg(test) driver mechanism; resolve its actual compilation/link/dependency implications rather than listing an unverified artifact name. Network downloads/build setup are distinct from locked test execution, and new dependencies require the normal approval policy. Record image identities, kernel/runtime/toolchain versions, target architecture, scenario names and actual outcomes/durations/counts. All required scenarios must execute and pass before M8 acceptance; compile-only/default-test success with unavailable Docker is not scoped acceptance of M8. Do not use a successful ignored-test summary as evidence that ignored tests ran. Real systemd remains M9. #### Progress - [x] Description prepared against accepted M1–M7 and approved Alpine methodology. - [x] M8.1 — implementer `/root/m8_1` prepared the host/container contract in `docs/TESTING.md` and `docs/ENVIRONMENT.md`; documentation link and diff checks passed. Fresh independent reviewer `/root/m8_1_review` raised M8.1-R1, which `/root/m8_1_repair` corrected and the reviewer closed on re-review. Exact driver transport remains an M8.4 decision against the pinned Testcontainers API. - [x] M8.2 — implementer `/root/m8_2` added a pinned Alpine runtime recipe, allowlisted context, package record and harmless readiness script, plus documentation. Fresh reviewer `/root/m8_2_review` raised M8.2-R1/R2; `/root/m8_2_repair` repaired both and independent re-review closed them. Selected-daemon image build and rebuild with 33 exact package constraints, image inspection (`sha256:f41cd6b848d8280d4d86bcaceea962e1b3e5a73e9496c6f9d7929470d0e874d5`, linux/amd64), read-only/networkless/capability-dropped readiness and full `apk info -v` lock match passed. Rust driver execution awaits M8.4. - [x] M8.3 — implementer `/root/m8_3` added the approved Testcontainers 0.28.0 blocking development dependency and named ignored host probe. Focused fmt, Clippy and default locked tests passed; explicit selected-daemon run failed nonzero before network creation because Docker lacks IPv6 iptables support. Independent reviewer `/root/m8_3_review` raised M8.3-R1/R2/R3; targeted repairs by `/root/m8_3_repair` and independent re-reviews closed all three. This completes the bounded host-probe implementation, not the blocked real-container M8 acceptance. No tunnel scenario has run. - [x] M8.3 scope revision — `/root/m8_ipv4_revision` changed only the test harness and guidance to use isolated IPv4 networking as requested by the user. Default gates pass; the selected-daemon probe passed 1/1 after R4–R7 repairs. Independent `/root/m8_ipv4_review` re-reviews closed all findings. - [x] M8.4 — implementer `/root/m8_4_driver` added the in-crate test-only driver and private runtime/application seams; musl builder prerequisite research is complete from `/root/m8_musl_explore`. The exact ignored readiness and Application smoke run in the pinned Alpine image. Independent `/root/m8_4_review` and `/root/m8_4_host_review` raised R1–R4; targeted repairers closed all findings on re-review. The coordinator rebuilt the final musl test binary, reran the selected-daemon smoke (1/1, 4.05s), and found no leftover named resources. M8.5 scenarios remain pending. - [ ] M8.5 — `/root/m8_5_traffic` owns the first bounded scenario set: delivered-file client transfer, baseline real peer observation, pre-Apply nonreachability and post-Apply IPv4 tunnel/handshake/counter proof. Deletion, restore, import/reissue and backup-failure scenarios follow sequentially; independent review and full real-suite evidence are pending. Local focused support tests currently pass 18/18 with one ignored privileged target, and default `cargo test --locked` passed 250 library tests plus other suites; the current musl driver built in the pinned Alpine builder (5m06s). The first selected-daemon traffic run reached the scenario but failed a stale pre-Apply assertion that expected zero peers even though the new required baseline peer remained applied. The assertion is being changed to require exactly that baseline and exclude the new peer; no traffic acceptance is claimed yet. A later bounded run reached fixture Apply but `preview_restart` rejected the raw baseline peer because it lacked managed alias metadata. The synthetic fixture is being corrected through `ConfigDocument::insert_legacy_metadata` before saved state opens. Cleanup completed after that failed run; real traffic acceptance is still pending. The next real run passed baseline handshake/counters, new-client Create, Commit and delivery, pre-Apply peer exclusion, verified fixture Apply and IPv4 ping. It failed only an overly exact host route-string assertion: actual new-peer routes include product-required `0.0.0.0/0` and `::/0`. The assertion is being made IPv4-specific; no IPv6 capability or traffic test is being added. A final successful traffic suite is still pending. A subsequent reader sample after IPv4 ping showed the new peer's fresh handshake and nonzero received/transmitted counters (916/732 bytes in that run). The host assertion still used the wrong route perspective: on the server, the peer's AllowedIPs are its assigned tunnel hosts, not the client's full-tunnel routes. The assertion is being corrected to the assigned IPv4 `/32`; the delivered client file is where `0.0.0.0/0` belongs. No passing end-to-end traffic summary is claimed yet. The host then added an immediate post-Apply/pre-ping reader sample and requires both received and transmitted counters to rise after a new IPv4 ping. The worker's final turn stalled after implementing this assertion; coordinator stopped that turn, ran the exact selected-daemon test once and observed 1/1 pass in 16.25s. Exact-name Docker container/network listings were empty afterward. Fresh independent `/root/m8_5_traffic_review` is inspecting the integrated scenario. That reviewer raised M8.5-R1 (P2): the fresh client used a host-derived setconf copy rather than consuming the intact copied first-delivery file, so traffic did not prove the artifact is usable. M8.5-R2 (P2): route assertions accepted unintended extra IPv4 prefixes. `/root/m8_5_traffic_repair` owns targeted fixes and real-suite rerun. Exact route assertions apply to IPv4 only per user direction; no IPv6 capability or route check is added. This bounded slice is not yet accepted; remaining M8.5 scenarios have not started. The repair added private client-copy byte comparison, in-container `wg-quick strip` consumption of the copied file, and exact IPv4 route-set assertions. The first sandboxed selected-daemon attempt was denied Docker access; the elevated rerun reached the scenario and failed nonzero at the fixed category `copied client configuration could not be safely stripped`. The strip prerequisite/fixture invocation is being diagnosed with synthetic inputs; no secret config was logged and no repaired traffic pass is claimed. Synthetic diagnostics isolated the cause to a copied `.conf` basename longer than wg-quick's 15-character interface-name limit. The repair uses short fixed private names without changing file bytes. The worker turn stalled after editing; coordinator stopped it and ran the repaired selected-daemon test once: 1/1 pass in 14.23s, with no named container/network afterward. Focused host target passed 21 tests with one ignored privileged test; fmt and diff checks passed. Independent R1/R2 re-review was interrupted by an agent usage limit before a finding report, so `/root/m8_5_traffic_review` resumed on 2026-10-24. That independent re-review closed both findings with no new issue: the copied first-delivery bytes are compared privately and consumed by wg-quick in the client namespace; exact IPv4 route sets reject extra prefixes. The bounded baseline/new-client traffic slice is complete as Alpine fixture evidence. `/root/m8_5_delete` now owns saved deletion versus live access. Its local fmt, full locked tests (251 library passed, two ignored), all-target Clippy, release build and focused saved-deletion test passed; pinned musl rebuild passed. The coordinator's exact selected-daemon run failed 1/1 after saved deletion: the baseline client's fresh IPv4 ping stopped before explicit fixture Apply. This is a real-scenario failure, so deletion is not accepted. `/root/m8_5_delete_repair` owns diagnosis and a targeted repair. A fresh continuity probe moved immediately after the prior new-client fixture Apply failed 1/1 on the same selected daemon in 14.25s; the baseline disruption therefore precedes deletion Commit. A separate sandbox-only attempt could not access Docker and supplies no scenario evidence. A bounded selected-daemon diagnostic run then failed 1/1 in 15.52s and reported the baseline peer still present with exact IPv4 route but no fresh handshake, zero reset counters, no learned endpoint and three lost pings after full fixture `wg setconf`. The repair is testing explicit reconnect of the existing client after that fixture apply, then requiring new traffic and reader evidence before continuing the deletion assertions. That reconnect passed far enough to reach deletion Apply. A pinned musl rebuild and exact selected-daemon run reported `RestartStage::Complete`, followed by fixture phase failure; inspection identified the release assertion parsing the client's CIDR as bare `IpAddr`. A targeted typed-address repair and rerun are complete. The next exact selected-daemon run reached post-deletion observation and exposed that full fixture `wg setconf` resets the baseline handshake and counters again; the harness now proves the exact retained/deleted peer set immediately, then reconnects the retained client and requires fresh traffic and reader counter deltas while fresh traffic from the deleted client fails. The coordinator's integrated exact run passed 1/1 in 24.88s, host support tests passed 22 with one privileged test ignored, focused CIDR validation passed, fmt/diff checks passed, and exact-name Docker container/network listings were empty. Fresh independent `/root/m8_5_delete_review` raised M8.5-R3 (P2): the negative ping helper treats any Docker CLI nonzero exit, including failed `docker exec`, as lost tunnel traffic. `/root/m8_5_delete_repair` is changing it to require a bounded ping summary with transmitted packets and zero received packets. The repaired host support target passed 23 tests with one ignored, the coordinator's exact selected-daemon rerun passed 1/1 in 42.96s, and exact-name container/network listings were empty. Independent `/root/m8_5_delete_review` closed R3 on re-review with no new finding: completed zero-reply traffic is distinguished from Docker execution and timeout errors. The saved-deletion bounded slice is accepted as Alpine IPv4 fixture evidence. `/root/m8_5_restore` implemented the two-restore scenario: pre-delete archive re-adds a saved client while runtime remains baseline-only; an earlier baseline archive later removes a saved live client and retains its address reservation until verified fixture Apply. Local fmt, focused tests, locked full tests, all-target Clippy, release build and diff check passed. Pinned musl rebuild passed, but the coordinator's first selected-daemon run failed 1/1 after 7.47s because the host archive-name parser selected the driver's `status=started` line rather than its completed result line. The targeted parser repair passed focused tests and the next real run advanced through the first password-free restore, explicit Apply and renewed restored client traffic. It failed 1/1 after 58.28s at the second restore of the baseline archive (`phase=restore status=started` only). A separate repairer is diagnosing that in-container phase with bounded diagnostics before another pinned musl rebuild, selected-daemon rerun and independent review. Import/reissue and backup-failure slices remain pending. - [ ] M8.6 — independent implementation reviews and repairs complete for M8.1–M8.3 only; M8.4–M8.5 and integrated real-scenario review pending. - [ ] M8.7 — prior default coordinator gates pass; the revised explicit real-container gate and remaining scenarios are pending. Acceptance incomplete. #### Review and repair Record stable finding IDs, iteration, location/impact, owner, fix evidence and disposition. Review actual namespace/daemon targeting, secret transport/logs, capability/mount boundaries, endpoint/full-route setup, real-reader invocation and state-persistence proof. Re-review negative tests so underlay connectivity or an unrelated failed setup cannot produce false success. The user-authorized IPv6 testing exclusion applies only to M8 integration tests; do not replace real IPv4 observations with mocks. M8.1 R1 review dispatched after coordinator inspected the two-file documentation diff. M8.1-R1 (low): `docs/TESTING.md` linked `SyncRunner` under the wrong Testcontainers module; repair changed the link to `runners/trait.SyncRunner.html`. Independent re-review closed the finding. Both rounds reported `git diff --check` passing on the M8.1 docs; link/consistency checks passed. No Docker evidence. M8.2 R1 review of the actual image recipe/docs raised two P2 findings: M8.2-R1 (`runtime-check.sh`): planned libtest artifact cannot accept the script's custom `--readiness` argument. M8.2-R2 (Dockerfile/package record): direct package pins still resolve dependencies from a mutable Alpine repository, and the record/inspection omits the installed closure. Targeted repair `/root/m8_2_repair` addressed both. Reviewer confirmed the pinned direct package versions and Alpine platform digest from primary indexes, plus passing shell syntax, documentation link and diff checks. R1 re-review closed both recipe findings after the repair. `runtime-check.sh` now uses a libtest-compatible exact ignored selector; the test itself remains pending M8.4. The full installed closure was recorded as 33 exact constraints; a second build succeeded with the lock, and read-only shell readiness and inspection passed on the selected daemon. The reviewer did not independently rerun the Docker commands. Coordinator also ran a disposable `--network none --cap-add NET_ADMIN` Alpine probe that created and removed `wg-m8-probe` and added an IPv6 address inside that namespace (exit 0); no host interface, module, or sysctl operation was performed. M8.3 R1 review raised M8.3-R1 (P2, `tests/testcontainers_wireguard.rs`): the outer timeout detaches a worker that may later create/hold test resources, so timeout cleanup is not guaranteed. First repair used a killed child and label-verified cleanup, but independent re-review kept R1 open because a Testcontainers request can run for 120 seconds and finish after the 75-second kill/cleanup pass. Second repair made the probe synchronous, retained finite per-request limits, added conservative aggregate documentation and explicitly reports uncertain late daemon completion. Independent re-review closed R1. The same re-review raised M8.3-R2 (P2, `container_harness.rs`): fixture exec stream reads can block indefinitely if a command hangs, invalidating the documented aggregate bound. First R2 repair wrapped commands in BusyBox `timeout`, capped retained output and drained streams, but independent re-review kept R2 open because the host attach read can still block after the stream is established. Coordinator separately verified BusyBox timeout in the cached Alpine image with a harmless delayed command (one-second elapsed inside, status 137); this does not prove the host stream is bounded. Second R2 repair uses a bounded Docker CLI exec transport and independent re-review closed R2. That re-review raised M8.3-R3 (P2, `container_process.rs`): the CLI exit code for an in-container timeout was discarded, so a timed-out command could be misclassified as a generic capability failure. Targeted repair mapped CLI exit codes 124/137/143 to the fixed timeout category, and independent re-review closed R3. No open M8.1–M8.3 review finding remains. The host-side bounded CLI exec path is not runtime-tested because network creation fails first. The selected-daemon explicit harness failed before network/container creation with `Docker host lacks required IPv6 iptables support`; separate disposable regular, internal and routed dual-stack network attempts failed on missing IPv6 NAT, `DOCKER-INTERNAL` filter and `DOCKER` filter chains respectively. No host networking, firewall, module, or sysctl remediation was attempted. M8.4 R1 independent review of `src/container_driver.rs` raised M8.4-R1 (P1): the only runnable ignored driver test is harmless readiness; no bounded in-container phase protocol retains an Application across create, Commit, delivery and fixture apply, so the new seam cannot yet reach real Rustix observation or lifecycle assertions. M8.4-R2 (P2): `SetWireGuard` lexical path validation allows a symlink under the fixture root to escape. Focused 7 driver argument tests passed, but no live fixture command ran. Targeted repair is pending; no M8.4 acceptance is claimed. M8.4 R1/R2 first repair by `/root/m8_4_repair` added an ignored `alpine_scenario_driver`, a bounded typed stdin/stdout phase protocol retaining one Application through Create, Commit, first delivery and fixture Apply, plus real Rustix verification through the test-only restart seam. It also added symlink component rejection and a regular-file/confined-path check for `wg setconf`; 10 focused driver tests, default tests (244 library tests, two ignored), fmt, all-target Clippy, release build and diff check passed. One initial default run had a transient TUI `Busy` failure; immediate rerun passed. Independent `/root/m8_4_review` re-review closed R2. It kept R1 open (P2): the existing host harness has no bounded stdin channel to invoke the driver, and the protocol does not yet cover M8.5's remaining lifecycle operations. The next bounded repair is host protocol transport and a real in-container Create/Commit/Deliver/Apply smoke path; M8.5 owns the additional scenarios. After the 2026-10-23 daemon/session interruption, coordinator inspection found no host-transport edits from the interrupted worker. `/root/m8_4_host_repair` was dispatched with ownership of the host harness and test-only driver to resume R1; no scenario acceptance is inferred from the prior musl readiness. The worker added bounded `docker exec -i` phase exchange and a private synthetic fixture bootstrap. Its current musl driver rebuilt in the pinned Alpine builder in 3m04s; focused host/driver checks and all-target Clippy passed. On the selected daemon, a first smoke attempt failed before application phases with a generic Docker category and left no owned resources. A diagnostic rerun passed link-up, IPv4 address setup, binary copy and chmod, then failed inside the application driver protocol. The failure stage is being isolated with fixed redacted phase markers; neither run is scenario acceptance. The worker also changed generated settings/server secret files to mode 0600 at creation and discovers the fixture endpoint inside the server namespace. The phase markers subsequently localized the error to Create: the synthetic server document lacked `[Interface] Address`, which the actual application requires for server address ownership. The fixture now includes IPv4/IPv6 server addresses in saved configuration while removing the wg-quick-only `Address` directive from a private 0600 copy passed to `wg setconf`. The authoritative saved file is unchanged. This is fixture setup, not an IPv6 capability or traffic check. A unique per-apply copy and exact cleanup are being added for repeated M8.5 apply phases; the corrected real smoke rerun is pending. One subsequent scenario process exited zero, but libtest captured its success output because the host invocation omitted `--nocapture`, so the host could not verify the expected phase sequence. This is not accepted as scenario evidence; the host invocation is being repaired and rerun. The completed host repair then passed the explicit selected-daemon smoke (1/1, 3.80s), and the coordinator independently reran it (1/1, 3.60s) with no `wga-m83` container/network on follow-up listing. It exercised one exact ignored driver test and observed Create, Commit, first delivery activation warning, and fixture Apply with real Rustix verification. Final worker default gates passed: fmt, all-target Clippy, `cargo test --locked` (247 library tests, two ignored), release build, focused driver/harness checks and diff check. Fresh independent `/root/m8_4_host_review` found M8.4-R3 (P2: a successful `wg setconf` followed by cleanup failure was reported as not submitted) and M8.4-R4 (P2: preview or partial-write failure could leave a secret-bearing setconf copy). `/root/m8_4_cleanup_repair` separated setconf submission from copy-cleanup outcome and added an armed cleanup guard; focused 15 driver tests, default tests (250 library tests, two ignored), fmt, all-target Clippy, release build and diff check passed. Independent `/root/m8_4_host_review` re-review closed R3/R4 with no new finding. The current test-only driver is being rebuilt for a fresh Alpine smoke; M8.4 is not yet accepted and M8.5 has not started. 2026-10-23 resume: the prior `/root/m8_4_repair` turn hit an agent usage limit before changing files; the same GPT-6-Luna/high repairer was resumed for R1/R2. The official `rust:1.97.1-alpine3.22` linux/amd64 builder manifest was resolved to `sha256:b348cb409ac0a73de15065997a360063cf87465574a15e3e4469862cb8996f02`, pulled onto the selected disposable daemon, and inspected as image ID `sha256:bc2b74a0f3f942897f678aa466c3cce54048cf1e094893af3596cf43766153c2`. A read-only/networkless/capability-dropped container reported rustc/Cargo 1.97.1 and installed `x86_64-unknown-linux-musl`; no application harness has yet been used for a scenario. A subsequent read-only source-mounted builder `cargo test --locked --lib --no-run --target x86_64-unknown-linux-musl` completed in 4m25s and produced a 114 MiB static PIE test binary under `/tmp/wga-m8-build-target/.../wireguard_admin-dcc9c812241bb031`. `file`/`readelf` showed no dynamic interpreter. A networkless, read-only, capability-dropped run in the pinned Alpine runtime selected only `container_driver::alpine_readiness` and passed 1/1. This proves the binary loads in Alpine; no real `wg`, application scenario, or Rustix observation ran. After R3/R4 repair, coordinator rebuilt the current musl harness in the pinned builder (`cargo test --locked --lib --no-run --target x86_64-unknown-linux-musl`, 3m22s), then ran the exact ignored host smoke with matching selected daemon ID and `WIREGUARD_ADMIN_TEST_DRIVER` pointing at that artifact. It passed 1/1 in 4.05s and verified the test-only Create/Commit/first delivery/fixture Apply phase sequence through the real Application and Rustix observation path. `docker ps -a --filter name=wga-m83` and `docker network ls --filter name=wga-m83` both returned empty afterward. This is Alpine fixture evidence; no client tunnel traffic, restore/delete/import, or actual systemd job ran yet. After the user excluded IPv6 testing, `/root/m8_ipv4_revision` changed the probe and M8 test guidance to isolated IPv4 while keeping SPEC.md untouched. The revised default suite, fmt, all-target Clippy, release build and diff check passed. The elevated selected-daemon probe created its IPv4 network, but its first Testcontainers container start failed with a generic redacted category; exact-name inspection found no owned resources afterward. A coordinator-only disposable `docker run` on a separately named internal IPv4 network succeeded with `NET_ADMIN`, and that network was removed. Independent reviewer `/root/m8_ipv4_review` raised M8.3-R4 (P2: swallowed Testcontainers error prevents diagnosing this start failure) and M8.3-R5 (P2: route read did not exercise IPv4 route mutation claimed by the guidance). `/root/m8_ipv4_repair` added fixed typed error categories and route add/assert/delete; independent re-review closed R4/R5 but the real route commands remain unrun because the probe still fails at container creation. It raised M8.3-R6 (P2: the `PullImage` category conceals that any Docker create 404 triggers Testcontainers' image-pull fallback). `/root/m8_ipv4_repair_r6` owns the category repair and diagnosis. The repair changed it to a fixed `container creation not-found image fallback failed` category with redaction regression; independent `/root/m8_ipv4_review` re-review closed R6. The explicit probe still fails there, before runtime route commands. No open scoped review finding remains, but actual M8.3 probe acceptance has not passed. The selected daemon is Docker 29.1.3, API 1.52; a direct Docker CLI create/start using the fixture image, internal network, `NET_ADMIN`, label, entrypoint and command succeeded and exact resources were removed. Testcontainers 0.28.0 uses Bollard 0.21.1 (default API 1.53); this was investigated, but the source-level image-name override below explains the observed create 404 without an API version change. No real tunnel or application scenario has run. Coordinator inspection of pinned Testcontainers 0.28.0 identified M8.3-R7 (P1, startup blocker): the host harness used `ImageExt::with_name` for unique container names, but that method overrides the image name. The nonexistent image explains the create 404 and pull fallback. `with_container_name` is the intended API. `/root/m8_name_repair` owns a narrow repair and live-probe regression; `with_container_name` and descriptor/name/owner assertions are in place. Independent `/root/m8_ipv4_review` re-review closed R7. The repairer's elevated selected-daemon probe passed (1/1); a concurrent coordinator probe collided on the fixed subnet, then a separate coordinator rerun passed (1/1, 1.46 seconds) with cleanup. M8.3 prerequisite acceptance is satisfied for the user-scoped IPv4 test path; no application scenario ran. #### Evidence and handoff Planning inspected current Cargo/module/test layout, Application public/private restart and delivery boundaries, runtime reader visibility, M7 acceptance record, and current TESTING/ENVIRONMENT/runtime documentation. Existing M7 evidence is synthetic workflow/PTY and ordinary library integration; it is not a real tunnel. This planning task did not rerun Cargo tests, contact Docker, probe kernel support, start containers, install toolchains/packages, add dependencies, or inspect keys. 2026-10-22 execution preparation: the active coordinator session `turn_context` reported `gpt-6-sol`/`medium`; project role files and explicit dispatch select `gpt-6-luna`/`high`. M1–M7 are accepted in this plan. Native rustc/Cargo are 1.97.1, rustfmt 1.9.0 and Clippy 0.1.97. The host lacks rustup, a musl linker, and the musl standard-library directory. The default local Docker daemon is reachable with approved access and identifies as Gentoo Linux, ID `1baad80e-d79d-49ab-b19a-fb00fd3662d7`; the user explicitly selected it as the disposable M8 environment. The scoped container WireGuard/IPv6 capability probe described above passed. No host WireGuard probe ran. The pre-existing expanded M8 description edit is preserved. 2026-10-22 integrated coordinator gates on the final M8.1–M8.3 revision: `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked`, `cargo build --locked --release`, release `--help` and `--version`, documentation local-link check, and `git diff --check` all exited zero. The default test run passed 235 library tests plus integration/doc tests; the Testcontainers target had 8 passing local support tests and one ignored privileged probe. The ignored count is not container evidence. The exact named probe was then explicitly selected with matching local Unix endpoint and daemon ID; it ran one test and exited 101 with `Docker host lacks required IPv6 iptables support`. The first sandboxed attempt could not access the socket, so the coordinator reran it with approved elevated access; only the latter establishes the daemon result. An elevated `docker network ls` and `docker ps -a` afterward showed no `wga-m83` network or container. Existing unrelated resources were untouched. No real peer traffic, netlink observation, application lifecycle, in-container Rust driver, or systemd behavior was verified. 2026-10-22 user scope change: skip IPv6 capability and traffic tests. The prior dual-stack network failure is historical evidence, no longer an M8 blocker. Use the already selected local disposable daemon with a unique isolated IPv4 bridge and scoped `NET_ADMIN`; run the revised named probe. An elevated coordinator run reached container creation but failed after 5.9 seconds with `Testcontainers could not start an owned fixture container`; the fresh IPv4 network and container were absent on follow-up inspection. Investigate this failure through scoped implementation/review before claiming the probe passes. Do not modify this host's firewall/modules/sysctls. Then settle the Alpine-compatible in-crate driver build/runtime path (host musl standard library/linker remain absent, so a pinned Alpine builder may be needed), execute M8.4–M8.5 sequentially, independently review/repair the complete scenarios, and rerun all M8 acceptance gates. #### Decisions and discoveries The production reader is bounded rustix netlink, not wireguard-uapi, and the restart adapter is private zbus. M8 must test that actual reader and preserve production API boundaries rather than resurrect a discarded library path or expose a fake-systemd switch. A test-only service apply plus real comparison is useful integration evidence but does not validate the systemd adapter itself. Image layers may be cached for speed/space while secret-bearing container state must be fresh. The container host shares its kernel with Alpine, so the fixture cannot manufacture missing kernel support by installing wireguard-tools. Primary implementation references (check selected versions at dispatch): [Testcontainers requirements](https://rust.testcontainers.org/system_requirements/docker/), [Testcontainers image options](https://docs.rs/testcontainers/latest/testcontainers/core/trait.ImageExt.html), and [WireGuard namespaces](https://www.wireguard.com/netns/). #### Outcome M8.1–M8.3 were implemented and independently reviewed, with all original findings closed. The user removed IPv6 testing from M8, and an IPv4-only harness revision is in progress. M8.4–M8.5 and real-container acceptance remain pending; M8 is not accepted. Milestone 9 must not start until M8 acceptance; actual systemd/VM validation remains separate and pending.