Files
wireguard-admin/docs/history/milestone-8-before-workflow-cleanup.md

703 lines
48 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.