# Execution Plan ## Purpose and authority This is the living execution plan for WireGuard Admin version 1. It records milestone scope, progress, review findings, and evidence so a new agent can resume from repository files without relying on chat history. Read [AGENTS.md](AGENTS.md), [the specification](docs/SPEC.md), [architectural decisions](docs/adr/), [implementation order](docs/IMPLEMENTATION.md), and [the glossary](CONTEXT.md) before execution. The specification defines behavior; this plan schedules its delivery. Approved dependencies are in [DEPENDENCIES.md](docs/DEPENDENCIES.md); Alpine/VM testing rules are in [TESTING.md](docs/TESTING.md). Do not change requirements to make a milestone pass. Creating this plan does not start implementation or change the active model. All milestones below are initially pending, and no validation results are claimed. ## Model roles | Role | Required model | Reasoning effort | Responsibility | | --------------------- | ------------------------- | ---------------- | --------------------------------------------------------------------------------------------------------- | | Coordinator | GPT-6-Sol (`gpt-6-sol`) | `medium` | Refine tasks, dispatch workers, resolve interfaces, verify results, maintain this plan, accept milestones | | Implementation worker | GPT-6-Luna (`gpt-6-luna`) | `high` | Implement a bounded task and its meaningful tests | | Independent reviewer | GPT-6-Luna (`gpt-6-luna`) | `high` | Review the actual changes and evidence against acceptance criteria | | Repair worker | GPT-6-Luna (`gpt-6-luna`) | `high` | Fix review findings and regressions, then report new evidence | These are the user's explicit model choices, not suggestions. Configure them through the execution harness, not by merely telling an agent to impersonate a model. In a harness using `spawn_agent`, use `model="gpt-6-luna"`, `reasoning_effort="high"`, and `fork_turns="none"` for workers; supply their context explicitly. Do not silently substitute another model. If the required model or effort is unavailable, report the limitation before starting affected execution. This Markdown file cannot switch the calling coordinator's model. ## Sequential execution and ownership - Number milestones with integers starting at **1**. Keep IDs stable, never renumber accepted work or reuse IDs. Append new milestones using the next available integer; add bounded tasks within a milestone when finer scope is needed. - Execute milestones in ascending order. Start the next milestone only after the current milestone is accepted. Do not bypass an unmet gate because a later task looks independent. - The coordinator owns PLANS.md, task boundaries, interface agreements, and acceptance. Workers report proposed plan updates; they do not independently mark milestones accepted. - Default to one implementation writer at a time. Independent tasks within the active milestone may run concurrently only with explicit disjoint file ownership and agreed interfaces. Do not let agents overwrite one another's edits or run competing Cargo manifest/lockfile changes. - Use subagents for current-task work, not new user-owned chats. Reviewers should be separate agents from implementers and initially read-only. Further delegation must remain within the coordinator's assigned scope and model policy. - Preserve unrelated user changes. Do not create commits, publish, deploy, or access production merely because a milestone is accepted. ## Milestone lifecycle and review loop Use states `pending`, `in_progress`, `in_review`, `needs_changes`, `blocked`, and `accepted`. A blocked milestone records the concrete missing prerequisite; it is not accepted and its successors remain pending. 1. **Prepare.** Inspect the actual repository. Expand the active milestone using the template below: concrete files/interfaces, small steps, observable outcomes, exact available commands, and expected results. Identify missing prerequisites before dispatch. 2. **Implement.** Give a worker a bounded task brief. It implements only the assigned scope, adds focused behavioral tests, and reports changed files, commands/results, assumptions, and unfinished work. 3. **Verify and review.** The coordinator examines the actual files/diff and checks evidence. A fresh reviewer evaluates correctness, regressions, specification/ADR compliance, security-relevant failure paths, and test coverage. Agent confidence or a prose summary is not acceptance evidence. 4. **Repair.** Record each actionable finding with an ID, location, impact, expected behavior, and reproduction or validation. Assign a worker to fix it and rerun affected tests. The reviewer rechecks fixes and newly affected behavior. 5. **Repeat.** Continue review and repair until findings are resolved and acceptance checks pass. Do not stop at an arbitrary iteration count or repeat an unchanged failing approach. Investigate repeated failures, reduce the task, or record a real external blocker. Settled requirements remain unchanged. 6. **Accept.** The coordinator runs milestone gates against the integrated files, records reviewer disposition and validation evidence, updates progress/outcomes, and marks the milestone accepted. Only then advance. Routine acceptance does not require another user approval; new dependencies outside the approved set and unresolved product decisions retain their existing approval rules. If a revision invalidates earlier acceptance evidence, record which milestones are affected and reopen the earliest affected milestone before advancing. Keep the integrated state buildable at each accepted milestone. ## Worker task brief Every dispatch must include: - Milestone ID, task ID, required model/effort, and role. - User-visible objective, relevant specification sections/ADRs, and prerequisite state. - Explicit read list, owned files, interfaces to preserve, and out-of-scope work. - Approved dependencies needed by this task; no permission to add unapproved dependencies. - Acceptance criteria and commands with expected outcomes; identify tests requiring Docker/VM access. - Safety boundary: synthetic fixtures only, no production keys/services/configuration, no host-network shortcuts. - Required report: changed files, checks actually run and their results, findings/risks, and remaining work. No secrets in reports. For reviewers, provide the same objective and criteria plus the change baseline or exact changed-file list. Ask for independent inspection rather than confirmation of the implementer's conclusions. ## Milestone record template Use this structure when expanding each active milestone. Replace placeholders before dispatch; do not interpret them as completed work. ```markdown ## Milestone — Status: pending Prerequisites: ### Outcome and scope ### Context and implementation steps ### Acceptance and validation - [ ] - [ ] Commands: ### Progress - [ ] ### Review and repair | Finding | Iteration | Location and impact | Owner | Resolution and evidence | State | | --- | --- | --- | --- | --- | --- | ### Evidence and handoff ### Decisions and discoveries ### Outcome ``` Use ISO 8601 timestamps with offsets for progress. Keep evidence concise and reproducible, following [execution records](docs/EXECUTION_RECORDS.md); diagnostic attempts belong outside this plan. Use redacted excerpts or artifact paths rather than large logs. Distinguish planned commands from executed commands and unavailable checks from passing checks. Keep the plan self-contained by summarizing discoveries that affect the next task, not just linking to an agent conversation. ## Validation gates For Rust milestones, use the relevant focused tests and the formatting, Clippy, default tests, and release-build commands in AGENTS.md. Add exact commands when targets exist; do not invent passing outputs. Unit and fixture checks run without root. Use standard-library test adapters rather than an additional mock framework. Container suites are opt-in and use pinned/cached Alpine images, fresh server/client containers, synthetic keys, isolated networking, and scoped `NET_ADMIN`. The real runtime adapter executes inside the server container. Missing prerequisites required by a suite are incomplete validation, not success. Milestone 8 validates IPv4 networking only by user direction; IPv6 capability and traffic remain unverified while the production dual-stack requirement remains in force. Alpine fixture application is not a production systemd test. Actual systemd/D-Bus acceptance runs in a disposable VM. Never use production to fill gaps in test coverage. Final release acceptance requires relevant unit, Alpine WireGuard, and VM systemd results reported separately. Document blockers without silently weakening the gate. ## Milestone register Each row is a delivery milestone, not a completion claim. Expand its record before execution; retain its original ID and acceptance scope. | ID | Milestone | Prerequisites | State | | --- | --------------------------------------------------------- | ----------------------------------------- | -------- | | 1 | Rust foundation, settings, and domain states | None | accepted | | 2 | Server configuration editing with byte preservation | 1 | accepted | | 3 | Client keys, encrypted vault, and existing-client import | 2 | accepted | | 4 | Backups, safe Commit, and full restoration | 3 | accepted | | 5 | Runtime observation and systemd restart adapter | 4 | accepted | | 6 | Client configuration delivery and reissue lifecycle | 5 | accepted | | 7 | Complete terminal management workflow | 6 | accepted | | 8 | Alpine Testcontainers integration | 7; suitable disposable Docker environment | accepted | | 9 | Real systemd acceptance and release readiness | 8; disposable systemd VM | retired | | 10 | Visible styled keyboard quick reference in bottom panel | 8; existing M7 TUI | accepted | | 11 | Valid subnet routes shown as a capability limitation | 10 | accepted | | 12 | Aligned, responsive client table | 11 | accepted | | 13 | Compact terminal QR disclosure | 12 | accepted | | 14 | Restart verification for valid legacy peers | 13 | accepted | | 15 | Diagnosable systemd restart and unloaded-unit resolution | 14 | accepted | | 16 | Root systemd private peer restart transport | 15 | accepted | | 17 | Adaptive client list, persistent details and clear states | 16 accepted; existing M7/M12 TUI | accepted | | 18 | Readable, complete in-app help for implemented workflows | 17 | accepted | | 19 | Refactoring contract, terminology and behavior map | 18 | accepted | | 20 | Explicit vocabulary and named operation contexts | 19 | accepted | | 21 | Domain ownership of reservations and apply knowledge | 20 | accepted | | 22 | Transactional draft editing and client creation vertical slice | 21 | accepted | | 23 | Readable saved-state Commit and restore scenarios | 22 | accepted | | 24 | Session-owned first delivery and password reissue scenarios | 23 | accepted | | 25 | Explicit restart verification scenario | 24 | accepted | | 26 | Application read models and thin TUI command adapter | 25 | accepted | | 27 | Integrated refactoring acceptance and architecture handoff | 26 | accepted | ### Milestone 1 — Rust foundation, settings, and domain states Status: accepted (2026-10-18T18:59:35Z; independent R2 re-review and coordinator gates passed). Prerequisites: no previous milestone; verified GPT-6-Sol/medium coordinator and GPT-6-Luna/high workers, the pinned native Rust environment, and registry access or cached approved dependencies. Docker, root, a running VPN, and musl linking are not required for this milestone. #### Outcome and scope Deliver a compilable single-package foundation that can read validated settings relative to the executable, represent client identities and draft/saved/applied states, and allocate IPv4/IPv6 addresses without touching a real server. Startup supports `--help` and `--version` without root, settings, or vault access. Management startup checks root before attempting privileged operations. This is a foundation, not a working VPN manager. Until later milestones wire in the TUI, management startup must explain that the management interface is not implemented and exit nonzero without reading a real server config or changing anything. Do not display a successful management session for a placeholder. Read SPEC.md sections Purpose, Data locations, Data sources, New client parameters, and Operations and states; all three ADRs; and docs/ENVIRONMENT.md, docs/PROJECT_LAYOUT.md, docs/DEPENDENCIES.md, and docs/DEVELOPMENT.md. No WireGuard document edits, key generation, vault/import, backups/Commit writes, systemd/runtime adapters, QR, TUI, or container infrastructure are implemented here. #### Context and implementation steps At preparation, the repository contained design/Codex documents and rust-toolchain.toml, but no Cargo.toml, Cargo.lock, or source tree. The toolchain pin is 1.97.1 and the created manifest uses edition 2024. Local system tools match; rustup is absent. Musl standard library/linker readiness remains deferred to milestone 8. Execute the tasks below sequentially with one implementation writer. For each task dispatch, the coordinator supplies exact document sections and the interface contract established by the prior task. Workers do not edit PLANS.md or accept work. | Task | Owner | Files and deliverable | | ---- | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | M1.1 | implementer | Cargo.toml, Cargo.lock, src/main.rs, src/lib.rs, narrow startup/error modules as needed, README.md and .gitignore: package scaffolding, arguments, root boundary, safe incomplete-management behavior | | M1.2 | implementer | src/config.rs, config/wireguard-admin.example.toml, settings tests: typed settings, validation, and executable-relative paths | | M1.3 | implementer | src/domain.rs and child modules only if needed, domain tests: public-key identity, change states, address allocation and reservation rules | | M1.4 | reviewer, then repairer as needed | Independently inspect the integrated M1 changes and acceptance evidence; coordinator assigns bounded repair files and finding IDs | | M1.5 | coordinator | Run integrated gates, update outdated scaffolding statements, record evidence and acceptance in this plan | M1.1 creates package `wireguard-admin`, binary `wireguard-admin`, library `wireguard_admin`, edition 2024, and rust-version 1.97.1. Keep main thin, parse only the small agreed argument surface with std, and test the effective-UID boundary through an injectable/pure decision rather than requiring root tests. Unknown arguments fail clearly. Preserve existing documentation/configuration. Add narrow ignores for local deployment data without hiding config examples or synthetic fixtures. Do not create placeholder files for all future modules. M1.2 uses TOML with explicit required fields for interface name, server config path, client source directory, server endpoint, IPv4 and IPv6 allocation ranges, DNS addresses, systemd unit, and nonzero restart timeout. Data root is optional and defaults to executable-directory/Backup with data and archives beneath it. Choose and document a fixed settings filename alongside the executable; do not add a new CLI settings override. Resolve relative configured paths consistently against that directory, never the process working directory. Separate parsing, validation, and resolution so tests do not read the host environment. Do not require referenced production files or systemd to exist to validate settings. Validate address families, ranges, endpoint syntax, usable service/interface names, DNS values, and timeout. Do not resolve DNS over the network in a settings test. Endpoint validation must support a hostname or IP with a port and bracketed IPv6. Document the resulting settings schema in the example and README. M1.3 represents a public key as a validated 32-byte identity with standard WireGuard Base64 parsing; duplicate aliases remain valid. Keep applied snapshots separate from saved and draft state. Model unavailable runtime state explicitly. Pass existing/server/reserved addresses as inputs; do not query real interfaces. Deletion from draft alone must not reserve an address as if saved; committed removal retains the address until an explicit successful-apply transition. Discarded drafts restore saved allocation availability. This milestone tests these transitions as pure model behavior, not an actual Commit implementation. Allocate the first usable free address with bounded work proportional to the occupied set, not subnet size. Exclude supplied server/occupied/reserved addresses and IPv4 network/broadcast addresses where applicable. Handle /31, /32 and narrow IPv6 ranges explicitly, avoid arithmetic overflow, and return clear exhaustion errors. Manual addresses must match the configured family/range and be available. Do not silently add IPv6 to imported IPv4-only model records. Approved direct dependencies for this milestone: serde (derive), toml, thiserror, ipnet, time when timestamps are needed, base64 for public-key representations, and rustix for the effective-UID query. Use tempfile as a dev-dependency for filesystem/subprocess fixtures if needed. Select compatible versions/features; no unapproved crate or premature crypto/TUI/container dependency. Generate the initial Cargo.lock once dependency choices are resolved; then use --locked. #### Acceptance and validation - [x] M1-A1: package/library/binary compile under the pinned native toolchain; Cargo.lock is tracked and no unrelated dependency or file scaffolding is added. - [x] M1-A2: help/version work without root or settings, unknown arguments fail, and management startup rejects non-root before privileged access. Root-path behavior is covered by injection, without running the application as root. - [x] M1-A3: valid TOML yields typed settings; missing/invalid fields produce actionable errors without opening a real server config, vault, or service. - [x] M1-A4: executable-relative paths and defaults remain identical from two different working directories; test via pure path inputs or subprocesses, not parallel process-wide current-directory mutation. - [x] M1-A5: valid/invalid public keys, repeated aliases, explicit unknown runtime state, draft discard, and saved/applied divergence have behavioral coverage. - [x] M1-A6: IPv4/IPv6 first-free selection, manual selection, occupied/server exclusions, tiny ranges, exhaustion, and deferred release are verified. A large IPv6 range test demonstrates that allocation does not scan the entire subnet. - [x] M1-A7: all standard gates pass without root/Docker/production access; a fresh independent reviewer has no unresolved actionable findings. - [x] M1-A8: README describes the limited executable and setup honestly; commands, example settings, and source paths match actual implementation. Update the design-only statement in AGENTS.md when source scaffolding really exists. Planned commands from the repository root after Cargo.toml and lockfile exist: ```sh cargo metadata --locked --no-deps --format-version 1 cargo fmt --all -- --check cargo clippy --locked --all-targets -- -D warnings cargo test --locked cargo build --locked --release ./target/release/wireguard-admin --help ./target/release/wireguard-admin --version ``` Expected: zero exits for these commands, successful help/version without settings, and all ordinary tests pass as an unprivileged user. Argument/root rejection and working-directory independence are exercised by tests. Record actual focused test commands when test names exist. Do not execute a bare management launch as root. No Alpine build, container tunnel, or VM/systemd check is claimed by M1 acceptance. #### Progress - [x] Preparation: toolchain configuration and environment observations recorded; this is not application implementation or milestone acceptance. - [x] M1.1 — implementer (`/root/implementer_m1_1`), 2026-10-18T18:47:43Z; package and startup boundary integrated. - [x] M1.2 — implementer (`/root/implementer_m1_2`), 2026-10-18T18:50:19Z; typed settings, validation, paths, example, and tests integrated. - [x] M1.3 — implementer (`/root/implementer_m1_3`), 2026-10-18T18:54:21Z; domain model, allocation, and tests integrated. - [x] M1.4 — fresh reviewer (`/root/reviewer_m1_r1`) R1 and R2, repairer (`/root/repairer_m1_r1`) for domain finding; all findings closed 2026-10-18T18:59:35Z. - [x] M1.5 — coordinator integrated validation and acceptance completed 2026-10-18T18:59:35Z. #### Review and repair | Finding | Iteration | Location and impact | Owner | Resolution and evidence | State | | -------- | --------- | ---------------------------------------------------------------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------- | ------ | | M1-R1-01 | R1/R2 | `src/domain.rs` constructor can claim verified without a matching applied snapshot; misleading state | repairer | constructor rejects inconsistent Verified; Commit/mismatch demote stale Verified; 13 focused, 22 total tests passed; R2 confirmed | closed | | M1-R1-02 | R1/R2 | `AGENTS.md` still claims no Rust application exists | coordinator | opening statement updated to limited scaffold; R2 confirmed | closed | | M1-R1-03 | R1/R2 | `Cargo.lock` existed outside Git index | coordinator | `git add -N Cargo.lock`; `git ls-files --error-unmatch Cargo.lock` succeeds; R2 confirmed; no commit/content staging | closed | #### Evidence and handoff Preparation verified local rustc/Cargo 1.97.1, formatter and Clippy availability; rustup/musl readiness limitations are in docs/ENVIRONMENT.md. This execution verified the active coordinator turn as `gpt-6-sol`/`medium` from session turn context; project agent files and `[agents]` defaults specify `gpt-6-luna`/`high`. The repository had no Cargo manifest/source and a clean worktree at 2026-10-18T18:45:43Z. Current rustc/Cargo are 1.97.1, with rustfmt 1.9.0 and Clippy 0.1.97. No prior milestone exists. M1.1 added `Cargo.toml`, `Cargo.lock`, `src/{main,lib,startup}.rs`, and updated README/.gitignore. The coordinator inspected those files and the working tree. The worker reported passing locked metadata, fmt, Clippy, four startup tests, release build, release help/version, and diff check; integrated M1 gates remain pending. Cargo registry search failed, but approved `rustix` 1.1.5 resolved from local cache. M1.2 added `src/config.rs`, the example settings TOML, approved `serde`/`toml`/`thiserror`/`ipnet` dependencies, and README settings guidance. The coordinator inspected the files. The worker reported five focused settings tests and nine total tests passing, along with locked metadata, fmt, Clippy, release build, and diff check. Cargo resolution required an approved network-enabled retry after sandbox DNS failure. Independent review remains pending. M1.3 added `src/domain.rs`, its library declaration, and approved `base64` dependency. The coordinator inspected the files. The worker reported 12 focused domain tests, 21 total tests, locked metadata, fmt, Clippy, and release build passing. The three implementer turns were independently verified from their session turn contexts as `gpt-6-luna`/`high`. Next: fresh review of the integrated milestone. R1 reviewer turn context was also verified as `gpt-6-luna`/`high`. R1 ran locked metadata, fmt, Clippy, 21 tests, release build, and help/version; all passed. It reported findings M1-R1-01 through M1-R1-03 above. No production, container, VM, or root management launch was used. Repairer turn context was verified as `gpt-6-luna`/`high`. Its domain repair passed 13 focused tests, 22 total tests, fmt, Clippy, and release build. The coordinator updated AGENTS.md and marked Cargo.lock intent-to-add in the Git index after a sandbox write denial and approved escalation. `git ls-files` now recognizes it; no Git commit was created. R2 re-review closed all three findings with no new actionable issues. Its turn context was verified as `gpt-6-luna`/`high`. Coordinator integrated gates on 2026-10-18: `cargo metadata --locked --no-deps --format-version 1`, `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked` (22 passed), `cargo build --locked --release`, release `--help`, release `--version`, and `git diff --check` all exited 0. Unknown argument and a bare management launch as UID 1000 exited 2 with clear diagnostics, as expected. `git ls-files --error-unmatch Cargo.lock` exited 0; the file is intent-to-add in the Git index and its contents are not staged or committed. Unit/fixture evidence only; Alpine WireGuard and disposable systemd VM checks are later milestones and were not run. No production configuration, key, service, or VPN was accessed. #### Decisions and discoveries Use the already installed native compiler release as the reproducible baseline, not an unpinned stable channel. The toolchain file is consumed by rustup proxies; the current system-packaged binaries require explicit version checks. This is an environment constraint, not permission to install packages or alter global tools. #### Outcome Accepted 2026-10-18T18:59:35Z after independent review and integrated gates. The executable currently exposes help/version and safe startup errors; settings and domain operations are library logic. Management/TUI behavior remains for later milestones. Stop here; next milestone is 2 and has not started. ### Milestone 2 — Server configuration editing with byte preservation Status: accepted (2026-10-18T19:26:14Z; independent R3 review and integrated gates passed). Prerequisites: milestone 1 accepted; pinned native Rust environment and approved cached dependencies or registry access. No root, WireGuard, systemd, Docker, or production files are required. #### Outcome and scope Deliver a pure library API that parses an existing server configuration, inspects its client entries, and prepares additions, deletions, alias changes, and managed metadata updates entirely in memory. Rendering an unmodified parsed document must reproduce the original bytes exactly. Editing a client must preserve every byte outside the explicitly owned edit spans. Governing requirements: SPEC.md Data sources, Operations and states, Importing existing clients, and configuration-preservation acceptance criteria; ADRs 0001 (no implicit apply) and 0003 (server authority); PROJECT_LAYOUT.md, DEVELOPMENT.md, DEPENDENCIES.md, and TESTING.md. Follow the existing model and module conventions instead of introducing a second client identity system. Out of scope: reading production files, filesystem write/Commit orchestration, key generation, client-file scanning/import and cryptographic matching, vaults, backups, TUI, runtime/systemd actions, and network integration tests. Startup must retain the safe incomplete-management behavior from milestone 1. #### Existing code and integration constraints - `src/lib.rs` currently exposes `config`, `domain`, and `startup` only. - `domain::PublicKey` parses canonical Base64 into 32 bytes and provides identity, equality, ordering, and formatting. Reuse it for lookup; never identify peers by alias or position alone. `domain::validate_alias` rejects empty/control text while allowing repeated aliases across clients. - `domain::Client` contains identity, alias, and `ClientAddresses` with mandatory IPv4 plus optional IPv6. `Snapshot` rejects duplicate public keys. The document representation must not force every existing `[Peer]` into these narrow types. - Existing peers may have multiple AllowedIPs, network prefixes, missing fields, duplicate keys, IPv6-only addresses, unknown parameters, or malformed metadata. Retain their original spans and return explicit inspection issues. Do not pick the first address, collapse routes, drop a peer, or invent missing values to force a successful conversion. Provide an explicit fallible projection for compatible records; keep unsupported records available for inspection. - Settings already expose resolved server paths but this milestone consumes supplied document content only. `ClientStates` models pure draft/saved/applied transitions; parsing/rendering must not mutate it or claim anything is applied. - Cargo already includes base64, ipnet, rustix, serde, thiserror, and toml. No generic INI parser or new unapproved dependency is needed. Approved `time` may be added for UTC timestamp validation; do not add crypto or TUI crates early. #### Context and implementation steps Run tasks sequentially with one writer. The coordinator supplies relevant requirements, acceptance IDs, owned files, and prior interface agreements to each worker. Use GPT-6-Luna/high implementer, a separate reviewer, and repairer under the GPT-6-Sol/medium coordinator. Workers do not edit milestone acceptance state. | Task | Owner | Files and deliverable | | ---- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | | M2.1 | implementer | `src/wireguard.rs`, `src/wireguard/document.rs`, `src/lib.rs`, focused tests: lossless document/span representation and peer inspection | | M2.2 | implementer | `src/wireguard/metadata.rs`, document integration, `docs/CONFIG_FORMAT.md`: versioned managed comment grammar and validated metadata | | M2.3 | implementer | wireguard module and narrow domain adapters: atomic in-memory add/delete/rename/metadata preparation keyed by PublicKey | | M2.4 | implementer | `tests/config_preservation.rs`, `tests/fixtures/wireguard/`, README/library usage documentation: integrated preservation and failure scenarios | | M2.5 | reviewer, repairer | Independent integrated review, bounded fixes and regression checks, re-review | | M2.6 | coordinator | Integrated gates, evidence/findings ledger, acceptance and next handoff in PLANS.md | | | | | M2.1 defines document ownership before editing. Preserve section order, whitespace, line endings (LF/CRLF, including mixed endings), blank lines, unknown directives, comments, and absence of a final newline. Use retained source bytes and spans, not reconstructed serialization or a map keyed by property name. Separate raw storage from parsed public views; errors and Debug must not expose the raw server text, Interface PrivateKey, or peer PresharedKey. Unknown/unparseable content is preserved, not silently normalized. Document supported input encoding; invalid text must either remain losslessly opaque or be rejected without lossy decoding. Recognize WireGuard sections and key/value syntax deliberately. Repeated `[Peer]` sections are expected; repeated/conflicting identity fields or the same public key in multiple peers are ambiguous, not last-write-wins. Inspect them with issues and refuse edits whose target or boundaries cannot be established safely. M2.2 specifies a uniquely recognizable managed block immediately before its peer, with begin/end markers, explicit format version, alias, and optional creation UTC timestamp. Before M2.3, document the exact grammar, escaping, ownership and attachment rules, supported versions, unknown-field policy, and examples in `docs/CONFIG_FORMAT.md`. Use unambiguous single-line encoding for aliases so `#`, `=`, quotes, Unicode, or marker-looking text cannot create directives/sections. Reject control/newline injection using domain validation. Reuse approved parsing facilities where useful; a TOML-encoded string within comments does not mean the server file itself is TOML. Never put secrets or a second public-key copy in the block. Existing peers with unknown creation date have no fabricated timestamp. Metadata absence is a legacy state, not corruption. Use a shortened public-key fallback only in a public view; do not persist it as a chosen alias implicitly. Client filename fallback belongs to later import work. Malformed, duplicated, misattached, or unsupported-version blocks must yield explicit issues and block unsafe metadata edits; do not overwrite them as if absent or interpret arbitrary administrator comments as managed blocks. M2.3 adds a new peer from validated public identity and supplied client addresses, not a newly generated key. New host routes use `/32` and `/128` as appropriate; existing AllowedIPs and connection parameters remain unchanged on alias edits. Allow IPv4-only existing clients, repeated aliases, and caller-supplied timestamps. Existing-peer metadata preparation is an explicit edit, not a side effect of parse. A rename changes only the owned alias representation (or inserts an explicit new managed block when safely absent), preserving any known creation date. Deletion removes only the selected peer's owned section content and attached managed block. Define handling of administrator comments around and within the peer so deleting it never removes/reassigns another peer's managed metadata or unrelated comments. Sections such as `[Interface]`, PostUp/PostDown, and arbitrary server directives must never be rewritten or executed. Unknown peer directives are retained on non-deletion edits; deletion deliberately removes the selected peer's configuration, not neighboring content. Validate each edit completely before applying it. Missing/ambiguous target, invalid key/alias/address/metadata, or unsupported structures must leave the original document unchanged. Sequential edits recompute or otherwise maintain valid spans; no stale offsets after insertions/deletions. Rendering never writes files and must not invoke commit_draft, system tools, or runtime adapters. M2.4 uses synthetic fixture data only. If secret-shaped input is required for preservation/redaction checks, generate dummy values in the test or clearly mark non-production placeholders; do not print full fixtures in assertion failures. Prefer comparisons of preserved slices and precise changed spans over brittle snapshots of the entire file. No real server sample is needed or permitted. #### Acceptance and validation - [x] M2-A1: unmodified round trips are byte-identical for LF, CRLF, mixed endings, no final newline, whitespace/comments, unknown directives, and multiple peers. - [x] M2-A2: parse/inspection/edit/render operate only on caller-supplied data; no filesystem persistence, live config access, runtime mutation, or startup change. - [x] M2-A3: public identity reuses PublicKey; duplicate aliases are supported; malformed/missing/duplicate keys and unsupported address shapes remain visible as issues rather than being silently normalized into Snapshot/Client. - [x] M2-A4: new peer addition, selected-peer deletion, rename, and explicit legacy metadata insertion affect only intended spans; all server parameters and unrelated administrator comments remain byte-identical. - [x] M2-A5: the exact metadata format is documented with supported version, escaping/attachment rules and examples. Missing creation dates stay missing; rename preserves known dates, and no key copy or secret appears in metadata. - [x] M2-A6: malformed/unknown-version/misattached blocks and ambiguous peers are preserved and reported; unsafe edits fail without changing any original bytes. - [x] M2-A7: boundary cases cover first/last/adjacent peer removal, inter-peer comments, Unicode/quoted/marker-like aliases, control injection, and repeated edits with no stale spans. Rendering an edit result and parsing it again produces the intended public records without modifying untouched content. - [x] M2-A8: unsupported routes, multiple addresses and IPv6-only records round-trip unchanged; projection to the narrow M1 model fails explicitly when necessary. - [x] M2-A9: errors/Debug never disclose secret-bearing source lines or complete documents; input rejection does not corrupt the caller's document. - [x] M2-A10: existing M1 tests and startup help/version behavior remain intact; focused/full gates pass and independent review has no unresolved findings. Planned commands from the repository root, after M2 targets exist: ```sh cargo test --locked wireguard cargo test --locked --test config_preservation cargo fmt --all -- --check cargo clippy --locked --all-targets -- -D warnings cargo test --locked cargo build --locked --release ./target/release/wireguard-admin --help ./target/release/wireguard-admin --version git diff --check ``` Use unit-test names containing `wireguard` for the focused filter, and verify that the filter actually runs tests. All commands above should exit zero; negative cases are assertions inside passing tests, not ignored failures. Run as an unprivileged user with synthetic input. Dependency resolution is needed only if approved `time` is introduced; update Cargo.lock before --locked gates. Document the exact commands/results and test counts when execution occurs. No container or VM result is required or claimed for this pure-library milestone. #### Progress - [x] Description prepared from the current M1 code and approved documents. - [x] M2.1 — implementer (`/root/implementer_m2_1`) completed 2026-10-18T19:10:30Z; lossless parser/inspection integrated. - [x] M2.2 — implementer (`/root/implementer_m2_2`) completed 2026-10-18T19:14:23Z; v1 metadata grammar, attachment, and format document integrated. - [x] M2.3 — implementer (`/root/implementer_m2_3`) completed 2026-10-18T19:17:05Z; pure peer edits/projection integrated. - [x] M2.4 — implementer (`/root/implementer_m2_4`) completed 2026-10-18T19:20:05Z; fixture suite and README integrated with one retained failing assertion. - [x] M2.5 — fresh reviewer (`/root/reviewer_m2_r1`) completed R1/R2/R3; repairer (`/root/repairer_m2_r1`) resolved both findings; R3 found no remaining issue. - [x] M2.6 — coordinator integrated gates passed and acceptance recorded. #### Review and repair | Finding | Iteration | Location and impact | Owner | Resolution and evidence | State | | --- | --- | --- | --- | --- | --- | | M2-R1-01 (M2-I-01) | implementation/R1 | `src/wireguard/edit.rs:214` rename rebuilt full metadata block and normalized mixed endings outside alias bytes | repairer | Replaced only the encoded alias value; mixed-ending regression and R2/R3 review passed | closed | | M2-R2-01 | R2 | `PeerInspection.issues` omitted valid but unsupported address shapes, contrary to M2-A3 | repairer | Added redacted unsupported-shape/missing-IPv4 issues and fixture assertions; R3 review passed | closed | R1 found the preservation defect. R2 confirmed that fix and found the inspection gap. R3 confirmed both repairs and found no remaining actionable issue. The byte-preservation assertion was retained; unsupported records remain visible. #### Evidence and handoff Planning inspected Cargo.toml, src/lib.rs, domain public-key/client/state types, settings interfaces, M1 acceptance evidence, and governing documents. M1 records 22 passing tests and independent R2 acceptance; this planning task did not rerun those checks and makes no new application-validation claim. No M2 source edits, workers, Cargo commands, production files, containers, or services were involved. Execution verified the active coordinator turn as `gpt-6-sol`/`medium` from session turn context. M1 is accepted. At execution start the only existing worktree change was this prepared M2 record in PLANS.md; no M2 source files existed. Rustc/Cargo 1.97.1, rustfmt 1.9.0, and Clippy 0.1.97 are available. M2.1 contract: a caller-supplied byte buffer with retained source/spans, a public peer inspection view keyed by the existing `PublicKey`, secret-free issues/errors/Debug, and exact unchanged-byte rendering. Invalid UTF-8 may be rejected with a redacted error. Editing remains for later tasks. Next action: dispatch M2.1. No M2 validation result is claimed yet. M2.1 added `src/wireguard.rs`, `src/wireguard/document.rs`, and a library declaration. The coordinator inspected the actual files. The parser retains a UTF-8 source buffer and byte spans, rejects invalid UTF-8 without echoing it, reports peer identity/route issues without losing raw content, and renders unchanged bytes exactly. The worker reported seven focused `wireguard` tests, 28 full tests, fmt, Clippy, release build, and diff check passing. Its turn context was verified as `gpt-6-luna`/`high`. Metadata grammar and edits remain unimplemented; next action is M2.2. M2.2 added `src/wireguard/metadata.rs`, integrated metadata inspection in the document module, and documented v1 grammar in `docs/CONFIG_FORMAT.md`. The coordinator inspected those files. Valid blocks expose alias, optional UTC creation time, and owned span; malformed/unsupported/misattached blocks yield redacted issues. The worker reported 14 focused `wireguard` tests, 35 full tests, fmt, Clippy, release build, and diff check passing; no new dependency. Its turn context was verified as `gpt-6-luna`/`high`. Next action is M2.3. M2.3 added `src/wireguard/edit.rs` and API exports for add/delete/rename, explicit legacy metadata insertion, and fallible projection. The coordinator inspected the code. The worker reported 21 focused `wireguard` tests, 42 full tests, fmt, Clippy, release build, and diff check passing. Its turn context was verified as `gpt-6-luna`/`high`. Next: M2.4 fixture-level behavioral tests and README updates, then independent review. M2.4 added `tests/config_preservation.rs`, a synthetic server fixture, and README library API guidance. The coordinator inspected those files. Focused integration results: eight passed, one failed at the mixed-ending metadata rename assertion (M2-I-01). Full `cargo test --locked` likewise has 42 unit tests passing but fails on that integration assertion. Fmt, Clippy, release build, and diff check passed. Its turn context was verified as `gpt-6-luna`/`high`. No acceptance gate is claimed passing while this fails. Next: fresh independent review of all M2 changes and the recorded defect. R1 reviewer turn context was verified as `gpt-6-luna`/`high`. It reproduced M2-R1-01, ran 21 focused `wireguard` tests passing, and found fmt, Clippy, release build, and diff check passing. Integrated test/full test remain red. Next: repair M2-R1-01, then R2 re-review. The repairer was configured as `gpt-6-luna`/`high`. First repair restricted an existing valid metadata rename to its encoded alias value. R2 independently confirmed the mixed-ending regression passed and identified M2-R2-01: valid unsupported routes were not reported in `PeerInspection.issues`. The second repair added redacted address-shape issues and their behavioral assertions, while retaining raw routes, safe metadata edits, and fallible projection. It also replaced a parser `expect` on user input with a redacted typed error. R3 independently verified both repairs and reported no further actionable finding. R2/R3 reviewer turns used the same independently spawned `gpt-6-luna`/`high` reviewer; it remained read-only throughout. Coordinator integrated gates on the final M2 source, run unprivileged with synthetic input on 2026-10-18: `cargo test --locked wireguard` passed 21 tests; `cargo test --locked --test config_preservation` passed 9 tests; `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo build --locked --release`, and `git diff --check` exited zero; `cargo test --locked` passed 42 unit and 9 integration tests (0 doc tests); release `--help` and `--version` exited zero with the existing safe startup output. The focused filter was verified to execute 21 tests. No production files, host VPN/service, container, or systemd VM was accessed; real-system checks are not required for this pure-library milestone and are not claimed. #### Decisions and discoveries M1's ClientAddresses cannot represent every valid existing AllowedIPs layout. Keep a lossless document model plus explicit projection issues rather than broadening product scope or truncating existing settings. Metadata grammar and comment ownership must be settled and documented before edit implementation. No new ADR is required merely for moving parser code into the planned modules. #### Outcome Accepted after independent R3 review and all M2-A1–A10 checks. No M2 blockers remain. Stop here; milestone 3 is the next action only on a new request. ### Milestone 3 — Client keys, encrypted vault, and existing-client import Status: accepted (2026-10-19T20:08:32Z; independent R7 review and coordinator gates passed). Prerequisites: milestones 1 and 2 accepted; pinned native Rust environment and access to compatible approved cryptographic dependencies or a populated cache. No root, running WireGuard, systemd, Docker, VM, or production input is needed. #### Outcome and scope Deliver library operations for WireGuard client key generation, encrypted vault initialization, password-free encryption of new records, password-authorized secret retrieval, and safe reconciliation/import of existing client files. Reuse M1 identities/settings and M2 server inspection without rewriting originals. Read SPEC.md Data sources, Encrypted vault, Importing existing clients, Connection delivery, and Backups and restoration; ADRs 0002 and 0003; CONFIG_FORMAT.md; DEPENDENCIES.md, DEVELOPMENT.md, and TESTING.md. Preserve the distinction between vault encryption, server secrets, and an unencrypted output file. This milestone prepares versioned vault bytes and encrypted import results; filesystem persistence/Commit and its mandatory backups belong to milestone 4. Initial setup prepares encryption of valid existing files immediately in the same setup operation, but must not install a vault, rewrite the server, or bypass the later persistence gate. Use supplied buffers and an explicit read-only source adapter; no automatic scan at application startup. TUI/password prompts, delivery files/QR and first-delivery session orchestration belong to milestones 6 and 7. No runtime or service operations are introduced. #### Existing code and integration constraints - M1 provides `domain::PublicKey`, `Client`, `ClientAddresses`, `Snapshot`, `ClientStates`, and validated/resolved settings, including the source directory and data paths. Public keys, not filenames or repeated aliases, identify secrets. - M2 exposes `wireguard::ConfigDocument::parse`, `render`, `peers`, `sections`, and `source_slice`, plus explicit add/delete/rename/legacy-metadata edits. `PeerInspection` provides identity candidates, AllowedIPs, metadata, and issues. Its raw source may contain secrets; never expose it through import reports. - `project_client` requires valid attached metadata and a supported address shape. Do not make successful projection a prerequisite for preserving or reconciling existing records. Missing metadata, IPv6-only peers, extra routes, and ambiguous keys must remain inspectable rather than being silently dropped or normalized. - Managed metadata is already v1: Base64url alias and optional strict UTC creation string in an attached comment block. Keep that grammar unchanged. The server alias wins; a filename/public-key fallback is display-only until an explicit metadata edit. Do not assign import time as an unknown creation date. - Server parsing is not client parsing: a client file's `[Interface] PrivateKey` identifies the client, while its `[Peer] PublicKey` normally identifies the server. Never use that server key as the client's identity. - Cargo currently contains base64, ipnet, rustix, serde, thiserror, and toml. Add only the approved crypto crates needed here: age, x25519-dalek, getrandom, secrecy, and zeroize; tempfile may be a dev-dependency for source-adapter tests. Verify current official APIs, maintained compatible versions, feature flags, and secret-type compatibility before choosing them. No custom cipher/KDF, OpenSSL subprocess, `wg genkey`, unapproved crate, or unrelated upgrade. #### Context and implementation steps Run these tasks sequentially with one writer. GPT-6-Sol/medium coordinates; GPT-6-Luna/high implements, reviews independently, and repairs. The coordinator supplies scope, owned files, relevant documents, accepted interfaces, checks, and required report contents. Workers do not accept milestones or edit PLANS.md. | Task | Owner | Files and deliverable | | --- | --- | --- | | M3.1 | implementer | `src/vault.rs`, optional `src/vault/keys.rs`, `src/lib.rs`, Cargo files: secret-safe WireGuard key types, generation and matching | | M3.2 | implementer | `src/vault/` modules as needed, `docs/VAULT_FORMAT.md`: asymmetric vault setup, password-protected identity, versioned envelopes and load validation | | M3.3 | implementer | `src/vault/import.rs` or a narrow import module: caller-buffer client parsing, reconciliation reports and encrypted import preparation | | M3.4 | implementer | read-only configured-directory adapter, `tests/vault_import.rs`, synthetic temporary inputs and README guidance: initial setup/import workflow and failure coverage | | M3.5 | reviewer, repairer | Independent crypto/use-of-library, import and integrated regression review; bounded fixes and re-review | | M3.6 | coordinator | Integrated gates, findings/evidence and acceptance/handoff in PLANS.md | M3.1 wraps private keys/passwords in secret-aware containers with redacted or no Debug, no implicit Display/Serialize, and explicit exposure only at necessary cryptographic/encoding boundaries. Generate WireGuard-compatible private keys using OS randomness and derive their public key using x25519-dalek. Preserve canonical WireGuard Base64 on output; validate decoded length/encoding on input. Keep imported private-key bytes intact rather than replacing them with newly generated keys. Test generation/derivation and matching without leaking values in assertion failures. Handle random-source errors as recoverable errors. M3.2 initializes a separate age recipient/identity pair, never reusing a WireGuard key. Store only the recipient and password-encrypted vault identity in the public serialized bundle; encrypt each client record to that recipient without requiring the password. Use the age library's established authenticated encryption and password protection; do not implement the cryptographic construction manually. Document exact v1 vault/record formats, component filenames/layout intended for M4, version handling, public record identifiers, and size limits before encoding production data. Use serde/TOML or opaque age payloads as appropriate, without silently introducing a second serializer dependency. Ensure serialization cannot write plaintext secrets through a generic derive. Reject unknown versions and truncated/oversized structures with redacted typed errors. Bind the decrypted payload to its expected client public key and vault context; derive its public key again and reject mislabelled/swapped records. On unlocking, verify the decrypted vault identity matches the declared recipient. Do not allow public envelope metadata alone to authorize a key's identity or reissue status. Do not invent cryptography to authenticate a plaintext index: document the limits of unauthenticated public metadata and perform the necessary checks after decrypt. Each authorized retrieval accepts a fresh password, decrypts only for that operation, validates binding, and returns an explicit short-lived secret result. Do not cache an unlocked identity, retain the password between requests, or fall back to original plaintext files after wrong password, missing/corrupt records, or password loss. Keep cloning/exposure of secrets minimal; zeroize supported owned buffers on drop and document unavoidable library-managed copies without claiming all process memory can be erased. Password-policy/UI additions are not part of this milestone. M3.3 parses supplied client content without generic lossy INI serialization. Require one unambiguous client private key and derive its PublicKey for matching. Validate client Address values while retaining enough information to report unsupported shapes and compare addresses rather than conflating client prefixes with server host routes. Preserve existing optional connection parameters needed later, including any valid preshared secret, inside the encrypted payload; do not introduce new MTU/keepalive/PSK settings or generation. Reject ambiguous or malformed secret fields rather than choosing the last occurrence. Do not run PostUp/hooks, resolve DNS, mutate routes, or execute text from imported files. Reconcile against the authoritative M2 server inspection and provide explicit, secret-free results for server-only, file-only, matched, mismatched, invalid, and ambiguous/duplicate candidates. Missing/invalid managed metadata and narrow projection failures are represented separately from cryptographic key mismatch. Compare the client file's server peer key with the server identity when it can be determined safely; absent/ambiguous server identity is unresolved, not assumed valid. Do not infer identity from filenames or friendly aliases. Matching key material may be encrypted even when address parameters conflict, but record/report that reissue is blocked pending explicit reconciliation. Do not truncate routes, overwrite server metadata, automatically add a file-only client to the VPN, or treat public filename fallback as authoritative. Duplicate files or existing vault entries must not cause silent overwrite or last-wins selection; preserve the prior record and return an explicit resolution issue. A repeat import without decryption must not claim plaintext equality it cannot verify. Define deterministic handling and tests rather than guessing equivalence. M3.4 supplies a bounded read-only adapter for the configured source directory: regular files only, documented candidate selection, no recursive traversal or symlink following, bounded content sizes, and redacted per-file errors. A source file failure must not abort other valid imports; a directory-enumeration failure must be visible and must not be called a successful complete scan. Use safe file opening for privileged future callers; expanding rustix's approved feature set is permitted when required, but no new direct crate is implicitly approved. Initial setup combines vault creation with attempted import of valid candidates. Return the encrypted prepared bundle and a report; invalid/conflicting candidates remain visible and originals are untouched. Explicit later import reuses the vault recipient without asking for a password or replacing the protected identity. Reissue authorization remains separate. Keep directory IO out of pure parsing and crypto tests; all adapter tests use synthetic temporary directories, never actual configured server/client paths. All writes in this milestone are test artifacts or explicit serialization buffers, not installed application state. #### Acceptance and validation - [x] M3-A1: generated WireGuard keys derive valid PublicKey values; imported key correspondence and malformed Base64/length handling are tested without tools. - [x] M3-A2: vault setup uses an independent key pair and password-protected identity; serialized application vault data contains no plaintext private keys, passwords, or imported secret-bearing configurations. - [x] M3-A3: new record encryption and later explicit import need only the public recipient, not a password/unlocked identity; loading ciphertext needs no password. - [x] M3-A4: correct-password retrieval returns the selected secret; wrong/missing password, unknown versions, malformed/truncated ciphertext, recipient mismatch, and record relabelling fail without fallback or loss of the original bundle. - [x] M3-A5: independent retrieval operations require fresh authorization, with no reusable unlocked identity/password exposed through ordinary vault APIs. Errors, reports and Debug are redacted; secret serialization is explicit and protected. - [x] M3-A6: matching uses the public key derived from client Interface PrivateKey, not its server peer key or alias. Reports cover matched/server-only/file-only, conflicts, invalid files, duplicate/ambiguous keys and unsupported address shapes. - [x] M3-A7: address/server-binding discrepancies remain explicit and block the authorized reissue path until resolved; a valid secret may still be encrypted. Duplicate imports never silently replace existing secrets or trusted metadata. - [x] M3-A8: setup prepares encrypted imports from valid source files while skipping invalid candidates with explanations. Regular-file selection, limits, symlink rejection and enumeration failures are tested using temporary data only. - [x] M3-A9: source files and original server bytes are unchanged; no metadata is implicitly inserted, no creation dates invented, and M2 parsing/editing guarantees remain intact. Missing metadata does not prevent encrypted preservation of a key. - [x] M3-A10: serialized bundle round-trip preserves encrypted identity/records without decryption, ready for M4 backup/persistence; no filesystem installation, implicit Commit/runtime change, UI delivery or first-issue bypass is introduced. - [x] M3-A11: standard integrated gates and M1/M2 regressions pass; independent review closes actionable crypto, import, secret-lifetime and boundary findings. Planned commands from the repository root after M3 tests and lockfile exist: ```sh cargo test --locked vault cargo test --locked --test vault_import cargo test --locked --test config_preservation cargo fmt --all -- --check cargo clippy --locked --all-targets -- -D warnings cargo test --locked cargo build --locked --release ./target/release/wireguard-admin --help ./target/release/wireguard-admin --version git diff --check ``` Ensure the focused filter runs tests and record counts/results; all gates should exit zero. Resolve approved new dependencies/update the lockfile before locked checks. Keep password-encryption tests bounded while exercising real library crypto; do not weaken production KDF parameters simply to speed up tests. Use synthetic generated keys and no sensitive panic/assert output. No root, Docker, VM, actual WireGuard interface, or production config is required or claimed. #### Progress - [x] Description prepared against accepted M1/M2 code and current documents. - [x] M3.1 — implementer (`/root/implementer_m3_1`) completed 2026-10-18T19:41:39Z; secret-safe WireGuard key generation, parse, derivation and matching. - [x] M3.2 — implementer (`/root/implementer_m3_2`) completed 2026-10-18T19:46:09Z; versioned age-encrypted bundle, fresh-password retrieval, format documentation. - [x] M3.3 — implementer (`/root/implementer_m3_3`) completed 2026-10-18T19:52:45Z; caller-buffer parse/reconciliation and authenticated import status. - [x] M3.4 — implementer (`/root/implementer_m3_4`) completed 2026-10-18T19:58:55Z; bounded read-only directory adapter, initial setup/import flow and synthetic tests. - [x] M3.5 — fresh reviewer (`/root/reviewer_m3_r1`) completed R1–R7; repairer (`/root/repairer_m3_r1`) resolved all findings; R7 found no actionable issue. - [x] M3.6 — coordinator integrated gates passed and acceptance recorded. #### Review and repair | Finding | Iteration | Location and impact | Owner | Resolution and evidence | State | | --- | --- | --- | --- | --- | --- | | M3-R1-01 | R1 | `src/vault.rs:134` Debug printed untrusted imported connection fields | repairer | Redacted imported payload Debug; sentinel regression and R2 passed | closed | | M3-R1-02 | R1 | `src/vault/source.rs:110` directory path was reopened after secure FD open, allowing enumeration/read inventory mismatch | repairer | Enumerates through pinned `rustix::fs::Dir`; path-replacement regression and R2 passed | closed | | M3-R1-03 | R1 | `src/vault/import.rs:409` unknown server identity was collapsed into a known key mismatch | repairer | Added Matches/Mismatch/Unresolved and blocked retrieval regression; R2 passed | closed | | M3-R1-04 | R1 | `src/vault/source.rs:206` duplicate existing vault entry was generic encryption failure | repairer | Added equality-unverified duplicate issue; repeat-import regression and R2 passed | closed | | M3-R2-01 | R2 | `docs/VAULT_FORMAT.md` omitted intended component filename/layout for M4 | repairer | Documented intended `data_directory/vault.toml` without persistence; R3 confirmed | closed | | M3-R2-02 | R2 | Secret-bearing test `assert_eq!` output could disclose synthetic key/config bytes on failure | repairer | Replaced with redacted boolean assertions; R3 confirmed | closed | | M3-R3-01 | R3 | `src/vault.rs:306` unsupported encrypted payload version reported as `RecordMismatch`, not actionable `UnsupportedVersion` | repairer | Split version/label checks; valid encrypted unsupported-version regression and R4 passed | closed | | M3-R4-01 | R4 | `src/vault.rs:48` canonical Base64 comparison created a plaintext key `String` without zeroization | repairer | Wrapped in `Zeroizing`; R5 confirmed | closed | | M3-R5-01 | R5 | `src/vault.rs:38` Base64 decode could drop partially decoded key bytes on malformed input before zeroizing wrapper | repairer | Decode into zeroizing buffer from allocation onward; R6 confirmed | closed | | M3-R5-02 | R5 | `src/vault.rs:287` identity UTF-8 conversion cloned decrypted identity into ordinary Vec, vulnerable on error | repairer | Borrow from zeroizing plaintext; R6 confirmed | closed | | M3-R6-01 | R6 | `src/vault/import.rs:112` allocated ordinary directive String before knowing whether value was secret/ignored | repairer | Borrowed dispatch; zeroizing PrivateKey/PSK allocations, duplicate/error paths and regressions passed; R7 confirmed | closed | The reviewer otherwise found crypto binding, fresh authorization and bounded scan behavior sound at fixture level. No finding may be closed by hiding a conflict, disabling tests, or relaxing encryption. #### Evidence and handoff Planning inspected Cargo.toml, exported M2 document/edit/metadata APIs, CONFIG_FORMAT.md, M1 model constraints, approved dependencies and ADR 0002. M2 records independent R3 acceptance, 42 unit plus 9 integration tests, and passing integrated gates. Those results are historical evidence; this planning task did not rerun application tests, select crypto versions, or add dependencies/source. No workers were dispatched and no source directory, private key, service, or network environment was accessed. Next execution action: verify configured model roles and the current repository, review official APIs/compatibility for the approved cryptographic libraries, then dispatch M3.1 with the bounded task and acceptance criteria. Escalate unavailable packages as dependency/environment blockers rather than changing approved crypto. Execution verified the coordinator session as `gpt-6-sol`/`medium`; M1 and M2 are accepted. At start, the only worktree change was this prepared M3 plan. Rustc/Cargo 1.97.1, rustfmt 1.9.0 and Clippy 0.1.97 match the pinned baseline. Official crate documentation was checked for `age` 0.12.1 (Rust 1.74 minimum, recipient and passphrase APIs, no default features), `x25519-dalek`, `getrandom`, and `secrecy`. `age` re-exports its compatible `secrecy` via `age-core`; use that re-export rather than adding a conflicting direct version. The local cache initially lacked `age`; a read-only `cargo info age@0.12.1` failed under sandbox DNS restrictions, then succeeded with approved network escalation. No manifest or source was changed during this prerequisite check. Next: assign M3.1 to the configured implementer. M3.1 added `vault::WireGuardPrivateKey` using approved `x25519-dalek` 2.0.1, `getrandom` 0.3.4, and `zeroize` 1.9.0; no `age`/`secrecy` direct dependency was introduced prematurely. The coordinator inspected the new key API and manifest. The implementer ran `cargo test --locked vault` (3 tests), `cargo test --locked` (45 unit and 9 preservation integration tests), fmt, Clippy, release build and diff check successfully. Its turn context was verified `gpt-6-luna`/`high`. No production data or service was accessed. Next: M3.2 versioned age vault format and authorized retrieval on top of this key type. M3.2 added approved `age` 0.12.1, `VaultBundle` recipient encryption and passphrase-protected identity, bounded TOML loading, ciphertext/recipient/key binding checks, and `docs/VAULT_FORMAT.md`. The coordinator inspected source, format and manifest. The worker ran `cargo test --locked vault` (9 tests), `cargo test --locked` (51 unit and 9 preservation integration tests), fmt, Clippy, release build and diff check successfully; its turn context was verified `gpt-6-luna`/`high`. This is task progress, not integrated M3 acceptance. M3.3 must extend encrypted records to retain imported connection parameters and enforce duplicate/no-overwrite and conflict reissue semantics; independent review must scrutinize the current public `reissue_blocked` flag's authority and secret lifetime. Next: M3.3 caller-buffer import/reconciliation. M3.3 added `src/vault/import.rs` and extended the encrypted v1 record payload to retain imported connection parameters, including valid PSKs. It changed duplicate insertion to fail without overwrite and moved the conflict gate into the authenticated payload; the public flag is advisory. The coordinator inspected implementation and updated format documentation. The worker ran `cargo test --locked vault` (15 tests), `cargo test --locked` (57 unit and 9 preservation integration tests), fmt, Clippy, release build and diff check successfully; its turn context was verified `gpt-6-luna`/`high`. This remains pending independent review. Potential review focus: unresolved versus mismatched server identity classification, preservation and lifetime of source buffers, and absence of conflict-clearing bypass. Next: M3.4 bounded read-only directory adapter and setup/import workflow. M3.4 added `src/vault/source.rs`, `tests/vault_import.rs`, README guidance, approved `tempfile` 3.23.0 as a development dependency, and `rustix` fs/std features. It uses Linux `openat2` no-symlink file opens, per-file/aggregate limits and incomplete-scan reporting. The coordinator inspected the adapter, fixture tests and manifest. The worker ran `cargo test --locked vault` (16 tests), `cargo test --locked --test vault_import` (5), `cargo test --locked --test config_preservation` (9), `cargo test --locked` (58 unit, 9 preservation and 5 vault-import tests), fmt, Clippy, release build and diff check successfully; its turn context was verified `gpt-6-luna`/`high`. No production path or service was accessed. Possible review focus: path-based directory enumeration after secure fd open, explicit duplicate-existing-record reporting, and whether server-key unknown is distinct from mismatch. Linux `openat2` absence yields an incomplete scan, not success. Next: fresh integrated independent review M3.5 before acceptance. Fresh R1 reviewer turn used `gpt-6-luna`/`high`. It inspected integrated code and ran focused vault (16), vault-import (5), and M2 preservation (9) tests; the full suite passed 58 unit plus 14 integration tests, and fmt, Clippy, release build, help/version and diff check passed. It found M3-R1-01 through M3-R1-04 above. These passing tests do not close the review findings. Next: repair, re-review, then coordinator integrated gates. The repairer (`/root/repairer_m3_r1`, `gpt-6-luna`/`high`) reported all four R1 findings repaired with focused regressions. It ran `cargo test --locked vault` (18), vault-import (5), M2 preservation (9), full tests (60 unit plus 14 integration), fmt, Clippy, release build and diff check successfully. The coordinator inspected changed paths. Findings remain pending independent R2 re-review; no acceptance is claimed yet. R2 independently confirmed M3-R1-01 through M3-R1-04 and found no remaining defect in those paths. It found M3-R2-01 and M3-R2-02 above. Focused vault (18), vault-import (5), preservation (9), full tests (60 unit plus 14 integration), fmt, Clippy, release build, help/version and diff check passed; the passing gates do not resolve the documentation and test-output gaps. Next: narrow repair and R3 re-review. R3 independently closed M3-R2-01/02 and confirmed R1 repairs remain intact. It found M3-R3-01 and noted an M3-A7 evidence gap: the existing known server-key mismatch test also has unsupported address projection, so an isolated mismatch-blocks-reissue test is needed. Aggregate scan-byte and directory-entry limits are implemented but lack dedicated boundary tests; their coverage should be strengthened if practical. R3 reran focused vault (18), vault-import (5), preservation (9), full suite (60 unit plus 14 integration), fmt, Clippy, release build, help/version and diff check successfully. Passing checks do not close M3-R3-01. Next: repair and R4. The repairer (`/root/repairer_m3_r1`) split encrypted payload version and label checks, added a valid age-encrypted unsupported-version regression, an isolated known server-key-mismatch/reissue-block regression, and inexpensive aggregate-byte/directory-entry cap tests through private testable limits. It reported 22 focused vault tests, 5 vault-import tests, 9 M2 preservation tests, 64 unit plus 14 integration tests in the full suite, and passing fmt, Clippy, release build and diff check. The coordinator inspected the new code; R4 independent re-review is pending. No M3 acceptance is claimed yet. R4 independently confirmed M3-R3-01 and all earlier repairs. It reran focused vault (22), vault-import (5), M2 preservation (9), full tests (64 unit plus 14 integration), fmt, Clippy, release build, help/version and diff check, all passing. It found M3-R4-01 above; this must be repaired before M3 acceptance. No production access or real-system result is claimed. R5 independently confirmed M3-R4-01 but identified M3-R5-01/02 in adjacent secret-lifetime paths. Focused vault (22), vault-import (5), preservation (9), full tests (64 unit plus 14 integration), fmt, Clippy, release build, help/version and diff check all passed; these checks do not prove zeroization on failure. Earlier repairs remain intact. Next: targeted cleanup and R6 re-review; M3 is not accepted. R6 independently confirmed M3-R5-01/02: base64 decoding now occurs inside a zeroizing Vec even on failure, and age identity parsing borrows from zeroizing bytes. It found M3-R6-01 in the import parser. It reran focused vault (22), vault-import (5), M2 preservation (9), full suite (64 unit plus 14 integration), fmt, Clippy, release build, help/version and diff check, all passing. No production access or real-system result was claimed. Next: targeted parser cleanup and R7. The repairer changed `src/vault/import.rs` to borrow section, directive key and value until dispatch; ignored/misplaced values are not allocated. Recognized PrivateKey/PSK values enter zeroizing storage immediately, including duplicate/error paths. Redaction regressions were added. It reported focused vault (22), full tests (64 unit plus 14 integration), fmt, Clippy, release build and diff check passing; the coordinator inspected the changed parser. Independent R7 re-review was the next gate after this repair. R7 independent reviewer (configured `gpt-6-luna`/`high`) inspected the final parser, prior repairs, cryptographic/secret lifetimes, source-directory safety and M3-A1–A11. It found no remaining actionable issue and recommended acceptance. The coordinator reran the full planned command set on the final integrated source on 2026-10-19: `cargo test --locked vault` passed 22 tests; `cargo test --locked --test vault_import` passed 5; `cargo test --locked --test config_preservation` passed 9; `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo build --locked --release`, and `git diff --check` exited zero; `cargo test --locked` passed 64 unit tests, 9 preservation integration tests and 5 vault-import integration tests (0 doc tests); release `--help` and `--version` both exited zero with existing safe startup output. The focused vault filter ran 22 actual tests. These are unprivileged synthetic/unit fixture results only. No production files, running VPN, host service, Alpine container or systemd VM was accessed; container/VM checks are not required for this M3 library milestone and are not claimed. No M3 blocker remains. #### Decisions and discoveries The client's Interface private key and the server's Peer public key have different roles. Existing M2 source/span APIs support authoritative reconciliation but must not be reused as an unsafe source-dump parser for secrets. M1 projection cannot represent all legacy peers; use richer import inspection without changing that model or metadata v1 merely to suppress anomalies. Prepared vault encoding is the M3/M4 interface: M3 supplies versioned encrypted components; M4 owns atomic installation, backup-before-mutation, restore, and Commit deletion. M6 owns file/QR generation and first-delivery session policy. This boundary avoids persisting unbacked state during an import/crypto milestone. #### Outcome Accepted after independent R7 review, closure of all findings and passing M3-A1–A11 gates. Stop here; milestone 4 is the next action only on a new request. No vault installation, Git commit, deployment or production access occurred. ### Milestone 4 — Backups, safe Commit, and full restoration Status: accepted (2026-10-19T21:10:25Z; independent R5 closed all findings and final coordinator gates passed). Prerequisites: milestones 1–3 accepted; pinned native Rust environment and available approved dependencies. This milestone uses synthetic temporary filesystems and injected failures, not root, production files, or running services. #### Outcome and scope Deliver library/application operations that install prepared M3 vault data, save an M2-edited server document through Commit, preserve internal allocation state, create/list backups, and preview/confirm full restoration without a vault password. Writes must be honest about partial failure and recoverable from the mandatory pre-operation backup. Neither Commit nor restore applies VPN changes. Read SPEC.md Data locations, Data sources, Operations and states, Encrypted vault, and Backups and restoration; all three ADRs; VAULT_FORMAT.md, CONFIG_FORMAT.md, DEVELOPMENT.md, DEPENDENCIES.md, ENVIRONMENT.md, and TESTING.md. Distinguish the application's Commit action from Git commits, which are not authorized here. Out of scope: TUI, output files/QR, password prompts, reissue reconciliation, real WireGuard/systemd observation or restart, automatic failed-restart rollback, concurrent editor detection/locking, compression, retention cleanup, migration to different server paths, and production access. The startup management placeholder remains safe until later milestones; expose testable APIs rather than a new CLI. #### Existing code and integration constraints - `config::ResolvedSettings` provides executable-relative server/client/data paths and data/archive directories; settings live at `wireguard-admin.toml` beside the executable. Use explicit resolved paths in adapters, never process cwd. - M2 `ConfigDocument` and edits preserve original bytes and report unsupported peers. Commit persists the prepared document without reserializing server settings, silently projecting away anomalies, or rewriting unchanged comments. - M3 `VaultBundle::to_toml`/`from_toml` encode/load encrypted v1 bundles without password entry. VAULT_FORMAT.md fixes the intended path as data/vault.toml. Preserve recipient, encrypted identity, records, and authenticated blocked flags; backups/restore do not decrypt or rebuild them from plaintext imports. - `PreparedImportSetup` contains bundle/report; read-only scans never installed application state. M4 owns initial installation through the same persistence and pre-Commit backup boundary as later prepared imports. - Vault records are private and there is currently no public removal operation. Add a narrow public-key removal API as needed; do not serialize/reparse internals to bypass encapsulation or expose plaintext. Do not automatically purge file-only imported records merely because they are absent from the server configuration. - M1 `ClientStates::commit_draft` updates the in-memory saved snapshot and address reservations only. Invoke/reflect that transition after successful disk commit, never before it. Current reservation state is private and has no persistence codec; add explicit validated state encode/load support, not generic secret or whole-document serialization. Preserve unsupported M2 peers outside narrow Client. - Historical verified/applied snapshots are not fresh runtime observations. On reopening/restoring, preserve needed reservation/history data but mark current runtime unknown until M5 verifies it. Restore never frees a pending deleted IP. - Cargo already includes M1–M3 libraries and tempfile for tests. Approved `tar` and `time` may be introduced with minimal features, and tempfile/rustix used for secure writes. Moving tempfile to normal dependencies is allowed if needed. No new hash/UUID/compression/database/framework dependency is implicitly approved. #### Context and implementation steps Use sequential tasks and one writer. GPT-6-Sol/medium coordinates; GPT-6-Luna/high implements, reviews independently, and repairs. Supply explicit ownership, scope, accepted interfaces, validation, and task report requirements on each dispatch. | Task | Owner | Files and deliverable | | --- | --- | --- | | M4.1 | implementer | `src/backup.rs` and child modules as needed, `docs/BACKUP_FORMAT.md`: versioned allowlisted archive, limits, inventory, validated preview | | M4.2 | implementer | narrow persistence module, `src/config.rs`/`src/domain.rs` adapters as needed, `docs/PERSISTENCE.md`: safe path handling, validated state, staged writes and recovery contract | | M4.3 | implementer | `src/app.rs`, `src/vault.rs` narrow deletion API, library exports: prepared change sets, initial setup/Commit and mandatory backup ordering | | M4.4 | implementer | application/backup/persistence modules: manual and pre-restart backup APIs, previewed confirmed full restore, restart-history boundary | | M4.5 | implementer | `tests/commit_restore.rs`, test-only failure adapters and README: round trips, fault injection, archive attacks, permission and reservation scenarios | | M4.6 | reviewer, repairer | Independent integrated review, bounded repairs, re-review of failure paths and regressions | | M4.7 | coordinator | Integrated gates, evidence/findings ledger and acceptance/handoff in PLANS.md | M4.1 defines a versioned uncompressed tar archive with a bounded TOML manifest. Use logical fixed member names for settings, server configuration, internal state, and encrypted vault; never restore directly to tar-supplied paths. Record UTC creation time, operation reason (manual/pre-Commit/pre-restore/pre-restart), member inventory/lengths, and historical verification status without inventing working state. Define collision-safe archive naming without overwriting an existing file. A new archive is visible in the list only when fully written and finalized. Capture the currently installed saved bytes, not the proposed draft or decrypted records. Settings and server config may contain secrets: archives require 0600. Exclude source client files, drafts, previous archives, staging/journal artifacts, and delivered plaintext output. Initial setup can lack vault/internal state: represent genuine absence explicitly, not as corruption or an empty fake vault. Restoring that backup must restore absence for those owned components. Missing required settings/server config or read errors are not optional absence. Bound archive/member size and entry count before reading/extracting everything. Reject unsupported manifest versions, missing/duplicate/unexpected members, absolute or traversal paths, links, device/special entries, inconsistent sizes, truncation, and unsafe extension/header interpretations. Parse allowed files in staging before mutation using existing settings/document/vault validators. Do not reject a losslessly supported anomalous peer simply because project_client fails. The tar checksum is not authentication; do not advertise tamper-proof backups or silently add a signing/hash dependency. Bounds/structural validation remain required. M4.2 documents file layout and a versioned internal-state schema for reservations, historical apply evidence, and operation recovery if needed. Keep aliases and client configuration authoritative in the server file. Reject unknown or malformed state rather than silently losing reservations. A file loaded from disk must not self-assert current RuntimeObservation::Verified without runtime verification. Separate orchestration from a narrow filesystem adapter for deterministic errors. Use safe no-follow/anchored path operations, regular-file checks and private staging. Detect target aliasing/overlap (for example vault and server resolving to the same file) before mutations. Do not traverse a symlink or use archive metadata to chmod/chown arbitrary targets. Create files/directories with 0600/0700 from creation; preserve protected directory access and handle existing permissions explicitly. Do not recursively delete the data directory to implement restore. Use same-filesystem temporary files, complete writes, flush/sync, atomic rename, and directory sync where applicable. A rename is not a transaction across server, settings, and data directories. Document ordering, durable recovery markers or an explicit equivalent, and recovery from interruption between replacements before implementing it. Never claim multi-file atomicity just because each rename is atomic. Backup-before-mutation and write recovery are not optimistic locking. On replacement failure, either leave the old set intact or return an explicit incomplete/recovery-required result with the preserved pre-operation backup. Do not publish success, advance saved memory, or accept another mutation over unresolved partial state. Recovery must be deterministic and tested after reopening from partial files. It may finish/restore a local write transaction as documented; it must never apply runtime changes or become automatic failed-restart rollback. Report cleanup failures honestly and keep recoverable evidence if cleanup is unsafe. M4.3 builds a prepared change set containing edited server bytes, staged encrypted vault, and validated internal state. Preview/validate it before installation. Complete the pre-Commit backup before the first protected destination replacement. Failure to create/finalize the backup stops Commit and preserves installed bytes and saved in-memory state. Then stage/install the set and only after success advance saved snapshots and expose successful Commit. A failed Commit retains retryable draft intent without treating possibly partial disk state as consistent. Deletion removes the explicitly selected client record and vault secret in the same committed operation; unknown/missing vault records need clear behavior, since server-only clients are legitimate. Unrelated imported/file-only records remain. Earlier archives remain unchanged. Prepared imports/setup never overwrite an existing vault identity silently and never clear authenticated reissue blocking. Discarding an uncommitted preparation must not change installed files. Persistent settings updates are not a new product feature; Commit handles agreed client and internal changes while settings are included in backups/restoration. M4.4 exposes listing and manual backup plus a reusable pre-restart backup operation for M5, without restarting anything here. Sorting/time labels do not imply a backup is working. Accept historical verification only from an explicit verified-apply boundary intended for M5; represent no verified state honestly. Any identity/token used to associate a saved generation with verification must not be a fresh runtime claim or a new crypto dependency. Never mark pre-restart bytes known-working based on the backup reason alone. Restore has a read-only preview followed by explicit confirmation of that prepared archive. Validate the chosen content and determine affected files before writes; then complete the precautionary backup, stage all replacement/deletion actions, and install. Restore settings, whole server file, internal state, and opaque vault as a complete saved set, not a client merge. Whole-file restoration of the archived server is intentional; ordinary Commit still obeys M2 client-only editing rules. Use current trusted destination mapping and enforce the installation's invariant paths. Archived settings cannot redirect writes outside that mapping or switch data roots during restore; report incompatible paths instead of adding migration logic. Keep the selected validated archive stable between preview and confirmation so confirmation cannot apply different bytes. Do not silently discard the active draft until confirmed; cancellation/validation/backup failure preserves it. Successful restore discards it and reloads the restored saved state, retaining conservative pending reservations across the still-unchanged runtime boundary. Historical restored state cannot assert the current interface matches it. Caller never needs or supplies a password for archive listing, backup, preview or restore. M4.5 injects failures at backup creation/write/finalization, staging writes, sync, each destination replacement, recovery and cleanup. Verify exact file sets and memory transitions rather than only error messages. Avoid permission tests that pass accidentally as root; run unprivileged and inject deterministic failures. Use temporary private directories and generated encrypted bundles. Do not log server fixture contents or decrypted data in failing assertions. #### Acceptance and validation - [x] M4-A1: documented versioned archive/state layouts round-trip settings, server bytes, encrypted vault and internal state without any password/decryption; initial missing vault/state is represented and restored explicitly. - [x] M4-A2: complete backups precede Commit/restore and the future restart boundary; backup/read/finalization failure prevents protected replacements and successful memory transitions. Manual backup/listing works without mutations to saved data. - [x] M4-A3: successful Commit writes the prepared client changes, vault and state; untouched server spans remain identical; saved memory advances only on success, runtime stays unchanged/unknown, and draft discard does not write files. - [x] M4-A4: explicit client deletion removes only its current vault record while server-only and file-only anomalies remain supported; old archives are unchanged. - [x] M4-A5: confirmed restore fully replaces owned saved components, including removal of components absent in the archive, without merging or reading plaintext client sources. Preview/cancel does not mutate or silently discard the draft. - [x] M4-A6: original client source files, unrelated files and all archives outside the selected/pre-operation artifacts are protected from traversal, symlink/link escapes, target aliasing and unsafe tar entries; test bounded parsing and rejection. - [x] M4-A7: 0600 secret-bearing files/archives and 0700 private directories apply from creation, including staging; errors and Debug redact secret-bearing bytes. - [x] M4-A8: injected failures/interruption between multi-file replacements produce no false success or premature saved-memory change; reopening detects incomplete work and follows the documented deterministic recovery path before new mutations. - [x] M4-A9: saved/deleted address reservations survive reopen and restore across unapplied changes; neither action releases them or trusts historical verification as current runtime. Missing/malformed state cannot silently permit address reuse. - [x] M4-A10: listing records UTC reason and explicit historical apply status; pre-Commit/pre-restart archives and newest archives are not automatically working. - [x] M4-A11: incompatible archived destination settings are rejected; restored server/config/vault stay at trusted invariant paths, and no whole-directory wipe or recursive archive inclusion occurs. - [x] M4-A12: M1–M3 regressions and integrated gates pass; independent review closes actionable persistence, archive, backup-order and failure-recovery findings. Planned commands from the repository root after M4 targets and lockfile exist: ```sh cargo test --locked backup cargo test --locked persistence cargo test --locked --test commit_restore cargo test --locked --test vault_import cargo test --locked --test config_preservation cargo fmt --all -- --check cargo clippy --locked --all-targets -- -D warnings cargo test --locked cargo build --locked --release ./target/release/wireguard-admin --help ./target/release/wireguard-admin --version git diff --check ``` Filters must execute actual focused tests. All commands should exit zero; negative cases are assertions within passing tests. Resolve approved added crates and update Cargo.lock before locked checks. Record actual counts, commands and failure-injection coverage. Tests are unprivileged filesystem/library checks only; no container/VM or live restart acceptance is required or claimed in this milestone. #### Progress - [x] Description prepared from accepted M1–M3 interfaces and current documents. - [x] M4.1 — `/root/implementer_m4_1` added `src/backup.rs`, `docs/BACKUP_FORMAT.md`, approved `tar`/`time` dependencies, and six focused archive tests (2026-10-19T20:23:23Z). Coordinator inspected the actual code; final integrated review and gates subsequently passed. - [x] M4.2 — `/root/implementer_m4_2` added `src/persistence.rs`, `docs/PERSISTENCE.md`, and validated reservation restoration in `src/domain.rs` (2026-10-19T20:30:34Z). Integrated review subsequently passed. - [x] M4.3 — `/root/implementer_m4_3` added `src/app.rs`, a narrow vault record-removal API, and one trusted archive-directory accessor (2026-10-19T20:35:58Z). Commit backup/install ordering and draft tests passed integrated review. - [x] M4.4 — `/root/implementer_m4_4` added list/manual/pre-restart backup boundaries and byte-bound restore preview/confirmation, with path-invariant validation and focused tests (2026-10-19T20:42:20Z); integrated review passed. - [x] M4.5 — `/root/implementer_m4_5` added nine cross-module tests and fixed restore reservations plus interface-name invariant (2026-10-19T20:49:14Z). Documentation was synchronized; independent review passed. - [x] M4.6 — fresh `/root/reviewer_m4_r1` reviewed the integrated result; separate `/root/repairer_m4_r1` repaired R1 and R4 findings; R2/R3/R5 re-review closed all with no new actionable findings (2026-10-19T21:10:25Z). - [x] M4.7 — coordinator ran all planned integrated gates and accepted M4 (2026-10-19T21:10:25Z). #### Review and repair | Finding | Iteration | Location and impact | Owner | Resolution and evidence | State | | --- | --- | --- | --- | --- | --- | | M4-R1-01 | R1/R2 | `src/persistence.rs` saved reads accepted existing secret-bearing files with broad permissions. | `/root/repairer_m4_r1` | R2 confirmed fail-closed server/vault mode checks and regression tests. | closed | | M4-R1-02 | R1/R2/R3 | Archive and installation failure paths lacked deterministic evidence. | `/root/repairer_m4_r1` | R3 confirmed partial restore interruption after server replacement, unchanged session saved/draft/applied state, failed rollback and fresh reopen recovery, and intact pre-restore archive; focused test passed. | closed | | M4-R4-01 | R4/R5 | `src/app.rs` lost verified saved-generation history on reopen and mislabeled pre-Commit backups. | `/root/repairer_m4_r1` | R5 confirmed durable history after mandatory backup, truthful archive labeling, reopen history with runtime Unknown, and failure/clear tests. | closed | R1 independently inspected the integrated code; `cargo test --locked --test commit_restore` passed 9 tests and `git diff --check` was clean. Reviewer did not claim Alpine, VM, live WireGuard, or host-service evidence. R2/R3/R5 completed the required re-reviews and closed all findings. #### Evidence and handoff Planning inspected Cargo.toml, M3 vault serialization and source/import APIs, VAULT_FORMAT.md, M1 state methods, M3 recorded acceptance and governing documents. M3 records independent R7 review, 64 unit tests plus 9 preservation and 5 vault import integration tests, and passing coordinator gates. These are historical results; this planning task did not rerun tests or modify application code, dependencies, files in configured runtime directories, or installed vault state. M4.1 worker reported `cargo test --locked --offline` (70 unit, 9 preservation, 5 vault-import), `cargo test --locked --offline backup` (6), Clippy, format, and `git diff --check` passing. These are worker results, not final integrated acceptance. The archive API accepts explicit saved bytes and validates a bounded fixed-member UStar format without extraction; app-level semantic validation and trusted path handling remain for M4.2–M4.4. M4.2 worker reported 10 persistence tests, full suite (80 unit + 9 preservation + 5 vault-import), Clippy, release build, format and diff checks passing. It supplies `SavedStore` with validated read/install/recover, a private journal, and explicit recovery-required results; per-syscall fault injection remains for M4.5. These are worker results only. M4.3 worker reported formatting, Clippy, full tests (89 unit + 14 integration), release build, and diff checks passing. Its `Application` stages public-key edits, pre-Commit backups exact installed bytes, and advances saved memory only after install. Broad-route legacy deletion fails closed because the persisted reservation format holds single addresses; exact-host legacy routes are retained. This is worker evidence, not integrated acceptance. M4.4 worker reported 95 unit + 14 integration tests, formatting, Clippy, offline release build and diff checks passing. Coordinator inspection identified additional reservation/path failure cases for M4.5 tests and independent review. M4.5 worker reported 95 unit + 23 integration tests, focused backup (11), persistence (10), and commit_restore (9), plus format, Clippy, release build, help/version, and diff checks passing. Its tests reproduced and fixed restore dropping current saved-client reservations and accepting an archived interface-name switch. Syscall-level write/fsync fault injection is not yet exhaustive; this remains an explicit review concern. These are worker results, not final acceptance. Next action: fresh independent M4 reviewer, then repair/re-review as needed. R1 returned M4-R1-01 and M4-R1-02 as actionable. Next action: a separate repairer fixes those findings, then the reviewer rechecks. Repairer reported both fixed, with 102 unit + 23 integration tests and formatting, Clippy, release build and diff checks passing (2026-10-19T20:57:23Z). The coordinator has not yet accepted those claims; next action is independent R2 re-review. R2 (2026-10-19T20:58:52Z) closed M4-R1-01 and partially closed M4-R1-02. Focused persistence (15), backup (12 unit + 2 integration), commit_restore (9), and injected Commit test (1) passed. Reviewer requires a restore partial-install failure/recovery test at the application boundary. Next action: targeted repair and R3 re-review; milestone remains in progress. Targeted repair added a private restore-install test seam and regression. R3 (2026-10-19T21:01:08Z) closed M4-R1-02, found no new actionable defects, and passed the focused test plus `git diff --check`. Next action: coordinator integrated gates and A1–A12 decision. Coordinator gates passed on the R3 revision, but a targeted historical-status audit found M4-R4-01 (2026-10-19T21:03:13Z). Those gate results will be rerun after repair. The milestone remains in review; next action is targeted repair and independent re-review. Final integrated evidence after R5 repair (coordinator, 2026-10-19T21:10:25Z): `cargo test --locked backup` passed 14 unit + 2 integration matches; `cargo test --locked persistence` passed 15; `cargo test --locked --test commit_restore` passed 9; `cargo test --locked --test vault_import` passed 5; `cargo test --locked --test config_preservation` passed 9. `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked` (108 library unit + 9 Commit/restore + 9 preservation + 5 vault import, 0 doctests), `cargo build --locked --release`, release `--help` and `--version`, and `git diff --check` all exited zero. All three findings are closed by independent R2/R3/R5; R5 found no new actionable issue. Evidence is unprivileged synthetic/unit-fixture only. No Alpine, disposable systemd VM, live VPN, host service, production file or production key was accessed or claimed. The terminal workflow and real runtime/systemd checks belong to later milestones. M4 has no remaining blocker. #### Decisions and discoveries Vault v1 already fixes data/vault.toml and authenticated conflict behavior. Preserve that format and opaque ciphertext. New state/backup formats belong in their own documents rather than altering vault or managed-comment v1 without need. The persistence layer spans directories and cannot assume a single atomic rename. Interruption recovery and conservative address reservations are required write integrity, not concurrent editing support or automatic failed-restart rollback. Confirmed archive restoration replaces server settings as part of explicit recovery; normal client Commit continues to preserve administrator-owned bytes. The v1 internal-state historical timestamp follows the application's single-administrator save boundary; it is not a digest check against manual out-of-band file changes. Reopen treats history as historical, not current runtime verification. Restoration carries forward pending address reservations for safety while replacing archived client/settings/vault saved content. #### Outcome Accepted after independent R5 review, closure of M4-R1-01/M4-R1-02/M4-R4-01, and all M4-A1–A12 gates. Stop after M4. Begin M5 only on a separate request; no Git commit, deployment or production access occurred. ### Milestone 5 — Runtime observation and systemd restart adapter Status: accepted (2026-10-20T22:59:39+03:00; scoped fixture acceptance and independent integrated re-review complete). Prerequisites: milestones 1–4 accepted; pinned native Rust environment and compatible approved `rustix` and `zbus` dependencies. Ordinary M5 acceptance uses injected adapters and temporary saved stores without root or host-service/network access. Real WireGuard and systemd acceptance stays assigned to milestones 8 and 9 respectively, not silently waived. #### Outcome and scope Deliver a read-only Linux WireGuard runtime adapter, a systemd D-Bus restart adapter, and application orchestration for an explicitly confirmed restart: complete backup, restart the configured unit, await its job, verify the configured interface/peers, persist verification through M4, then publish applied state and release reserved addresses. Supply safe public runtime/statistics views for the future TUI without deriving client identity or online status from aliases. Read SPEC.md Operations and states, Client screen and statistics, and Backups and restoration; ADRs 0001 and 0003; PERSISTENCE.md, BACKUP_FORMAT.md, DEVELOPMENT.md, DEPENDENCIES.md, TESTING.md, and the current application/domain code. Commit and restore must retain their strict no-runtime-mutation behavior. Out of scope: TUI/screens, automatic refresh scheduling in a render loop, delivery or QR, automatic failed-restart rollback/retry, reload/syncconf as an alternative apply path, additional service managers/interfaces, production tests, and creation of the Alpine/VM harnesses. Keep management startup safely incomplete; do not introduce a new restart CLI or run real adapters in default tests. #### Existing code and integration constraints - `Application` owns the saved store/bytes, byte-preserving document, `ClientStates`, vault draft and historical verification. Resolve runtime settings from the current saved generation, including settings restored from a backup, not a stale constructor copy or archive-supplied arbitrary bus/service destination. - `Application::create_pre_restart_backup` archives installed bytes and sets a pending boundary flag. `mark_verified_after_runtime_check` is crate-private: it checks that boundary and saved generation, validates an observed Snapshot, installs verification state through SavedStore's journal, then advances memory. Wire these existing boundaries into the real sequence; do not bypass them with a direct state-file write or public "mark verified" setter. - `PersistedState` v1 holds reservations and a historical UTC timestamp. Reopen is always Unknown. Commit/restore clear stale verification; M5 records the timestamp only after matching runtime and successful journaled write. BACKUP_FORMAT.md ties archive historical status to that saved generation, not merely the date. - The existing `pre_restart_backup_pending` boolean is not proof of a successful restart. Scope/invalidate authorization per attempt, failure, cancellation and saved-generation change; an old successful backup must never authorize later unrelated verification. Refine the internal contract if needed, preserving M4 guarantees and tests. No new concurrency/version-control scheme is required. - `Snapshot` includes aliases and a narrow ClientAddresses model; kernel peers do not contain aliases or managed comment timestamps. Do not compare raw kernel structures directly against Snapshot equality or invent runtime metadata. - M2 `ConfigDocument::peers()` retains full AllowedIPs and redacted issues; some valid peers cannot project into M1 Client. Include every authoritative peer in technical verification, or fail closed as unverifiable. Never ignore unsupported records to obtain a matching reduced subset. Missing/invalid managed alias alone must not be mistaken for evidence of a different runtime peer. - Cargo already contains time, rustix, tempfile and persistence/crypto dependencies. The approved `wireguard-uapi` 3.0.1 public read path cannot guarantee complete, bounded dumps (M5-R1-04). Use existing approved `rustix` with narrow `net`/`event` features for a checked read-only Generic Netlink GET adapter; add approved zbus only when needed. Inspect current official APIs/features and Rust compatibility. Do not assume older zbus blocking examples apply to the selected version. No unapproved direct Tokio/executor/mock crate or utility subprocess fallback is authorized. #### Context and implementation steps Run tasks sequentially with one writer under the established model policy: GPT-6-Sol/medium coordinator; GPT-6-Luna/high implementer, independent reviewer and repairer. Dispatch owned files, exact interfaces, acceptance IDs, checks and required evidence. Workers do not mark milestones accepted or edit PLANS.md. | Task | Owner | Files and deliverable | | --- | --- | --- | | M5.1 | implementer | `src/runtime.rs`, narrow domain/application views and `docs/RUNTIME.md`: public observation model, saved technical projection and pure comparison contract | | M5.2 | implementer | runtime adapter modules, Cargo files/library exports: approved-rustix read-only WireGuard Generic Netlink codec/transport with isolated conversion/error tests; split pure decoder then bounded socket transport | | M5.3 | implementer | `src/service.rs`, child modules if necessary: zbus systemd restart/job observation with deadline and a mockable transport boundary | | M5.4 | implementer | `src/app.rs`, narrow M4 state/persistence integration: confirmed restart orchestration, per-attempt authorization, durable verification and honest failure results | | M5.5 | implementer | `tests/runtime_restart.rs`, test-only adapters, README/RUNTIME/PERSISTENCE documentation: integrated success, order and failure scenarios | | M5.6 | reviewer, repairer | Independent integrated review, bounded fixes and re-review, including M4 persistence regressions | | M5.7 | coordinator | Integrated gates, evidence/findings and milestone acceptance/handoff in PLANS.md | M5.1 defines public observations keyed by PublicKey, with explicit interface presence/unavailability, peer AllowedIPs, last handshake, received/transmitted byte counters, and observation time. Preserve zero/never-handshaken separately from unavailable data. Counters are current interface values, may reset after a restart, and are not historical usage or proof of online/offline status. Traffic is reported from the server interface's perspective. Alias display joins by public key using saved metadata, never list index or filename. Define technical equivalence independently of counters, handshake, peer ordering, route ordering, aliases, dynamically learned endpoints and timestamps. Verify the expected configured interface exists and the complete saved peer public-key set and normalized AllowedIPs match, including absence of deleted/extra peers and an empty valid peer set. Report missing/extra/mismatched/unverifiable records with public identities and categories. Duplicate/ambiguous saved identities or partial runtime dumps cannot pass. Do not claim comparison of fields the adapter cannot observe. Retain full route sets for legacy peers; enrich M1 projection narrowly only if required and do not drop valid data to fit Snapshot. Document the boundary between technical match and M4's narrow observed Snapshot: use saved aliases only after technical peer/route equivalence is established, not as alleged kernel observations. If the complete document cannot be safely verified/represented for durable marking, keep the operation unverifiable and reservations retained rather than passing a fabricated subset to M4. M5.2 uses the configured interface via Linux WireGuard/netlink APIs only; no set, add/remove, module loading, host route manipulation or external `wg` invocation. Handle multipart responses and peer/AllowedIPs fragments according to the [kernel protocol](https://docs.kernel.org/netlink/specs/wireguard.html); require matching request sequence, an error-free terminal `NLMSG_DONE`, no interrupted dump and a complete response before claiming absence or equality. Bound reads/waits and aggregate limits. Convert no-device, permission, malformed data, transport failure and timeout to distinct actionable, redacted outcomes. WireGuard library structures may include server private or preshared keys even when only public statistics are needed. Never derive/log their full Debug, retain them in public observations, or serialize them as status data. Minimize lifetime and clear supported owned buffers; document unavoidable library copies honestly. Default tests exercise supplied decoded inputs/test transport, never a host socket. M5.3 connects to the system bus and fixed systemd Manager API for the validated configured unit, requests an actual restart, and follows the returned job to its terminal result. Subscribe/install observation before submission so immediate completion cannot be missed; correlate by the actual job and unit, ignore unrelated signals, and handle disconnection/name-owner changes or subscription failure. Successful request submission, job disappearance, or a unit already being active is not completed restart evidence. Accept only the documented successful terminal result, then let runtime verification decide whether VPN configuration matches. Account for wg-quick oneshot units that are active/exited; do not demand a daemon PID or long-running process that WireGuard itself does not require. Use a monotonic configured deadline across D-Bus connection/subscription/request, job waiting and post-job verification rather than resetting it on each retry. Backup completion precedes service mutation but may have a separate filesystem failure boundary. A timeout bounds the application's wait, not proof that systemd stopped its job. Report "may still complete" when appropriate; do not retry a restart or issue broad cancellation/rollback automatically. Ordinary test code uses a supplied job event/transport adapter; no real system bus is contacted. M5.4 adds explicit preparation/confirmation for restart and draft discard suitable for a future TUI. Preview/cancel does not save/discard a draft or call systemd. On confirmed discard-and-restart, reject unresolved persistence recovery, validate saved input and configured unit, complete the pre-restart backup, discard only as authorized and restart only the saved configuration. Never implicitly Commit. A backup failure leaves protected state and draft unchanged and submits no job. Check compatibility with service-managed config rewriting, notably wg-quick SaveConfig behavior: do not edit administrator-owned settings to work around it or claim saved-generation verification when shutdown has overwritten intended peer changes. Detect an unsafe/unverifiable apply precondition and explain it; use a safe test fixture/service setup, not an automatic production setting change. Demote stale current verification when a restart is submitted; a failed request, job, read or comparison cannot leave a misleading previously Verified state. Keep reservations and recovery information conservative. Read back the complete runtime after successful job completion within the deadline; do not require a client to connect or handshake, since correct peers can be idle. Periodic read-only observations may report correspondence, but must not release deleted addresses or persist successful restart history by bypassing the explicit sequence. Only a matching full observation from the successful attempt can reach M4's journaled verification boundary. If the saved-generation check or state write fails after VPN restart, report "runtime changed, verification not durably recorded" rather than full success. Retain reservations/in-memory safety and honor RecoveryRequired before later mutations. Do not rollback the VPN, repeat restart automatically, mark the old history as current, or remove the backup. M5.5 uses fake monotonic time and deterministic adapter event sequences, temporary saved stores and synthetic configuration. Assert call order: backup completed before restart request; successful job before runtime verification; matched observation before journaled state publication; persistence success before memory and reservation release. Failures must be visible by stage, including whether a restart was submitted/may still be active, backup location, and any required manual recovery. Avoid sensitive D-Bus/library error text in reports. #### Acceptance and validation - [x] M5-A1: public read-only observation distinguishes missing/unavailable/unknown, no handshake and valid zero counters; public-key joins and counter resets do not invent online state, aliases, traffic history, or successful apply. - [x] M5-A2: configured-interface runtime adapter consumes complete bounded peer data, including fragmented route lists, and exposes only public information; no runtime mutations, secret Debug output or default host access occur. - [x] M5-A3: pure verification covers dual-stack and IPv4-only peers, reordered sets, empty peers, missing/extra/deleted peers, route mismatches, malformed or partial observations, and unsupported saved shapes without subset success. - [x] M5-A4: service adapter targets only the configured systemd unit, tracks the returned job without a fast-completion race, and handles unrelated events, failed/canceled jobs, disconnects, permission errors and monotonic timeout. - [x] M5-A5: explicit cancellation preserves the draft; confirmed draft discard never commits. Failed mandatory backup submits no restart and preserves draft, saved bytes, reservations and previous historical evidence. - [x] M5-A6: successful restart alone is insufficient: interface/peer verification must pass before M4 marking; idle clients need no handshake to be applied. - [x] M5-A7: each failure invalidates that attempt's authorization; a stale backup flag, old successful job, read-only refresh or old verified Snapshot cannot mark a later generation applied or release a reserved address. - [x] M5-A8: verified state and timestamp become visible only after M4 journaled state persistence succeeds. Injected post-restart write/recovery failure retains safe reservations and reports changed runtime without false overall success. - [x] M5-A9: reopening still treats verification timestamps as historical; Commit and restore retain their no-runtime-mutation behavior and invalidate stale apply evidence. Archived settings changes select the restored unit/timeout correctly. - [x] M5-A10: timeouts do not imply a job was canceled; no automatic restart retry, live setting rewrite, reload/syncconf fallback or VPN rollback is introduced. - [x] M5-A11: full M1–M4 regressions and integrated M5 fake-adapter tests pass with no root, Docker, real system bus/interface, production paths or secret reads. - [x] M5-A12: independent review closes actionable runtime/service/race/persistence findings; documentation clearly defers real WireGuard checks to M8 and actual systemd/D-Bus acceptance to M9 rather than claiming them from mocks. Planned commands from the repository root once M5 targets/lockfile exist: ```sh cargo test --locked runtime cargo test --locked service cargo test --locked --test runtime_restart cargo test --locked --test commit_restore cargo test --locked --test config_preservation cargo test --locked --test vault_import cargo fmt --all -- --check cargo clippy --locked --all-targets -- -D warnings cargo test --locked cargo build --locked --release ./target/release/wireguard-admin --help ./target/release/wireguard-admin --version git diff --check ``` Ensure filters execute actual tests and report counts and exit status. Resolve approved dependencies and update Cargo.lock before locked gates. Default tests must remain deterministic, unprivileged and independent of a daemon/bus/network. No bare management launch, actual restart, or host WireGuard probing is permitted. #### Progress - [x] Description prepared against accepted M1–M4 interfaces and current documents. - [x] 2026-10-20T20:44:56+03:00 preparation: confirmed M1–M4 accepted in this plan, inspected the worktree and role configuration; stopped before dispatch because the active coordinator model/effort is not exposed for verification. - [x] 2026-10-20T21:39:34+03:00 execution resumed: verified the active session's latest turn context as `gpt-6-sol`/`medium` in its local session record, and confirmed the four configured roles use `gpt-6-luna`/`high`; reread required project documents and inspected the actual repository. The earlier model-verification blocker is resolved. - [x] M5.1 — implementer delivered pure public observation and full saved-peer comparison in `src/runtime.rs`, `docs/RUNTIME.md` and library export. Independent R1 review found wrong-interface acceptance and two test gaps; repairer fixed all three; fresh R2 re-review found no remaining actionable M5.1 finding. Final `cargo test --locked runtime` passed (14 matching tests, including 10 runtime-module tests; 104 library tests filtered). This is partial M5 work, not milestone acceptance. - [x] M5.2 — after confirming `wireguard-uapi` 3.0.1's incomplete/unbounded public read API, a read-only explorer identified existing approved `rustix` `net`/`event` as a bounded transport route. Implementers delivered a checked pure decoder (`src/runtime/netlink.rs`) and nonblocking read-only socket transport (`src/runtime/transport.rs`) with synthetic/fake-I/O tests. Independent R2 and R3 reviews found three on-wire/error defects; repairer fixed all, and independent re-review closed them. Latest focused `cargo test --locked runtime` passed 29 matching tests; fmt, Clippy and diff checks passed. No host socket or real WireGuard behavior was tested; M8 remains responsible for isolated real adapter validation. - [x] M5.3 — approved `zbus` 5.19.0 async-io adapter and pure job tracker implemented in `src/service.rs` and `src/service/dbus.rs`, with a shared monotonic deadline and signals installed before restart. Independent R1 review found alias/primary-unit mismatch; repairer resolved `GetUnit` + `Unit.Id` before restart and R2 re-review closed the finding. `cargo test --locked service::` passed 18 fake/pure tests; fmt, Clippy and diff checks passed. No system bus or real service was contacted; M9 VM acceptance remains pending. - [x] M5.4 — `Application` now exposes saved-bound restart preview, explicit cancel/restart/discard choices and redacted stage-aware reports; backup precedes discard/submission, full technical match and one-use attempt authorization precede M4 journaled marking. SaveConfig/recovery/stale-generation checks and fake adapter tests added. Independent R1 found false “may still complete” on known terminal job failure; repairer corrected it and fresh R2 review closed the finding. Latest `cargo test --locked app` passed 33 matching tests; fmt, Clippy and diff checks passed. No host bus/interface was accessed. - [x] M5.5 — added `tests/runtime_restart.rs` public preview/cancel/restored-settings integration tests (2 passed), private fake-adapter/clock deadline coverage in `src/app.rs` (34 app-filtered tests passed), and README/RUNTIME/PERSISTENCE documentation distinguishing synthetic M5 evidence from later real M8/M9 validation. `cargo test --locked` passed 161 library units plus 25 integration tests; fmt, Clippy and diff checks passed. Full independent integrated review remains M5.6. - [x] M5.6 — fresh independent integrated review found six actionable API, reporting, documentation and deadline findings; repairer addressed them in two bounded rounds, and independent re-review closed all six. The reviewer found no remaining actionable scoped M5 issue. Real kernel/systemd behavior remains M8/M9 work. - [x] M5.7 — coordinator inspected the integrated worktree, ran every planned command on the final revision, recorded results below, and accepted M5-A1–A12 for the specified synthetic/fake-adapter scope. #### Review and repair R1 independent reviewer findings and R2 disposition: - M5-R1-01 (P1, `src/runtime.rs` interface observation/comparison): a peer/route match from the wrong interface could pass. Repairer added observed interface identity and a configured-name argument to comparison; wrong-name regression now fails closed. R2 independently confirmed closure. - M5-R1-02 (P2, `src/runtime.rs` test coverage): no direct invariance test for handshake changes and counter resets. Repairer added one; R2 confirmed closure. - M5-R1-03 (P2, `src/runtime.rs` test coverage): missing malformed-route and structural-document subset-match regressions. Repairer added both; R2 confirmed closure. - M5-R1-04 (blocking dependency/API limitation, M5.2): independent reviewer confirmed `wireguard-uapi` 3.0.1 `src/linux/socket/wg_socket.rs:87-108` loops on `iter.next()` without a deadline and uses `while let Some(Ok(response))`, allowing a receive error after a fragment to terminate the loop and return a partial `Device` as success. The public API provides no completeness proof or read timeout. Secret-bearing decoded fields can be kept private by careful consumption, but that does not fix boundedness or completeness. An abandoned timeout thread also cannot cancel the read. Resolution: read-only explorer verified `rustix` 1.1.5 provides public safe AF_NETLINK/NETLINK_GENERIC socket and polling APIs when its approved direct dependency enables `net`/`event`. It does not provide a WireGuard codec, so M5.2 must implement a small checked decoder with explicit complete-dump and aggregate-limit fixture tests, followed by a bounded socket transport. This is an implementation choice within the approved dependency list, not a new direct dependency or specification change. No unsafe fallback or partial-success adapter is accepted; this finding remains open until tested. - M5-R2-01 (P1, `src/runtime/transport.rs` request IFNAME encoding): the initial builder put the NUL outside `nla_len`; repairer included it in the payload and added exact-byte assertions. Independent R3 re-review confirmed closure. - M5-R2-02 (P2, `src/runtime/transport.rs` family errors): type validation made `NLMSG_ERROR` handling unreachable; repairer added checked errno mapping for no-device, permission and transport failures and injected tests. Independent R3 re-review confirmed closure. - M5-R3-01 (P1, `src/runtime/transport.rs` IFNAME attribute number): initial request used type 3 (private key) instead of kernel-defined IFNAME type 2; repairer corrected it and added a literal numeric assertion independent of the implementation constant. R3 reviewer follow-up confirmed closure. - M5.3-R1 (P2, `src/service.rs`/`src/service/dbus.rs` alias correlation): the tracker initially compared `JobRemoved.unit` to the configured literal, which can be an alias while systemd reports the primary unit name. Repairer added bounded read-only `GetUnit`/`Unit.Id` resolution before `RestartUnit`; the request still targets the configured name and the terminal event must match both primary ID and returned job path. Synthetic alias/failure/order tests passed; fresh R2 reviewer confirmed closure. - M5.4-R1-01 (P2, `src/app.rs` service failure report): submitted terminal `JobRemoved` failures were incorrectly reported as possibly still completing. Repairer distinguished terminal failure from ambiguous request/wait timeout or disconnect while preserving submission/manual-recovery fields. Synthetic cases for failed, dependency, timeout, canceled, skipped and unknown results passed; fresh R2 reviewer confirmed closure. M5-R1-04 is now resolved at the implementation-choice level with a bounded `rustix` decoder/transport and synthetic tests; actual kernel behavior is deferred to M8 and has not been claimed here. Integrated review finding IDs below are prefixed `M5-I` to distinguish them from earlier task-local reviewer IDs: - M5-I-R1-01 (P1): a public raw systemd adapter could be called without the mandatory application backup. Repairer made it crate-private and added a compile-fail public-API doctest; independent R2 confirmed closure. - M5-I-R1-02 (P1): a public domain verification setter bypassed the application sequence. Repairer restricted it to the crate; R2 confirmed closure. - M5-I-R1-03 (P2): runtime failures collapsed to one uninformative category. Repairer added six redacted runtime-failure categories; R2 confirmed closure. - M5-I-R1-04 (P2): TESTING.md incorrectly described the `wireguard-uapi` read adapter. Repairer documented the actual approved-rustix path; R2 confirmed. - M5-I-R2-05 (P2): expiry of the shared application deadline could be reported as a generic runtime-read failure or mismatch. Repairer added a distinct deadline result and deterministic before/after-read fake-clock tests; the independent reviewer re-reviewed and closed it. - M5-I-R2-06 (P2): RUNTIME.md called the now crate-private service adapter a public API. Repairer corrected the boundary description; independent re-review closed it. The fresh integrated reviewer reported no further actionable issue against M5-A1–A12 after the second repair round. Review and tests use synthetic state; they do not establish real kernel or systemd/D-Bus behavior. #### Evidence and handoff Planning inspected current Cargo dependencies, Application backup/verification boundaries, persistence/state schemas, backup metadata rules and M4 evidence. M4 records independent R5 acceptance and coordinator results: 108 library unit, 9 Commit/restore, 9 preservation and 5 import tests, plus completion gates. Those are historical recorded results; this planning task did not rerun Cargo checks, add dependencies/source, dispatch agents or contact a system bus/VPN. 2026-10-20 preparation confirmed `.codex/config.toml` requests `gpt-6-sol`/ `medium`, all four `.codex/agents/*.toml` request `gpt-6-luna`/`high`, and the available spawn-agent catalog lists those models and efforts. The active primary session exposes only the GPT-6 family, not its exact model or effort; project defaults can be overridden by a live session. Thus the mandatory coordinator verification is incomplete. The worktree already had a user change to this M5 description; it was preserved. `rustc`/Cargo reported 1.97.1, rustfmt 1.9.0 and Clippy 0.1.97. `git diff --check` passed before this status edit. No M5 code, tests, dependencies, worker dispatch or acceptance checks ran during that earlier preparation. The active session was subsequently verified by matching `CODEX_SESSION_ID` to its local rollout record and reading the latest `turn_context` model and effort: `gpt-6-sol` / `medium` (2026-10-20T18:36:50.175Z). The four role files explicitly configure `gpt-6-luna` / `high`; M5 workers will be spawned with those effective model/effort overrides and their configured roles. This resolves the prior configuration blocker without substituting models. 2026-10-20T21:50:55+03:00 coordinator validation of the partial integrated revision (all exit 0): `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked` (118 library unit, 9 Commit/restore, 9 config-preservation and 5 vault-import tests passed; main/doc tests 0), `cargo build --locked --release`, `cargo test --locked runtime` (14 matching tests, of which 10 are in the runtime module), each of the three named M4 integration targets (9/9/5 passed), release `--help` and `--version`, and `git diff --check`. No service or runtime_restart test target exists yet, so those planned checks were unavailable and not counted as passing. `wireguard-uapi` and `zbus` are not in the retained manifests/lockfile. No root, Docker, host interface, system bus, production data, Alpine or VM test was used. Passing partial gates does not establish M5-A2 or M5-A4–A12. 2026-10-20T22:03:03+03:00 continuation: active coordinator session rollout again showed `gpt-6-sol` / `medium`; role files still specify `gpt-6-luna` / `high`. Worktree remained the prior partial M5 result. Read-only explorer examined cached `wireguard-uapi`/`rustix` sources and kernel protocol docs; no socket or production state was accessed. The chosen `rustix` route needs checked Generic Netlink family resolution, exact sequence/DONE/error handling, fragment coalescing, monotonic deadline and strict limits before M5-A2 can pass. Final M5.7 validation on the integrated revision (all exit 0): `cargo test --locked runtime` (32 matching tests), `cargo test --locked service` (19 matching tests), `cargo test --locked --test runtime_restart` (2), `cargo test --locked --test commit_restore` (9), `cargo test --locked --test config_preservation` (9), `cargo test --locked --test vault_import` (5), `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked` (163 library unit tests, 25 integration tests and one compile-fail doctest passed; binary tests 0), `cargo build --locked --release`, release `--help` and `--version`, and `git diff --check`. The release binary reports that the management interface is not implemented, as expected. Tests were unprivileged synthetic/fake-adapter checks; no Docker, Alpine, systemd VM, host bus/interface, production path, private key or real restart was accessed. M8 and M9 still require their own real-system acceptance and cannot inherit a passing result from these checks. Next action: stop after accepted M5. M6 may begin only on a separate request; M8 Alpine and M9 disposable-VM validation remain future milestones. M5.3 prerequisite decision: use approved `zbus` 5.19.0 with `async-io` and default features disabled. A standard-library wake/park executor with one monotonic deadline can bound connection setup, match-rule registration, method calls and signal waits without an unapproved direct runtime dependency. Subscribe and install observation before `RestartUnit`; buffer/handle a fast completion that arrives before the method reply. Confirm real signal and alias behavior in the disposable systemd VM at M9; fake events here are not VM evidence. #### Decisions and discoveries M4 verification persistence already supplies the intended durable boundary, but its pending-backup flag is only one prerequisite. M5 must establish restart/job and runtime evidence before invoking it and invalidate per-attempt authority. Aliases are server metadata, not kernel data; verification must separate technical correspondence from display/domain projection. A successful service job and a successful durable application record are distinct stages with distinct failures. Primary API references for implementation (check the chosen version at dispatch): [systemd D-Bus](https://www.freedesktop.org/software/systemd/man/latest/org.freedesktop.systemd1.html), [wireguard-uapi](https://docs.rs/wireguard-uapi/latest/wireguard_uapi/), and [zbus](https://docs.rs/zbus/latest/zbus/). These references do not pin new versions. #### Outcome M5.1–M5.7 are complete. Accepted for the milestone's explicitly scoped synthetic/fake-adapter checks after independent integrated review, two repair rounds, re-review and all planned coordinator gates. No real WireGuard or systemd/D-Bus result is claimed; M8/M9 remain future acceptance stages. No Git commit, deployment or production access occurred. Stop after M5; do not start M6 without a separate request. ### Milestone 6 — Client configuration delivery and reissue lifecycle Status: accepted (2026-10-21T21:50:26+03:00; independent integrated re-review closed findings and all scoped M6.7 gates passed). Prerequisites: milestones 1–5 accepted; pinned native Rust environment and compatible approved dependencies. Validation uses generated keys, synthetic saved stores and temporary output directories without root, a terminal, Docker, production inputs or any actual runtime/service call. #### Outcome and scope Deliver testable application/library operations to create a new client with a session-held private key, Commit its encrypted record and server entry, save its plaintext WireGuard configuration after Commit, and optionally produce a QR code for the same delivery. Subsequent issuance requires a fresh password per selected client. Output errors permit changing the destination and retrying initial issuance during the current creation session without password entry. Read SPEC.md New client parameters, Encrypted vault, Connection delivery, Importing existing clients and Operations and states; ADRs 0001–0003; VAULT_FORMAT.md, CONFIG_FORMAT.md, PERSISTENCE.md, RUNTIME.md, DEVELOPMENT.md, DEPENDENCIES.md, TESTING.md and the current Application/VaultBundle code. Out of scope: TUI/password entry widgets, rendering live terminal screens, plaintext source-file fallback, automatic key rotation/reconciliation, adding connection settings, runtime mutation, automatic restart, actual phone/client import, Alpine harness and VM tests. Keep startup safely incomplete. Generation and saving an output file do not apply configuration to WireGuard. #### Existing code and integration constraints - `Application::add_client` currently accepts a prepared public `Client` and edits server/domain drafts. It does not generate or own a private key or authorize first delivery. Add a coordinated creation operation rather than treating any existing/imported public key as eligible for password-free output. - `WireGuardPrivateKey::generate`, `public_key`, and explicit Base64 exposure exist. `VaultBundle::encrypt_record` encrypts a new key using the recipient, while `retrieve_client_record` returns short-lived retained connection fields only after password-based decryption and authenticated conflict checks. - New-key records currently have no retained client parameters. Build their configuration from authoritative saved peer addresses and current saved application settings, not empty record defaults. Imported records can contain DNS/MTU/endpoint/routes/keepalive/PSK; preserve supported existing parameters rather than silently converting every imported client to a new default template. - `RetrievedClientRecord` exposes its key and optional preshared key explicitly and redacts Debug; do not turn it into a globally cached unlocked vault or add a password-free retrieval API to work around first-delivery state. - `Application::commit` saves staged document/vault/state only after backup and journaled installation succeeds. `delete_client`, `discard_draft`, restore, saved settings and restart choices can affect delivery eligibility. Integrate with these transitions without regressing M4/M5 or bypassing recovery-required handling. `Application::document()` is a draft; output must use committed bytes. - M5 observes public technical runtime fields and does not supply a trusted server private key for generation. Derive the server public key from the authoritative saved `[Interface] PrivateKey` through existing X25519 code, in a narrow secret boundary, or use an already validated authoritative derivation if available. Missing/ambiguous/invalid server identity blocks output rather than guessing or reading another file/interface. Never print or serialize the server private key. - Cargo already contains the key/crypto, file-safety and time dependencies. Approved `qrcode` may be added with minimal features (no image/SVG output needed). No decoder, image, CLI, TUI, random, hash or new helper crate is implicitly approved. Check official APIs/version compatibility before adding the crate. #### Context and implementation steps Run bounded tasks sequentially, one writer at a time. GPT-6-Sol/medium coordinates; GPT-6-Luna/high implements, independently reviews and repairs. Dispatch file ownership, prerequisite interfaces, acceptance IDs and actual required checks. Only the coordinator updates milestone acceptance and evidence in PLANS.md. | Task | Owner | Files and deliverable | | --- | --- | --- | | M6.1 | implementer | `src/delivery.rs` and child modules as needed, library exports, `docs/DELIVERY.md`: secret-safe payload model and deterministic configuration generation | | M6.2 | implementer | `src/app.rs`, narrow delivery/domain integration: coordinated new-client creation and session-scoped first-issue state | | M6.3 | implementer | delivery/files module, existing safe filesystem helpers: file preparation, explicit overwrite confirmation, secure output and retry results | | M6.4 | implementer | delivery/QR module, Cargo files, app integration: optional matrix/text output bound to the same authorized payload/file action | | M6.5 | implementer | `tests/client_delivery.rs`, focused unit tests and README/DELIVERY guidance: full first-issue/reissue and failure lifecycle | | M6.6 | reviewer, repairer | Independent integrated review, bounded findings repair and re-review | | M6.7 | coordinator | Integrated validation, evidence ledger and scoped acceptance/handoff in PLANS.md | M6.1 renders a deterministic standard WireGuard client file: `[Interface]` with client private key, saved addresses and DNS; `[Peer]` with server public key, endpoint and routes. New clients have IPv4/IPv6 and routes `0.0.0.0/0, ::/0`, using the configured template and authoritative saved host addresses. Define the client Address prefix policy explicitly, keeping host identity separate from server-side AllowedIPs; do not infer client addresses from the public endpoint. No new MTU, keepalive or PSK defaults are introduced. For imported reissue, retain validated existing optional fields and IPv4-only shape where applicable. Never execute or copy hooks/unknown arbitrary directives from source configs. Validate decrypted fields again at rendering boundaries so newlines/control text cannot inject sections/directives. Compare retained identity and addresses/server binding with fresh saved authoritative values; a stale or mismatched record fails closed instead of silently regenerating a different connection. Respect authenticated reissue_blocked, missing server/file-only cases, and M2 unsupported projection without truncating routes to get a valid output. Document precedence between saved server state, application defaults for new clients, and retained imported fields; do not invent a conflict-resolution UI. Keep plaintext payloads in explicitly exposed, short-lived zeroizing owners, redacted Debug/errors and no generic Serialize/Display. Raw source/client output is never an ordinary status object. Bound payload size. Passwords, encoded private keys and rendered files must not appear in logs, panic output, snapshots or test failure diagnostics. Document unavoidable encoding/QR-library copies honestly. M6.2 coordinates address selection, key generation, server/client draft insertion, vault encryption and temporary first-issue tracking as one preparation. Use M1 allocation/reservations and optional manual selection; reuse M2 edits and M3 key matching. Failure at any preparation step leaves prior draft and eligibility unchanged. Existing vault setup may require its initial password separately; creating additional clients in an existing vault does not request that password. Define an internal lifecycle such as pending-draft, committed-awaiting-first-file, and issued/invalidated, keyed by PublicKey and actual creation/committed record context rather than alias, UI row, or historical public-key presence. Store no first-issue private key, password, or eligibility in settings, internal state, backup, vault plaintext fields or recovery journal. Session state is not recreated by reopen/restore/import. A failed/incomplete Commit cannot promote eligibility. Unrelated valid commits/renames must not accidentally authorize another key or consume an unused first issue; compare the relevant saved client/record context. Discard/delete/restore/reopen/session end and any relevant key/record replacement invalidate stale pending material. Define transition timing so cancellation and failed backup/pre-install validation preserve unchanged work, while a successful replacement or unresolved partial persistence cannot retain unsafe output authority. A confirmed restart applying an unchanged saved client must not require a password merely because runtime state changes. Do not conflate access activation with the first-output permission. Successful saved deletion revokes output authorization immediately, even if the old runtime peer remains active until restart. M6.3 prepares destination selection without writing, then requires explicit confirmation if a file exists. Never derive a path directly from an alias that may repeat or contain separators; use a safe suggested filename and explicit selected destination. Reject symlink/special-file destinations and unsafe ancestor traversal; do not overwrite internal application files, server config, vault, settings, journal or backups as a client output. Output goes only to the chosen location; no directory-wide chmod/deletion or source-file cleanup. Use secure temporary/staged writes, mode 0600 from creation, complete flush/sync, atomic publication and parent sync as appropriate. New-file publication must not silently replace a file; overwrite requires a confirmed bounded action for that destination. Document behavior if failure follows publication (for example parent sync failure): the file may already exist and must be reported honestly, with explicit confirmation on retry, not claimed absent or automatically removed. Output failure before confirmed success preserves retry eligibility for the same initial client in the current session, allowing a different directory. A normal plaintext file must be saved for every delivery action; QR-only success is not allowed. Success consumes initial eligibility and drops the cached private key. Later file/QR issuance for that client requires a fresh password. Password reissue is per action/client, not session-wide vault unlocking; wrong password, conflict or missing key has no source-file bypass. Retries may keep only bounded operation-local material, never a reusable unlocked identity across unrelated actions. M6.4 generates an optional QR from the exact bytes saved in that delivery action, using qrcode's matrix/public rendering API without PNG/image features or external qrencode. Support a future terminal renderer through a matrix/controlled text representation with quiet zone and documented dimensions. Keep secret-bearing matrix/text out of Debug, snapshots and ordinary progress messages. Do not expose an unguarded second QR path that reconstructs private material from cached state. One authorized action may include both file and QR; showing that same prepared QR does not require a second password. If QR encoding exceeds capacity or fails, the file can still be successfully delivered: report file success and QR failure separately, consume first-issue eligibility, and do not report a total failure that misleadingly permits another first file. A later new QR/delivery action then requires password authorization. A future TUI displays/clears the operation-local QR; terminal lifecycle implementation remains M7. M6.5 covers complete library flows with private temporary stores and generated synthetic keys. Use fault-injected file writes and unit-tested authorization transitions, not privileged filesystem behavior. Assert file content through redacted comparisons/parsed public values and explicit key correspondence without printing secrets. Reuse M3 parsing for compatible generated files to demonstrate identity/server/routes semantics. File/QR equality should be guaranteed by a single payload source and tested at the encoding boundary; do not claim external QR decoding/real-device compatibility unless actually checked or add a decoder crate without approval. #### Acceptance and validation - [x] M6-A1: new clients yield valid full-tunnel dual-stack configurations using committed addresses/settings and a correctly derived server key; missing or ambiguous authoritative identity fails without guessing or exposing secrets. - [x] M6-A2: imported reissue preserves supported existing optional parameters and IPv4-only shape; stale identity/address/server bindings, authenticated conflict flags and unsupported data block unsafe delivery without truncation. - [x] M6-A3: coordinated creation stages a matching public client and encrypted key; failed preparation leaves prior drafts intact. No output is authorized before successful Commit or from an imported/existing key without a password. - [x] M6-A4: failed/incomplete Commit does not promote first issue; a successful Commit permits first output for the newly created client in that session. Draft discard/delete/restore/reopen and record replacement cannot restore or transfer stale eligibility; duplicate aliases remain independent by public key. - [x] M6-A5: failed file save supports same-session retry/new directory without password for initial issuance; success consumes eligibility. Reissue requires fresh per-client authorization with no cached identity/plaintext-file fallback. - [x] M6-A6: mandatory plaintext file creation uses 0600 from first write, safe paths, bounded payloads, explicit overwrite confirmation and honest partial- publication failure reporting; protected application/source files stay untouched. - [x] M6-A7: optional QR uses the exact same payload as the saved file; QR failure does not erase file success or grant another first issue. No independent QR-only bypass or retained cross-action secret cache exists. - [x] M6-A8: outputs warn when saved changes are not verified applied. Unavailable runtime is not silently treated as active; delivery never invokes service or WireGuard operations, releases reservations or changes apply history. - [x] M6-A9: rendering/input injection, wrong password, missing vault/record, duplicate/conflicting metadata, occupied/manual address failures, overwrite decline, symlink/special paths, and write/flush/publication/sync failures are tested. - [x] M6-A10: secret-bearing types, errors/Debug and tests do not disclose keys, passwords, configuration payloads or QR matrices; ephemeral material is never persisted in backup/journal/state and supported owned buffers clear on drop. - [x] M6-A11: M1–M5 regressions and integrated gates pass unprivileged with no real network, system bus, runtime, production paths or terminal required. Independent review closes actionable auth-lifecycle/rendering/output-safety findings. Planned commands from the repository root after M6 targets and lockfile exist: ```sh cargo test --locked delivery cargo test --locked --test client_delivery cargo test --locked --test runtime_restart cargo test --locked --test commit_restore cargo test --locked --test vault_import cargo test --locked --test config_preservation cargo fmt --all -- --check cargo clippy --locked --all-targets -- -D warnings cargo test --locked cargo build --locked --release ./target/release/wireguard-admin --help ./target/release/wireguard-admin --version git diff --check ``` Ensure focused filters run actual tests. Resolve approved qrcode and update Cargo.lock before locked checks; do not add Ratatui/Crossterm prematurely. Record commands, counts and exit results. All commands should exit zero; negative cases are passing assertions. No phone QR scan, real tunnel, terminal screenshot, Alpine or systemd result is required or claimed for this library/file-output milestone. #### Progress - [x] Description prepared against accepted M1–M5 code and current documents. - [x] 2026-10-20T23:12:02+03:00 execution preparation: verified active coordinator `gpt-6-sol`/`medium` in this session's latest turn context and project worker roles `gpt-6-luna`/`high`; confirmed M1–M5 accepted, read required specification, ADRs and supporting documents, and inspected the clean source baseline. The pre-existing expanded M6 plan edit in this worktree is preserved. - [x] M6.1 — implementer delivered bounded, zeroizing payload rendering in `src/delivery.rs`, library export and `docs/DELIVERY.md`, with new-client and authenticated-import paths. Independent R1 found an unchecked caller-supplied server identity; repairer moved derivation to the saved `[Interface] PrivateKey` boundary and added malformed/missing/ambiguous cases. Independent re-review closed M6.1-R1. Final focused `cargo test --locked delivery::tests` passed 7 tests; fmt, Clippy and diff checks passed. Fixture evidence only; no file/QR or host behavior claimed. - [x] M6.2 — implementer added transactional dual-stack creation and encrypted vault staging, plus session-only first-file state in `src/app.rs` and a narrow encrypted-record context accessor in `src/vault.rs`. Independent R1 identified duplicate live authorizations; repairer made issuance single-flight with explicit retry states, and re-review closed it. The same reviewer identified stale standalone plaintext after app mutation; second repair moved output behind an app-owned `deliver_first_issue` closure holding the mutable session borrow, and re-review closed it. Final `cargo test --locked app::tests::` passed 41 synthetic tests; fmt, Clippy and diff checks passed. M6.3 still must connect actual file results to these states. - [x] M6.3 — implementer added secure file-output adapter in `src/delivery/files.rs`, app first-issue bridge, narrow SavedStore protected-path accessor and documentation. Initial focused `cargo test --locked delivery` passed 16 and `cargo test --locked app` passed 47; fmt, Clippy and diff checks passed. Fresh independent review found two actionable findings (M6.3-R1/R2 below). Three targeted repair rounds and independent re-review closed both under the documented single-administrator threat model; final focused `cargo test --locked delivery` passed 20 and file-module tests passed 12; fmt, Clippy and diff checks passed. No production filesystem behavior is claimed. - [x] M6.4 — implementer added approved `qrcode` 0.14.1 with default features disabled, bounded/zeroizing operation-local matrix representation, file-first optional QR integration and focused tests. Initial `cargo test --locked delivery` passed 22 and `cargo test --locked app` passed 51; fmt, Clippy and diff checks passed. Independent R1 found a public QR-only encoder path; repairer made it crate-private with a compile-fail doctest, and independent re-review closed the finding. Final focused delivery (22) and doctests (2) passed. No phone scan or terminal rendering is claimed. - [x] M6.5 — implementer added fresh password-per-action reissue from installed vault bytes, validated authenticated key-only/imported provenance, a live-generation activation warning, five public synthetic lifecycle tests in `tests/client_delivery.rs`, and README/DELIVERY guidance. Initial focused integration (5), delivery (22), app (51) and partial-provenance regression (1) passed with fmt, Clippy and diff checks. Fresh independent review found first-issue outputs omitted the activation warning. Repairer added it to file-only, file+QR and definite-publication/bookkeeping-uncertain results; re-review found uncertain file publication still omitted it, so a second repair added warning to failed outcomes and injected file-only/QR uncertainty tests. Independent re-review closed the finding. At M6.5 closure, `cargo test --locked app::` passed 50 and `--test client_delivery` passed 5; fmt, Clippy and diff checks passed. Integrated gates ran later in M6.7. - [x] M6.6 — a fresh independent integrated reviewer inspected M6-A1–A11 and found three actionable issues: secret-bearing assertion diagnostics, optional imported server-key binding in the public renderer, and unreported temporary-file cleanup failure. Repairer fixed all three; independent re-review closed them with no new actionable issue. The reviewer found M6-A1–A10 supported by scoped synthetic evidence and left M6-A11 pending coordinator integrated gates. - [x] M6.7 — coordinator ran every planned command against the integrated revision, verified all exit zero and nonempty focused test filters, checked changed files and reviewer evidence, and accepted M6-A1–A11 for the explicitly scoped fixture/file-output milestone. No real WireGuard, systemd, terminal or phone scan is claimed. #### Review and repair M6.1-R1 (P2, `src/delivery.rs`): the initial renderers accepted a caller-supplied server public key without proving it came from the saved server configuration; the initial valid-looking test even used a different key. Repairer made both renderers derive it from exactly one valid saved `[Interface] PrivateKey`, reject missing/duplicate/malformed sources with a redacted error, and compare imported retained identity to that key. Synthetic regression tests passed (7 focused); the independent reviewer re-reviewed and closed the finding. M6.2-R1 (P2, `src/app.rs`): two outstanding first-issue capabilities could both expose plaintext before either was consumed. Repairer made authorization single-flight/token-bound and added explicit pre-publication versus uncertain- publication retry handling. The independent reviewer closed this part after focused tests. M6.2-R2 (P2, `src/app.rs`): invalidating a standalone token after deletion or replacement did not revoke a caller-held plaintext payload. Repairer removed that accessor and introduced an app-owned publication closure that validates saved peer/record context immediately before the file writer runs while holding `&mut Application`; stale deletion/replacement tests show the closure is not invoked. Independent re-review closed the finding. The real file adapter is still M6.3 work, not evidence from these synthetic closures. M6.3-R1 (P2, `src/delivery/files.rs`): a confirmed overwrite rechecked the destination inode before `renameat`, but another entry could replace it between the check and publication and then be overwritten without confirmation. The independent reviewer supplied a deterministic `Checkpoint::Publish` substitution reproducer. Repairer changed confirmed replacement to atomic `RENAME_EXCHANGE`, checks the displaced inode and rolls back without deleting unexpected entries; rollback identity and parent sync failures report uncertainty. A later re-review found the normal-success temp-name unlink still vulnerable to a different local writer. Final repair requires a selected parent owned by root/current effective user without group/world write, checks existing ancestors, and uses an exclusive 0600 temporary name with 128 bits of OS randomness. Independent re-review closed R1 under the single-administrator threat model. Same-privilege concurrent name changes remain explicitly outside scope; temporary 0600 output retained after a rollback is documented for manual cleanup rather than risking deletion of an unconfirmed entry. M6.3-R2 (P2, `src/app.rs`): after a definite file publication, a fallible post-publication saved-state read/mark could return an undifferentiated app error, concealing that a plaintext file was already created. The independent reviewer requested an explicit published-but-bookkeeping-uncertain result and fault test. Repairer added `PublishedButBookkeepingUncertain` with definite file-success evidence and invalidated the session-held first-issue key on bookkeeping failure. The independent reviewer closed R2 after a synthetic post-publication saved-store failure test. No password-free retry is authorized by that result. M6.4-R1 (P2, `src/delivery/qr.rs`): a public QR encoder allowed a library caller to render a payload and generate a QR without the mandatory file action. Repairer restricted the encoder and module to crate visibility, kept public output/error value types for the guarded app result, and added a compile-fail doctest. Independent re-review closed the finding after file-first and QR-failure tests. QR scanning and terminal rendering remain unverified/future. M6.5-R1 (P2, `src/app.rs`): initial file-only and file-plus-QR delivery did not report whether the saved client had been verified active. Repairer bound an activation warning to the exact installed generation and the live verified saved/applied snapshot, never the historical timestamp. Re-review found the possibly-published parent-sync failure still lost the warning; second repair carried it in the failed outcome and tested uncertain publication for both variants with QR skipped. Independent re-review closed R1. All evidence is synthetic/fault-injected, not live VPN or device compatibility. M6.6-R1 (P2, `src/app.rs` tests): `assert_eq!` on saved and rendered client configuration bytes could print a private key if a test failed. Repairer changed those to boolean assertions and audited related tests. Independent integrated re-review closed this secret-diagnostic finding. M6.6-R2 (P2, `src/delivery.rs`/`src/vault.rs`): the public imported renderer accepted a missing saved-server binding from a mutable retrieved record. Repairer made retained fields private/read-only, required imported provenance and an exact present saved-server binding, and added missing/stale binding tests. Independent integrated re-review closed it. M6.6-R3 (P2, `src/delivery/files.rs`/`src/app.rs`): ignored temp-file cleanup errors could leave a plaintext 0600 staging file while reporting an ordinary pre-publication failure. Repairer added a distinct `ManualCleanupRequired` result, retry gating until explicit cleanup acknowledgement, documentation and an injected cleanup-failure test confirming no selected-file publication. Independent integrated re-review closed it. The user may still need to inspect and remove a retained staging file; the application does not silently erase an uncertain entry. All actionable M6 task-local and integrated review findings are closed after repair and independent re-review. The integrated reviewer found M6-A1–A10 supported by synthetic/fault-injected evidence; M6-A11 then passed the coordinator's final integrated gates. No password check was weakened and no preservation test was disabled. #### Evidence and handoff Initial planning inspected current Application creation/Commit/discard/restart interfaces, VaultBundle key-only encryption and password-authorized retained fields, current Cargo dependencies, M5 acceptance record and runtime documentation, plus product specification/secret-storage ADR. M5 records scoped fixture acceptance and passing integrated gates with independent re-review; real kernel/systemd checks remain M8/M9. That planning-only turn did not rerun application tests, add crates/source, generate private keys/output/QR, dispatch agents or contact host runtime/services. Final M6.7 validation on the integrated revision (all exit 0): `cargo test --locked delivery` (23 matching tests), `cargo test --locked --test client_delivery` (5), `cargo test --locked --test runtime_restart` (2), `cargo test --locked --test commit_restore` (9), `cargo test --locked --test vault_import` (5), `cargo test --locked --test config_preservation` (9), `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked` (205 library unit, 30 integration and two doctests passed; binary tests 0), `cargo build --locked --release`, release `--help` and `--version`, and `git diff --check`. Release `--help` correctly states the management interface is not implemented. Checks used synthetic keys, temporary stores/output directories and fake adapters without root, Docker, host WireGuard/system bus, production paths or a terminal. The approved `qrcode` 0.14.1 was added with default features disabled; no other direct dependency was introduced. No phone QR scan, live tunnel, Alpine test or disposable systemd VM result is claimed. Those validations remain future M7–M9 work, not passing M6 evidence. Next action: stop after accepted M6. M7 may begin only on a separate request; do not create a Git commit or deploy. #### Decisions and discoveries New vault records contain a key but no template parameters, while imported records retain supported connection fields. Delivery needs an explicit provenance and precedence rule rather than assuming both are interchangeable. Creation must retain ephemeral private material separately from password-only vault retrieval; otherwise first delivery without a password is impossible. That ephemeral material cannot be persisted or reconstructed after session loss. M5 applies saved state and M6 exports saved state; neither should call the other implicitly. File and QR are two representations of one authorized delivery action, with file success as the first-issue completion boundary. M7 supplies the terminal interaction around these library contracts. #### Outcome M6.1–M6.7 are complete. Accepted for the milestone's explicitly scoped synthetic/file-output checks after independent integrated review, repair, re-review and all planned coordinator gates. The TUI, phone compatibility, real WireGuard and actual systemd/D-Bus behavior are not claimed; M7–M9 own their later acceptance. No Git commit, deployment or production access occurred. Stop after M6; do not start M7 without a separate request. ### Milestone 7 — Complete terminal management workflow Status: accepted (2026-10-21T23:23:41+03:00; independent R2 closed the finding and coordinator gates passed for synthetic UI/PTY scope). Prerequisites: milestones 1–6 accepted; verified coordinator/worker model roles, pinned native Rust environment and registry access or cached compatible approved `ratatui`/`crossterm` dependencies. Default validation runs without root, host WireGuard, host D-Bus or production files. Terminal lifecycle evidence requires a disposable pseudo-terminal or an isolated interactive terminal with synthetic adapters. Missing terminal evidence is an unmet M7 gate, not a passing test. #### Outcome and scope Replace the management-startup placeholder with a usable native Rust TUI around M1–M6 application operations. The administrator can inspect clients/anomalies and current statistics, initialize the vault/import existing files, prepare creation, rename and deletion drafts, review and Commit, explicitly restart, deliver a file and optional QR, create/list backups, preview/confirm restoration, and exit with the specified draft warning. The UI keeps draft, saved and applied state distinct. Read SPEC.md in full, CONTEXT.md, ADRs 0001–0003, IMPLEMENTATION.md, PROJECT_LAYOUT.md, DEVELOPMENT.md (especially terminal behavior), DEPENDENCIES.md, TESTING.md, ENVIRONMENT.md, CONFIG_FORMAT.md, VAULT_FORMAT.md, PERSISTENCE.md, BACKUP_FORMAT.md, RUNTIME.md and DELIVERY.md before editing their respective boundaries. Preserve their versioned formats and accepted M1–M6 contracts. Out of scope: new VPN settings, IP/key editing operations, server bootstrap, remote/multiple-interface management, action/service logs, traffic history, automatic save/apply/rollback, concurrent-editor reconciliation, container/VM harness implementation and production testing. Real kernel and systemd evidence remain mandatory later at M8/M9; M7 acceptance covers UI and synthetic integration. Do not redesign accepted library behavior in key handlers. #### Context and implementation steps Actual preparation state and integration constraints: - `src/main.rs` and `src/startup.rs` still return `ManagementUnavailable` for root management; help/version and lazy root checking already work. `src/lib.rs` exports the application modules; no `src/tui.rs` or TUI dependencies exist yet. - `Application` in `src/app.rs` owns document/domain/vault drafts and session-only first-issue material. `SavedStore::read_saved` performs journal recovery; `Application::open` accepts caller-provided `ClientStates` and marks runtime unknown. Introduce a narrow, tested application loading/projection boundary where needed so startup derives states from authoritative document bytes, rather than rebuilding client rules in the binary or supplying an empty set. - The narrow `Snapshot` cannot represent every inspected peer. List document anomalies and unsupported/ambiguous entries explicitly without silently dropping them. Disable actions requiring an unambiguous public key/supported projection; do not choose a duplicate alias or truncate routes to enable them. - Existing operations include `prepare_new_client`, `rename_client`, `delete_client`, `discard_draft`, `commit`, initial vault/setup preparation, backup list/create, restore preview/confirmation, restart preview/choice, first-file delivery and password-per-action `reissue_client_file`. Reuse these boundaries. Add only missing application-facing projections/adapters, with focused tests; ordinary UI state never contains server bytes or private keys. - `vault::source` provides bounded configured-directory scanning and setup; `vault::import` provides reconciliation/key verification. Explicit selected-file import needs a bounded adapter/application entry point if the existing API cannot express it. Keep originals untouched, imported secrets encrypted in draft, anomalies visible and conflicting reissue blocked. Do not invent an automatic conflict repair or an unlock-by-clearing-a-flag UI. - Runtime transport and restart adapters are synchronous bounded operations; terminal code must schedule them off the input/render loop. A read-only refresh may report technical correspondence, but must not mark applied, persist a verification timestamp, release addresses or suppress M6 activation warnings. - `Application::confirm_restore` replaces saved settings and session state. Refresh every settings-dependent view/operation from the restored generation; discard stale previews, output confirmations and queued statistics. Preserve fixed installation paths/interface constraints and M6 invalidation semantics. One implementation writer at a time; coordinator owns PLANS.md. Establish the application/worker interface before terminal wiring. Add modules only as needed. | Task | Owner | Files and deliverable | | --- | --- | --- | | M7.1 | implementer | `src/app.rs`, `src/startup.rs`, relevant config/persistence/vault adapters and tests: safe session loading, public display projections and missing selected-file import/application boundaries | | M7.2 | implementer | `src/tui.rs`, `src/tui/state.rs`, `src/tui/events.rs` as needed: secret-free screen/navigation state, typed commands/results, serialized application worker and bounded statistics scheduling | | M7.3 | implementer | `Cargo.toml`, `Cargo.lock`, `src/main.rs`, `src/lib.rs`, `src/startup.rs`, `src/tui/terminal.rs`, `src/tui/render.rs`: approved terminal dependencies, interactive startup, guarded lifecycle, responsive layout and client/statistics screens | | M7.4 | implementer | TUI forms/dialogs and narrow application additions: vault setup/import, creation/manual addresses, aliases/deletion, draft review/Commit, explicit restart and cancellation flows | | M7.5 | implementer | TUI delivery/backup screens: masked authorization, destination/overwrite/retry, file plus optional QR, manual backups, archive preview and confirmed restore | | M7.6 | implementer | `tests/tui_workflow.rs`, focused TUI/terminal tests, `README.md`, affected supporting docs: synthetic end-to-end workflows, terminal evidence and accurate operating instructions | | M7.7 | reviewer, then repairer | Independent integrated review of M7 scope and regressions; bounded finding repairs and fresh re-review evidence | | M7.8 | coordinator | Integrated gates, evidence ledger, scoped acceptance and M8 handoff in PLANS.md | M7.1 loads executable-relative settings only after management privilege checks, uses the safe saved-store/recovery path and derives the initial client view from the server document. Do not silently replace corrupt state/vault with empty data. Initial vault creation asks for a masked password and confirmation, checks existing files at the earliest opportunity, presents reconciliation results and stages changes through Commit. Cancellation does not install a vault or rewrite source files. Explicit import and managed metadata additions remain draft operations; unknown creation dates remain unknown. Temporary fallback labels are filenames or shortened public keys, never a new alias registry. M7.2 keeps one owner of mutable application/session state. Use standard-library threads/channels where sufficient; serialize mutations and reject duplicate submission while busy. Specify bounded/coalesced event queues, refresh interval, read timeout, stale-result generation checks and worker shutdown. Do not allow old results to overwrite a newer Commit/restore/restart view or recreate delivery authorization. Progress/error/cancel navigation remains responsive while work is pending. Cancellation of a dialog is distinct from aborting submitted work: do not report an operation cancelled while it may still save files or restart. Exit during submitted work must wait for an honest outcome with progress; no unbounded join or silent detachment of protected mutations. An already submitted systemd job can outlive its timeout; show the existing report's uncertainty. M7.3 checks terminal suitability before raw/alternate-screen entry and before saved-store recovery or setup mutations. Help/version still require neither root, settings, terminal nor vault. Non-TTY/unsupported startup yields plain stderr and a nonzero result without escape sequences. A scoped terminal guard restores raw mode, alternate screen and cursor after normal exit, partial initialization, render/input errors and handled interruption, including best-effort panic cleanup. Avoid secret-bearing panic diagnostics and competing writes to the active screen. List alias/fallback label, shortened key, IPv4/IPv6, draft/saved differences, unapplied/unknown runtime status and anomalies using text as well as color. Keep selection by full public key across refresh/sort and show that key in client confirmations. Malformed records may use document location for inspection, never as an alternate ambiguous mutation identity. Show last-handshake time and current interface RX/TX counters; distinguish never, zero and unavailable/stale readings. Missing runtime is nonfatal. Support keyboard-only use, visible focus, help, scrolling, empty/error states and resizing to a terminal too small for content. Escape all untrusted text, including imported filenames, aliases and errors. M7.4 offers dual-stack default/manual allocation and reports validation/exhaustion without losing prior draft. Renames may repeat. Deletion warns that saved removal can leave runtime access active and addresses reserved. A semantic draft preview shows client changes without raw configuration diffs (which could expose secrets). Commit has no runtime calls. With a draft, restart offers exactly “Discard draft and restart” or “Cancel”; cancellation preserves draft and first-issue eligibility. Confirmed restart uses the application's preview and full backup/job/verification sequence. Exit warns that draft will be discarded, offers cancellation and never implicitly commits. Failed backup, Commit, restart or recovery displays the actual stage/result and leaves state consistent with the existing application contract. M7.5 permits first delivery only after Commit in the same creation session; reissue requests a fresh password for the selected client every time. Passwords and QR matrices have short-lived dedicated owners, outside ordinary view/debug/ snapshot state. Cancel/close clears them; do not retain an unlocked vault. The selected destination and explicit overwrite confirmation use M6 capabilities, not alias-derived paths. Show retry, publication uncertainty, manual-cleanup and file-success/QR-failure outcomes accurately. Changing the directory can retry a failed eligible first file. QR is additional to successful mandatory file output; render its quiet zone and modules without clipping, show a resize/file fallback when it cannot fit, and clear the disclosure screen on close. Show the M6 activation warning without inferring activation from historical verification. Backup rows show archive identity, UTC creation time, reason and historical verified status without labelling the newest archive as working. Restoration previews clients/settings changes safely, identifies the exact archive, warns of full saved-state replacement and draft loss, and requires confirmation. Cancel has no replacement/precautionary-backup side effects. Confirmed restore needs no password, makes its precautionary backup and never restarts; refresh the session view and report saved/unapplied state. No archive cleanup or merge is added. #### Acceptance and validation - [x] M7-A1: production management startup reaches the TUI using executable-relative settings and safe recovery; injected non-root/non-TTY/invalid startup checks prove early failure. Help/version remain independent of privileged data. - [x] M7-A2: keyboard list/details/forms, focus/help, scrolling, small/empty/error screens and resize are usable. Duplicate aliases and refresh cannot retarget actions. All authoritative peers/anomalies remain visible, including unsupported entries; untrusted terminal controls are escaped. - [x] M7-A3: synthetic setup/import covers server-only, file-only, matching, mismatched and invalid files, preserves original bytes, encrypts valid secrets, blocks conflicting reissue and stages metadata without fabricating dates. - [x] M7-A4: create/rename/delete, semantic preview, cancel/discard and Commit preserve draft/saved/applied boundaries. Allocation includes manual/invalid/ exhausted cases. Backup failure stops protected mutation; Commit/restore invoke no service/runtime mutation even through the UI dispatch path. - [x] M7-A5: restart with draft exposes the specified two choices; Cancel preserves draft. Fake service/runtime outcomes prove successful verification releases reservations, while job failure, timeout, missing/mismatching peers or failed verification persistence do not. No implicit retry or rollback occurs. - [x] M7-A6: first-file success/retry/change-directory, overwrite refusal/approval, uncertain publication, QR failure after file success, reissue/password errors, session exit and restore invalidation follow M6. No original-file password bypass, secret snapshot or QR-only disclosure path is introduced. - [x] M7-A7: backup list/manual backup, exact-target preview/cancel/restore, precautionary-backup failure and restored operational settings are covered. Restoration fully replaces saved state without password or implicit restart. - [x] M7-A8: delayed/failing fake workers demonstrate responsive input, duplicate prevention, bounded/coalesced refresh and stale-result rejection. Statistics never imply online status or advance applied state; exit/shutdown is bounded and reports submitted-operation uncertainty accurately. - [x] M7-A9: synthetic terminal lifecycle checks demonstrate cleanup on normal exit, partial setup failure, render/input error, handled interruption and panic where feasible. Record exact PTY/manual procedure and outcomes; default tests never acquire production adapters or require root. Explicit QR display retains a complete quiet zone, is not clipped and clears on dismissal. - [x] M7-A10: independent reviewer has no unresolved actionable findings; all integrated Rust gates pass and documentation describes actual workflows and remaining M8/M9 limits. No real-system result is claimed from UI fixtures. Planned commands (repository root; pinned environment verified first): `rustc -Vv`, `cargo -V`, `rustfmt --version`, `cargo clippy --version`; `cargo test --locked --test tui_workflow` after M7.6 creates that target; `cargo test --locked --test client_delivery`, `cargo test --locked --test runtime_restart`, `cargo test --locked --test commit_restore`, `cargo test --locked --test vault_import`, `cargo test --locked --test config_preservation`; `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked`, `cargo build --locked --release`, `./target/release/wireguard-admin --help`, `./target/release/wireguard-admin --version`, and `git diff --check`. Expected: successful checks, default tests without host access, accurate help and no secret output. Select compatible minimal TUI dependency versions from official crate documentation at execution time; retain Cargo.lock, avoid unrelated updates. Record focused terminal test names and the exact isolated smoke procedure once implemented; never launch root management against the development host for proof. Phone/decoder QR interoperability must be recorded separately if demonstrated; no scan success is presumed from matrix/render tests or required via production. #### Progress - [x] M7.1 — implementer `/root/m7_1`: safe session bootstrap, secret-free all-peer projection and bounded selected-file import integrated; review remains at M7.7. - [x] M7.2 — implementer `/root/m7_1`: secret-free state/events and serialized application worker integrated; final independent review remains at M7.7. - [x] M7.3 — implementer `/root/m7_1`: terminal adapter, startup and list/statistics rendering integrated; synthetic PTY and independent review subsequently passed. - [x] M7.4 — implementer `/root/m7_1`: setup/import, create/rename/delete, draft/Commit and explicit restart forms integrated. - [x] M7.5 — implementer `/root/m7_1`: delivery, backups and previewed restore workflows integrated. - [x] M7.6 — implementer `/root/m7_1`: synthetic workflow/terminal evidence and documentation integrated. - [x] M7.7 — independent R1 review, targeted repair by `/root/m7_repair_r1`, and R2 re-review closed M7-R1-01; no actionable findings remain. - [x] M7.8 — coordinator reran all planned integrated gates, recorded scoped evidence and accepted M7-A1–A10. #### Review and repair | Finding | Iteration | Location and impact | Owner | Resolution and evidence | State | | --- | --- | --- | --- | --- | --- | | M7-R1-01 (P2) | R1/R2 | `src/tui/render.rs` client list omitted per-row draft/saved change and anomaly text required by SPEC/M7-A2. | `/root/m7_repair_r1` | Added row-leading state/anomaly markers, draft-removal rows, 34-column TestBackend and control-escaping assertions; independent R2 confirmed closure. | closed | R1 separately noted coverage limits for M7-A5 confirmed restart through the UI executor and M7-A9 terminal failure/interruption cases. The reported M7-A4 delete/discard coverage gap is disputed by `tests/tui_workflow.rs` worker-path assertions for `DeleteClient` and `DiscardDraft`; ask R2 to verify those lines. Repairer reported M7-R1-01 fixed and added synthetic PTY handled Ctrl-C cleanup evidence. Follow-up added five isolated PTY child cases: failure after raw-mode setup, failure after alternate-screen entry, render I/O error, input I/O error and active-mode panic. Assertions check raw mode off, matching restoration sequences and a generic panic diagnostic without payload. Focused lifecycle PTY test, focused TUI tests (25), workflow tests (6), fmt, Clippy and diff checks passed in the repairer run; `docs/TESTING.md` records procedure and limits. Independent R2 review and coordinator rerun remain pending. M7-A5 UI executor fake-outcome coverage remains a separate acceptance question. #### Evidence and handoff 2026-10-21 execution preparation: confirmed M1–M6 accepted in their milestone records and register; preserved the pre-existing M7 expansion in this worktree. Matched `CODEX_SESSION_ID` to this session's local rollout record and verified its latest `turn_context` is `gpt-6-sol`/`medium`; all configured worker roles are `gpt-6-luna`/`high`, and M7.1 was dispatched through the harness with those exact overrides. Native rustc/Cargo 1.97.1, rustfmt 1.9.0 and Clippy 0.1.97 matched the pin. Inspected the actual source/startup/application/store/import interfaces. No M7 acceptance gate is claimed yet. M7.1 implementation changed `src/app.rs`, `src/startup.rs` and `src/vault/source.rs`; no dependency or versioned format changed. The worker reported `cargo test --locked` (209 library unit plus integration/doc suites), Clippy, fmt and diff checks passing. Coordinator inspected the changed-file list and public interfaces, ran `git diff --check` (pass), and independently ran `cargo test --locked --lib selected_file` (4 passed). M7.1 supplies `open_from_saved_store`, `display_projection`, `open_management_session` and `import_selected_file`. Independent integrated review is still pending. M7.2 implementation added `src/tui.rs`, `src/tui/state.rs`, `src/tui/events.rs` and a library export. The worker reported focused `cargo test --locked tui::` (9 passed), fmt, Clippy and diff checks passing. Coordinator inspection found a blocking `send` in the first shutdown fallback; the implementer replaced it with an atomic stop request and nonblocking signal, and added a full-queue/delayed-read regression test. This repair was checked before progressing. The operation protocol currently covers draft mutation, Commit, backups and selected import; later tasks extend it for restart, restore and delivery. The shutdown handle must remain live while it reports `Pending`. M7.3 implementation added the guarded Crossterm/Ratatui terminal path, keyboard list/details/help and bounded read-only runtime sampling. Approved direct dependencies `ratatui` 0.30.2 and `crossterm` 0.29.0 were added; their resolution required advancing the already approved `time` pin from 0.3.44 to 0.3.47. The worker reported `cargo test --locked tui::` (15 passed), fmt, Clippy and diff checks passing, plus successful help/version smoke checks. Coordinator reran focused TUI tests (13 passed before the final lifecycle repair) and inspected startup, dependency and terminal changes. Inspection found terminal I/O error paths could drop an active worker and a panic hook was left installed; the implementer repaired both and added synthetic tests for delayed operation reporting and hook restoration. Actual PTY lifecycle evidence and independent integrated review remain required. M7.4 implementation added `src/tui/forms.rs` and extended terminal, render/state/events, with a narrow draft-only peer selection fix in `src/app.rs`. The worker reported `cargo test --locked tui::` (18 passed), fmt, Clippy and diff checks passing. Coordinator reran the focused TUI tests (18 passed) and inspected the secret owner, forms and command dispatch. Current focused tests cover masked setup input, manual-address validation, full-key rename confirmation and draft-restart choice; M7.6 must add broader synthetic behavior tests for Commit/restart failure and cancellation paths before acceptance. M7.5 implementation extended worker-owned prepared destinations and validated restore previews, password-per-action reissue, file-plus-optional-QR disclosure, backup browsing and exact-archive restoration. The worker reported `cargo test --locked tui::` (22 passed), fmt, Clippy and diff checks passing; coordinator reran focused TUI tests (22 passed) and inspected continuation and result handling. Synthetic tests currently cover generation-bound prepared actions, password masking, retry confirmation and QR fit. Actual end-to-end publication/restore failure behavior and terminal disclosure lifecycle remain M7.6 evidence needs; no real-system behavior is claimed. M7.6 added `tests/tui_workflow.rs` (6 tests), synthetic terminal injection and restart-result rendering test, and updated README/TESTING/PROJECT_LAYOUT. The worker reported `cargo test --locked --test tui_workflow` (6 passed), focused restart rendering (pass), fmt, Clippy, full `cargo test --locked` (233 unit, 36 integration, 2 doctests), release build and diff check passing. The PTY smoke ran under `/usr/bin/script` with a synthetic application and fake unavailable statistics; it exercised 80×24 → 40×8 → 80×24 resize, q exit and alternate-screen/cursor restoration. No host WireGuard/systemd or production path was accessed. The UI test constructs restart outcomes for display and cancel; actual fake service/runtime outcomes remain application-library evidence. QR file-success/encoder-failure is likewise covered at the M6 app boundary, while M7 proves QR ownership, dismissal and complete fit. No phone or external decoder interoperability is claimed. Coordinator gate rerun and independent review are pending. M7.7 R1 was dispatched to a fresh read-only reviewer, separate from the implementer, with all M7-A1–A10 criteria and actual changed/untracked paths. The coordinator updated AGENTS.md's stale introductory implementation-status sentence without changing behavior rules. Review findings are pending. R1 reviewer `/root/m7_integrated_review_r1` inspected actual source, untracked TUI files and docs; `cargo test --locked --test tui_workflow` passed 6/6 and `git diff --check` passed. It identified M7-R1-01 above. It did not claim real WireGuard/systemd evidence. Repair and re-review are next. R2 independent re-review inspected the renderer repair and synthetic terminal refactor, ran the focused row test, five-case lifecycle PTY test, `cargo test --locked --test tui_workflow` (6/6) and `git diff --check` (all pass), and closed M7-R1-01 with no new actionable finding. R2 withdrew its earlier A4 gap after verifying worker `DeleteClient`/`DiscardDraft` assertions. It found the existing application fake service/runtime restart tests plus TUI preview, choice/cancel and result-display tests sufficient for M7-A5's synthetic scope; these do not prove the real systemd adapter. Coordinator final integrated validation on the reviewed revision (all exit 0): `rustc -Vv` and `cargo -V` reported 1.97.1; `rustfmt --version` reported 1.9.0-stable; `cargo clippy --version` reported 0.1.97. `cargo test --locked --test tui_workflow` passed 6/6, including the synthetic PTY smoke; `cargo test --locked --test client_delivery` 5/5, `runtime_restart` 2/2, `commit_restore` 9/9, `vault_import` 5/5 and `config_preservation` 9/9. `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked`, `cargo build --locked --release`, release `--help`, release `--version` and `git diff --check` all passed. The coordinator independently reran the focused five-case lifecycle PTY test (1/1), and checked 29 local Markdown links in the changed guidance/plan files with none missing. Help and version printed only their expected public text. Actual changed and untracked source, test, manifest, lockfile and documentation paths were inspected; no unapproved direct crate, production path, live service or Git commit was introduced. Acceptance mapping: A1 uses injected root/TTY/settings/recovery tests and the compiled production startup path; A2 uses all-peer projection, rendering, selection and 34-column anomaly/state tests; A3 uses synthetic source reconciliation and M3 vault checks; A4 and A7 use worker workflow plus Commit/restore fixture tests; A5 uses TUI choice/cancel/result tests and M5 fake service/runtime cases; A6 uses worker delivery/reissue tests and M6 file/QR failure cases; A8 uses delayed worker/queue/generation tests; A9 uses isolated PTY normal/interrupt/resize/failure/panic cases and QR fit/dismissal; A10 uses closed R2 review and the coordinator gates above. All are scoped to synthetic fixtures, fake adapters and isolated PTY behavior. No Alpine real WireGuard, disposable-VM real systemd/D-Bus, external QR decoder or phone scan was performed; those results are not inferred from M7 acceptance. Planning only: inspected the approved specification, ADRs, supporting contracts, accepted M1–M6 records and current startup/application/store/import/delivery/runtime interfaces. No M7 code, dependency change, terminal session, Rust gate, Alpine or VM test was executed as part of this expansion. Planning validation passed: `git diff --check`; a local Markdown-link check found no missing PLANS.md targets; record-structure checks confirmed all eight template sections, tasks M7.1–M7.8, criteria M7-A1–M7-A10 and pending status. The milestone register now matches the accepted M5/M6 records. Rust rebuilds are not required for this documentation-only change. Historical planning handoff above preceded M7 execution. The next milestone is M8 Alpine Testcontainers integration, which requires a separate execution request and its disposable environment prerequisites. Do not create Git commits, deploy or test on production from this acceptance. #### Decisions and discoveries M7 completes terminal orchestration around accepted library contracts rather than adding a second persistence/runtime/delivery implementation. Session loading, selected-file import and safe public projections are explicit integration tasks, not assumed existing APIs. Runtime statistics/technical matches and historical verification remain distinct from a verified explicit restart. Terminal cleanup and responsiveness need their own evidence beyond pure render tests. No new product decision or specification change is introduced by this expansion. #### Outcome Accepted for the defined synthetic UI/PTY scope after independent R2 review and all planned coordinator gates. M8/M9 retain real WireGuard/systemd release gates. ### Milestone 8 — Alpine Testcontainers integration Status: accepted (2026-10-25T20:43:00+03:00; M8.1–M8.7 complete, independent review closed, scoped IPv4 Alpine gates passed). 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 - [x] 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. - [x] 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. - [x] 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. - [x] 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. - [x] 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. - [x] 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. - [x] 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. - [x] 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. - [x] M8-A9: secret-free diagnostics, private copied files, limits/deadlines and cleanup work on both success and failure; only test-owned resources are removed. - [x] 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. Executed integrated host and selected-daemon commands, from repository root; the exact Docker environment, image/driver commands, counts and durations are in [the M8.7 evidence](docs/evidence/milestone-8.md): ```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 ``` The exact pinned image-build and in-container driver-build commands, prerequisites, and output artifact path are in [TESTING.md](docs/TESTING.md). Both were executed; the test-only musl driver compiled and ran in Alpine. Build setup and cached dependency access are distinct from the locked host test execution. No unapproved direct dependency was added. Image identities, kernel/runtime/toolchain versions, target architecture, scenario names, outcomes, durations and counts are recorded in the evidence report. All required scenarios executed and passed; the ignored-test output explicitly reports 1/1 passing rather than treating a skipped test as success. Real systemd validation remains M9. #### Progress Current snapshot: 2026-10-25. This replaces the chronological execution notes; [the historical record](docs/history/milestone-8-before-workflow-cleanup.md) preserves previous attempts and their reported evidence. | Task | State | Result / next action | | ------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | M8.1 | complete | Reviewed host/container contract. | | M8.2 | complete | Pinned Alpine recipe, package closure and readiness verified. | | M8.3 | complete | Reviewed IPv4 prerequisite probe passed on the selected daemon. | | M8.4 | complete | Reviewed in-container Application driver and real Rustix smoke passed. | | M8.5 traffic | complete | Copied first-delivery file drives real IPv4 traffic; exact IPv4 routes, handshakes and counter deltas verified; independent findings closed. | | M8.5 deletion | complete | Saved deletion preserves access until explicit fixture Apply; subsequent deleted-client traffic fails and retained-client traffic succeeds; independent finding closed. | | M8.5 restore | complete | Two password-free restores, unchanged runtime before Apply, renewed IPv4 traffic and pending-address release after verified Apply passed on the selected daemon. Independent code review found no implementation defect; the plan-status finding closed on re-review. | | M8.5 import/reissue | complete | M8.5-IMP-01 closed on independent re-review; focused tests and fingerprinted pinned musl build passed. Selected-daemon suite passed 1/1 in 82.06s with cleanup, including reissued-file IPv4 traffic. | | M8.5 backup failure | complete | M8.5-BF-R1 closed on independent re-review. Typed backup errors, unchanged saved/runtime state, no fixture apply, retained reservation and reader errors passed on a known-source selected-daemon run (1/1, 81.45s). | | M8.6 | complete | R1 documentation, R2 IPv6 plan-gate and R3 stale milestone summary findings each closed on independent re-review. No code defect was found. | | M8.7 | complete | Cached image/readiness/packages, pinned musl rebuild, host gates, two fresh selected-daemon full runs and the documented unfiltered opt-in command passed on stable source inputs. Final independent review closed all findings; coordinator accepted M8-A1–A10 for the scoped IPv4 Alpine work. | #### Review and repair | Finding / work item | State | Disposition / next evidence | | --- | --- | --- | | M8.1-R1; M8.2-R1/R2 | closed | Independent re-review closed documentation/image findings. | | M8.3-R1–R7 | closed | Timeout/transport/error/name fixes reviewed; revised IPv4 probe passed. | | M8.4-R1–R4 | closed | Driver protocol, path confinement, host transport and cleanup findings independently closed; real smoke passed. | | M8.5-R1/R2 | closed | Delivered bytes consumed in client namespace; exact IPv4 route assertions; independent re-review closed both. | | M8.5-R3 | closed | Negative ping requires completed transmitted/zero-received evidence; Docker execution errors cannot pass; independent re-review closed it. | | M8.5 restore phase failure | closed | Fixture expected only the removed client's IPv4 reservation; Application correctly retained both saved addresses. Exact-set expectation now derives all removed-client addresses; focused regression and selected-daemon pass. [Diagnostic record](docs/diagnostics/milestone-8.md). | | M8.5-RESTORE-DOC-1 | closed | Independent reviewer found stale restore status/handoff; coordinator updated the snapshot, and independent re-review closed it. No code defect found. | | M8.5-IMP-01 | closed | P2: the Alpine driver had omitted invalid-file byte preservation. Repair checks all three sources before claiming unchanged originals; focused regression and fingerprinted selected-daemon rerun passed, and independent re-review closed it. | | M8.5 failure phase | closed | Fixture misread `delete_client`'s bool as peer deletion; it actually reports vault-record removal and the legacy baseline has no record. Repair checks draft peer absence; focused regression and known-source selected-daemon run passed; independent review found no further issue with this check. | | M8.5-BF-R1 | closed | P2: Commit-failure assertion formerly accepted any error. Repair requires `Err(AppError::Backup(_))`; focused regression and known-source selected-daemon run passed, and independent re-review closed it. | | M8.6-R1 | closed | P2: TESTING.md and evidence report had stale M8.5 pending claims. Repairer recorded completed first-delivery transfer and fingerprinted backup-failure evidence; independent re-review closed it. | | M8.6-R2 | closed | P2: General plan gate required IPv6 prerequisites despite M8's explicit IPv4-only scope. Coordinator scoped prerequisite language to the suite and retained production dual-stack requirement as unverified; independent re-review closed it. | | M8.6-R3 | closed | P2: Milestone status summary still reported active M8.5 restore repair. Coordinator updated it to M8.1–M8.5 complete with M8.6/M8.7 open; independent re-review closed it. | | M8.7-R1 | closed | P2: Reviewer saw a prior plan snapshot while final evidence was being entered. Task/handoff rows and outcome now record passing repeated runs and R3 closure; independent re-review closed it. | | M8.7-R2 | closed | P3: Validation passage still described implemented and executed commands as planned, and snapshot date was stale. Coordinator corrected the passage and date; independent re-review closed it. | Detailed closed-finding rationale and superseded attempts are in the historical record. New diagnostic attempts belong in `docs/diagnostics/`, not this table. #### Evidence and handoff Previously reported evidence, carried forward without rerunning during this 2026-10-24 documentation cleanup: | Scope | Result and limit | | --- | --- | | IPv4 prerequisite | Coordinator probe passed 1/1 (1.46s); isolated resources cleaned. | | M8.4 Application smoke | Coordinator musl rebuild and selected-daemon smoke passed 1/1 (4.05s); named resources absent afterward. | | M8.5 delivered-file traffic | Repaired real run passed 1/1 (14.23s); host support tests passed 21 with privileged test ignored; independent re-review closed R1/R2. | | M8.5 deletion | Repaired coordinator real run passed 1/1 (42.96s); host support tests passed 23 with privileged test ignored; cleanup verified; independent re-review closed R3. | | M8.5 restore | Worker reported fmt, focused/full locked tests, all-target Clippy, release build and diff check passing before the final reservation-helper edit; focused helper regression and pinned musl rebuild passed afterward. Coordinator exact selected-daemon run passed 1/1 (72.66s), with named resources absent. Independent review found no code issue; later integrated gates are recorded in the M8.7 row. | | M8.5 import/reissue | Worker reported fmt, Clippy and focused import tests after its final assertion edit; full locked tests and release build passed before it. After M8.5-IMP-01 repair, focused regressions and Clippy passed. Coordinator pinned musl build and selected-daemon run passed 1/1 (82.06s), with stable source fingerprint and named resources absent. [Reproducible evidence](docs/evidence/milestone-8.md). | | M8.5 backup failure | Worker reported fmt, Clippy, full locked tests/release before the final typed-error repair; focused regression, fmt and Clippy passed afterward. Coordinator pinned musl build and selected-daemon run passed 1/1 (81.45s), source-input SHA256 `b28dc6e4cb92aa6edf384d966ba5952a8d6d0efe4c22d6a804b67fb2c0297da2` stable before build/after run, driver SHA256 `ad45dc3f6b0987b075ac522e171c82c6651124c3d01c65c14df90595fe552815`, named resources absent. Independent re-review closed BF-R1. | | M8.7 integrated gates | On the same source fingerprint, `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked`, `cargo build --locked --release`, release help/version, and `git diff --check` exited 0. Cached Alpine image ID `sha256:f41cd6b848d8280d4d86bcaceea962e1b3e5a73e9496c6f9d7929470d0e874d5` passed readiness; 33 constrained packages recorded. Pinned musl rebuild produced the same driver SHA256. Exact selected-daemon scenario passed 1/1 twice (80.77s each); documented unfiltered `--ignored` command passed 1/1 (81.87s). Named containers/networks absent after each. [Reproducible evidence](docs/evidence/milestone-8.md); prerequisite failures in [diagnostics](docs/diagnostics/milestone-8.md). Final independent review closed M8.7-R1/R2 and found M8-A1–A10 supported. | Exact earlier commands, environment/build identities and intermediate results remain in the historical record. For new evidence use the report format in [execution records](docs/EXECUTION_RECORDS.md), including the tested revision or working-tree identity. Earlier runs lack a complete source fingerprint; do not infer that they validate subsequent edits. Final gates must run on the integrated revision. Do not rerun unrelated gates merely to maintain this document. Next action: Milestone 8 is accepted. Stop here; Milestone 9 remains pending and requires a disposable systemd VM before its real D-Bus acceptance work. #### Decisions and discoveries - The user excluded IPv6 capability and traffic testing on 2026-10-22; production dual-stack requirements are unchanged. Real IPv6 remains unverified. - Use the real bounded Rustix reader and test-only fixture Apply. Alpine cannot validate the private zbus/systemd adapter; real systemd acceptance remains M9. - Build the musl test driver in the pinned Alpine builder; the host lacks the musl prerequisites. Cached image/build layers may be reused; secret-bearing fixture state must be fresh. Build identities are preserved in the history. - Full fixture `wg setconf` resets learned peer state. Traffic assertions reconnect retained clients and require fresh observations; peer presence alone is not continuity evidence. #### Outcome M8 accepted for its user-scoped IPv4 Alpine Testcontainers work after independent review closed all actionable findings and the host and repeated real-container gates passed on one source fingerprint. M8-A1–A10 are satisfied within that scope. IPv6 capability and traffic were excluded by user direction; real systemd/D-Bus behavior remains unverified and belongs to M9. Milestone 9 was later retired by user direction; this historical M8 acceptance does not establish real systemd/D-Bus behavior. ### Milestone 9 — Real systemd acceptance and release readiness Status: retired (2026-10-26T20:52:01Z; user judged the milestone goal unnecessary over-engineering and removed it from the development sequence). No M9 acceptance result exists; real systemd/D-Bus behavior remains unverified. Prerequisites: milestones 1–8 accepted; explicitly selected disposable Linux VM with its own kernel, systemd as PID 1, system D-Bus, WireGuard and synthetic data; compatible pinned-toolchain artifacts and a bounded management/console channel. Missing VM access or required capabilities is a blocker, not passing acceptance. #### Outcome and scope Verify the real production `Application::restart` -> private zbus systemd job adapter -> real Rustix WireGuard observation -> journaled verification sequence. Complete operating/recovery instructions, an acceptance evidence map, and a reproducible release candidate. Acceptance authorizes no production experiment, installation, publication, deployment, Git commit or tag. Read SPEC.md, IMPLEMENTATION.md, CONTEXT.md, ADRs 0001–0003, RUNTIME.md, PERSISTENCE.md, BACKUP_FORMAT.md, DELIVERY.md, ENVIRONMENT.md, TESTING.md, DEPENDENCIES.md and EXECUTION_RECORDS.md. Read only the relevant final M8 evidence and focused diagnostics when needed; do not reconstruct every historical attempt. M8 was accepted for user-authorized IPv4-only Alpine coverage. Preserve that acceptance and do not silently reintroduce excluded IPv6 tests as a prerequisite for the VM. Production dual-stack requirements remain unchanged, so the final acceptance matrix/release notes must explicitly retain the unverified real IPv6 limitation. Do not claim complete dual-stack production validation from IPv4 VM/container or synthetic tests. Any additional IPv6 validation or requirement change needs an explicit scope decision; it is not a reason to block independent systemd/IPv4 work or conceal the remaining release qualification. Out of scope: replacing Alpine coverage, new product features, another service manager, global toolchain/package installation, production access, automatic VPN rollback, new unapproved direct dependencies, and a general-purpose VM framework. Choose the smallest reproducible VM procedure using an available approved runtime; if none exists, record the prerequisite instead of installing one silently. #### Current implementation and critical boundaries - M7 provides actual startup, root checks, executable-relative settings, TUI and serialized ApplicationWorker. Help/version remain unprivileged. Use real root management only inside the selected VM, never on the development/production host. - M8's `src/container_driver.rs` is cfg(test) and uses an explicitly fake fixture service apply plus real observation/persistence. It does not exercise zbus. Do not reuse that callback as M9 service evidence. Reuse safe fixture generation and redacted protocol patterns only where their semantics actually match. - Application has preview/confirmation, pre-restart backup, attempt/generation checks, bounded service/runtime operations and durable state marking. Public `Application::restart` is the required VM entry point. Test-only VM code may inspect private results but must not substitute its service/read/install adapters. - The pinned zbus 5.19.0 adapter subscribes before submission, resolves the loaded unit's primary ID, follows the returned job and accepts only `done`. Verify this against actual systemd, including fast completion and an alias resolving to a primary unit. Do not demand a daemon PID from an active/exited oneshot service. - A service job completing does not prove peer correspondence; matching peers do not prove verification was persisted. Check both stages and address release. Historical markers remain historical after reopening. Retain M4 journal recovery and M5 timeout/partial-runtime-change reporting; no rollback/retry is automatic. - A release executable is distinct from a cfg(test) harness. Capture each artifact's source identity/target and prove the actual executable starts in the VM as well as exercising library operations through a test driver. #### Tasks and ownership Execute sequentially with one writer, GPT-6-Sol/medium coordination and GPT-6-Luna/high implementer, independent reviewer and repairer. Provide bounded file ownership and acceptance IDs; the coordinator maintains this current snapshot. | Task | Owner | Deliverable and boundary | State | | --- | --- | --- | --- | | M9.1 | implementer | `docs/VM_ACCEPTANCE.md` and `tests/vm/` contract: explicit disposable target selection, pinned base/provisioning, driver, prerequisites and cleanup; no production endpoints | paused by user: guarded first start ran; Debian installer progress was observed, then the exact disposable VM was powered off on stop request; guest completion/provisioning/reset/cleanup unverified | | M9.2 | implementer | Minimal VM fixture/opt-in driver and build recipe: real systemd/WireGuard service, private synthetic settings/data, compatible test and release artifacts | pending M9.1 guest/guard | | M9.3 | implementer | Execute actual successful/failed/timeout/verification VM scenarios below; repair narrowly if real integration exposes defects | pending M9.2 fixture | | M9.4 | implementer | README/operations/recovery and release documentation, requirements-to-evidence matrix, release-candidate identity/runtime dependency assessment | complete for host/documentation scope: independently reviewed operator/recovery docs, qualified matrix and native GNU artifact assessment; guest startup remains blocked | | M9.5 | reviewer, repairer | Fresh independent review of VM targeting, actual production adapter use, failures/recovery, evidence and remaining release limitations; repair/re-review | partial document reviews closed; fresh VM implementation review pending after M9.1–M9.3 | | M9.6 | coordinator | Integrated default/affected Alpine/VM gates, concise final evidence and qualified acceptance decision; no publishing or deployment | prior host gates passed; integrated VM gates and acceptance pending | M9.4 review finding: **M9.4-R1 (P2, closed)** — `docs/RUNTIME.md` described accepted M7 TUI/startup and M8 real IPv4 Rustix validation as future work. Targeted repair corrected those claims while retaining the systemd/D-Bus and real IPv6 limits; independent re-review closed it. Guest/systemd validation remains unavailable. M9.1-R1–R8 (P2, closed on independent re-review) covered bounded guest marker capture, strict inventory, accurate host access status, occupied-path preflight, the observed NAT baseline, exact SATA controller inventory, target-folder symlink escape, and actual VBox machine-readable recording/storage syntax. Repairs and regressions passed the integrated synthetic VM suite (26/26). The create run registered only the pinned target UUID `b14c850b-a21e-4a56-acca-785590f0c4ac`; the UUID-pinned resume configured its VDI, ISO, host marker and manifest. The actual read-only guard then passed on the powered-off target. Neither create nor resume may be rerun on this occupied state. Diagnostic attempts are in [milestone-9 diagnostics](docs/diagnostics/milestone-9.md). M9.1-R9/R10 (P2, closed on independent re-review) corrected unattended command options and accepted VirtualBox's empty SATA port rows while rejecting extra media. The integrated synthetic VM suite passed 33/33 before actual preparation. #### VM setup and execution contract Use a disposable server VM and a controlled synthetic client in a separate VM or isolated namespace inside the lab. Test traffic must reach tunnel-only addresses, not succeed via the management network. Do not attach physical production networks, forward production credentials, mount host D-Bus/system paths, or expose host service control. Keep management reachable independently of the VPN being restarted. Document VM image/release, kernel, architecture, systemd, D-Bus and WireGuard-tools versions, fixture units/drop-ins and exact provision/build/run/reset commands. Cache immutable image/build inputs; reset mutable state between scenarios. Require explicit target selection and lab marker validation before privileged test actions. A remote target being reachable or running Linux is not sufficient authorization. Refuse missing identity markers, ambiguous endpoints and accidental host execution. Use bounded waits/transport, owned-resource cleanup and redacted reports. Secrets never travel in argv, public logs, committed images/snapshots or evidence files. Provision wg-quick/service prerequisites in the VM only, with a loaded/enabled fixture service, permissions compatible with SavedStore, and stable invariant paths. Set up fixtures without unsafe SaveConfig rewriting; test preflight refusal of such a configuration rather than modifying administrator settings automatically. Existing fixture hooks, if needed, are fixed reviewed lab code, never imported text. Network/kernel prerequisite changes and snapshots affect the disposable guest only. Do not use a naked ignored test that can restart the current host's service when someone runs --ignored. Put target guards at the earliest privileged boundary. Use an explicitly selected VM launcher/driver and document exact commands after its files exist. All normal cargo tests remain unprivileged and bus/network-free. No production environment switch or fake-systemd mode is added to the binary. #### Required scenarios 1. **Real successful apply.** Establish the existing client's IPv4 tunnel. Through Application create/Commit/deliver a new client and show saved/runtime divergence. Preview/cancel preserves draft and sends no service request; confirmed restart with the real adapter has a completed pre-restart backup, matching terminal job, actual interface/peer comparison and durable state write. New traffic succeeds, verification history is recorded, and reopening treats it as history, not a new live verification. Repeat a fast real job/loaded alias case without losing its completion signal. Do not use log inference in place of application outcomes. 2. **Deletion and reservations.** Commit deletion of a working client: access and reservation remain until the real successful restart/verification path. After it, the peer is absent, fresh tunnel-only traffic fails, durable reservation is released and old backups remain. Establish positive traffic first and bound negative checks so unrelated fixture failure cannot pass the scenario. 3. **Failures and deadline.** Use fixed guest-only fixture unit/drop-ins to cause a terminal job failure and a submitted job exceeding the configured deadline. Check bounded return, honest submitted/may-still-complete classification, no automatic retry/rollback, no false Verified state or IP release. Wait/reset the guest explicitly after a late completion; timeout is not cancellation. Also cover an unresolved configured unit before submission and backup failure that prevents any restart. Use focused synthetic tests for exhaustive bus races, not destructive daemon manipulation merely to duplicate unit coverage. 4. **Successful job, failed verification.** A dedicated fixture service completes but leaves the interface missing or a mismatched peer set. The real adapter must report the appropriate verification failure and retain reservations; no client handshake is required to prove correct idle peers. A separate controlled saved- state write failure after actual restart must report changed runtime without falsely recording durable verification. RecoveryRequired must prevent unsafe subsequent mutations; use established M4 fault seams only for persistence, never replace the actual service/runtime calls to claim this scenario passed. 5. **Manual restore/recovery.** After a deliberately bad saved/apply state, select a known-good archive through preview/confirm without supplying a vault password. Check precautionary backup, restored settings/config/vault and unchanged runtime until an explicit real restart; then verify the restored peer set and traffic. Follow the documented manual procedure, not a hidden automatic rollback. Keep transport access independent and validate file permissions/reopen behavior. 6. **Actual executable smoke.** In the guest's console/PTY, start the release binary from a dedicated root-only directory outside PATH, with settings alongside it. Check list/current statistics and one controlled Commit/restart workflow through the real worker path; exit/cancel restores terminal state. Help/version work as non-root without settings; non-root management is rejected. Reuse M7's detailed synthetic terminal tests rather than repeating every keyboard/form edge case. #### Release preparation and evidence Document installation prerequisites and binary placement, 0600/0700 permissions, setup/import with untouched originals, initial vault password and unrecoverable password loss, first/reissue file/QR behavior, Commit versus restart, timeout and partial-write outcomes, and manual recovery. Explain backup plaintext server-secret exposure and that backups beside the binary are not protection against disk loss. Do not add automatic retention, encryption, or remote backup features. Produce a native release candidate under the pinned compiler and locked dependency set. Record source/dirty identity, artifact identity/target and actual dynamic-library requirements. Test the artifact in the chosen guest; distinguish its supported baseline from the tested development host. Existing musl artifacts may be reused only with captured compatible inputs. Do not claim reproducibility from merely running cargo build twice or claim support for untested architectures/distributions. Create a specification-to-evidence matrix (including preservation, secrets, saved/ applied separation, backups, delivery and terminal behavior) using existing valid M1–M8 evidence plus M9 results. Reuse M8's current evidence where relevant inputs are unchanged; rebuild/rerun affected Alpine behavior when M9 changes invalidate it. Never carry forward a pass from an unidentified or older dirty artifact. List actual IPv4, synthetic dual-stack, unverified real IPv6, QR/device and systemd scopes honestly rather than labelling the entire product universally verified. #### Acceptance and validation - [ ] M9-A1: documented guarded VM procedure/compatible artifacts execute in the explicitly selected disposable guest; default tests cannot target host services. - [ ] M9-A2: real zbus submission/job completion and Rustix verification succeed through Application::restart, including fast completion/primary-unit alias; actual release-worker startup is exercised separately from the test harness. - [ ] M9-A3: completed pre-restart backup precedes service mutation; Commit/restore never apply changes; canceled draft/restart actions have no unintended effects. - [ ] M9-A4: real successful deletion/apply releases durable reservations only after full verification; fresh traffic and reopened state support the result. - [ ] M9-A5: unit/job/deadline/backup/missing-interface/mismatch and post-restart persistence failures have accurate staged outcomes, retained reservations, deterministic manual recovery and no automatic restart retry/VPN rollback. - [ ] M9-A6: previewed password-free restore plus explicit real restart recovers the selected known-good state/traffic; the VM procedure matches operator instructions. - [ ] M9-A7: root/non-root executable-relative startup, limited real terminal flow, artifact compatibility and private output/backup permissions are verified in VM. - [ ] M9-A8: evidence matrix, release artifact identity, runtime dependencies, operator/recovery docs and unresolved coverage qualifications are accurate. M8's IPv6 exclusion is preserved; no unsupported full dual-stack claim is made. - [ ] M9-A9: current default gates and affected Alpine/VM scenarios pass on the identified integrated revision; independent review closes actionable findings. Unavailable required VM checks prevent acceptance, rather than becoming a mock pass. #### Validation and execution records From repository root, run these existing commands at the integrated gate, not on every diagnostic retry: ```sh cargo fmt --all -- --check cargo clippy --locked --all-targets -- -D warnings cargo test --locked cargo build --locked --release ./target/release/wireguard-admin --help ./target/release/wireguard-admin --version git diff --check ``` Before VM execution, document exact newly implemented build/provision/run/cleanup commands in `docs/VM_ACCEPTANCE.md` and the existing selected-daemon Alpine command in TESTING.md. No VM target/command is claimed available yet. Record actual results, counts, selected VM/base identity, source/artifact identity, cleanup and review mapping in `docs/evidence/milestone-9.md` when runs occur. Keep only latest applicable evidence/status in this plan. Investigations belong in `docs/diagnostics/milestone-9.md` when needed; after two failed repairs reassess/minimize before another expensive full run per EXECUTION_RECORDS.md. Do not create placeholder reports claiming tests. #### Review, evidence and handoff M9.1 initial non-runnable contract passed read-only review. After the user authorized local VirtualBox, approved outside-sandbox inventory confirmed `VBoxManage` 7.2.8 works and `/dev/vboxdrv` is visible there; only Win7/Win11 are registered and neither is a test target. The official Debian 13.7 amd64 netinst ISO in the private lab root has SHA256 `a7ef94ac2fb9a7fec454552abd629b7cc9d5155c886165a45649f5ce6167e355`, matching the downloaded official SHA256SUMS. The new target UUID `b14c850b-a21e-4a56-acca-785590f0c4ac` is configured and powered off with the private manifest/marker, exact VDI and pinned ISO; actual read-only guard `check` exited 0. The integrated synthetic VM suite passed 26/26 after independent review/repair; no existing Windows VM was modified. An exact-target VirtualBox unattended install dry run with private synthetic password files, Guest Additions ISO, and redacted output exited 0. The reviewed preparation command then exited 0; read-only `showvminfo` found the expected VDI and a private generated auxiliary `.viso` at SATA ports 0/1. It did not boot the guest. M9.4 [operator instructions](docs/OPERATIONS.md), [qualified evidence matrix](docs/RELEASE_READINESS.md), and updated README, environment and runtime guidance passed independent review after M9.4-R1 repair. The native GNU artifact SHA256 is `d52d43cd02332352ffbd09c97e3b8095a710359128260c1286006e1c76c1b283`; host `--help` and `--version` exited 0. On the integrated unchanged Rust source, `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked`, `cargo build --locked --release`, and `git diff --check` exited 0. M8's source-input fingerprint remains `b28dc6e4cb92aa6edf384d966ba5952a8d6d0efe4c22d6a804b67fb2c0297da2`; no Rust/Alpine input changed, so M8 evidence was not rerun. No VM scenario, production access or key read occurred. Guest compatibility and actual zbus/ systemd behavior remain unverified. The prior blocker audit was valid before VirtualBox was made accessible outside the sandbox; its no-VM conclusion is superseded by this authorized prerequisite path. M9.4 has no open finding. M8 is accepted for real IPv4 Alpine application/transport coverage; its test-only service callback is not systemd evidence. Carry forward that distinction. Initial M9 evidence is unavailable until actual guest execution; record missing historical inputs as unavailable rather than reconstructing them. Next action: none for M9. The user retired it and authorized development to continue with M10. Do not restart the VM or resume M9 work without a new explicit request. No M9-A1–A9 check is complete. #### Outcome Retired at the user's request. The separate disposable Debian VM `b14c850b-a21e-4a56-acca-785590f0c4ac` was powered off; a read-only running-VM inventory was empty afterward. M9.1 remains incomplete, M9.2–M9.3 have not run, and M9-A1–A9 remain unchecked. Prior host gates and M9.4 documentation passed; no release acceptance or deployment is claimed. Real IPv6 remains separately unverified by the previously accepted test scope. ### Milestone 10 — Visible styled keyboard quick reference in bottom panel Status: accepted (2026-10-26T21:07:24Z). The user retired M9 and explicitly directed development to continue. M1–M8 are accepted; M9 is no longer a prerequisite. M10's scoped TUI checks and independent review passed. #### Outcome and scope Display discoverable keyboard controls inside the bordered panel at the very bottom of the terminal. Users should be able to find routine operations without opening Help. Render each hint as a distinct key label followed by its action, with bold/color emphasis on keys and readable action text. Keep UI text in English, following repository conventions. These are keyboard hints; adding mouse interaction is outside this milestone. The present `src/tui/render.rs::draw` reserves only two rows for a fully bordered footer. Its top and bottom borders leave no interior row, hiding the existing plain shortcut text. Repair layout as well as styling; merely recoloring that paragraph cannot meet the requirement. #### Context and implementation steps | Task | Owner | Scope and expected result | State | | --- | --- | --- | --- | | M10.1 | implementer | Inspect `src/tui/render.rs`, `state.rs`, `forms.rs` and `terminal.rs`; derive hints from actual supported keys and screen/form state. Implement the bottom panel with sufficient interior height, styled key/action groups and width-aware packing. | complete for task scope: R1/R2 repaired and independently re-reviewed; render 9/9 and forms 7/7 focused tests passed on integrated HEAD | | M10.2 | implementer | Cover resize, narrow/minimum terminals, list/details, forms, progress and QR disclosure with synthetic render/navigation checks. Update README shortcut guidance if needed. | complete: render 13/13, TUI workflow 6/6 including PTY smoke; integrated review closed | | M10.3 | reviewer / repairer | Independent review of actual layout, hints versus handlers, readability, clipping and state correctness; repair and re-review actionable findings. | complete: R1 Clippy and R2 README precision repaired and independently re-reviewed with no remaining finding | | M10.4 | coordinator | Run integrated gates, record source/artifact identity and actual results, maintain this plan and make the scoped acceptance decision. | complete: integrated gates passed; source fingerprints and results in [M10 evidence](docs/evidence/milestone-10.md) | List/details quick reference must include navigation (`Up/Down`, `Enter`, `Esc` as applicable), `?` Help and `q` Exit, plus the implemented management keys: `n` New, `r` Rename, `d` Delete, `p` Preview, `c` Commit, `x` Discard, `R` Restart, `o` First file, `O` Reissue, `b` Backups, `B` Backup, `s` Setup and `i` Import. Distinguish uppercase from lowercase visibly. Context-dependent actions must be omitted or explicitly marked unavailable when their real prerequisites are not met. Form hints must reflect their actual Enter/Tab/Esc behavior; busy screens must not advertise blocked submissions. QR disclosure must retain a visible hint for clearing it with Esc. Use the existing Ratatui styles and dependencies. Select contrasting styles for navigation, ordinary actions and destructive actions, while keeping key/action text sufficient without color. Adapt panel height to packed rows and terminal height, preserve usable main content, and retain the existing too-small-terminal fallback. At narrow widths prioritize navigation and Help, showing an explicit Help/overflow hint for omitted commands rather than silently clipping them. Keep Help as the complete reference and preserve all existing operation semantics. #### Acceptance and validation - [x] M10-A1: the lowest bordered panel has visible interior text; at 80x24 all applicable list actions are displayed in readable packed rows. - [x] M10-A2: key labels are visually distinct with bold/color styling; action text is readable, and meaning remains understandable without color. - [x] M10-A3: hints match actual key handlers, uppercase/lowercase and active screen/form prerequisites; no hint implies a disabled operation can run. - [x] M10-A4: resizing through 120x30, 80x24, 40x8 and the supported minimum 34x8 causes no panic, overlapping borders or invisible footer text; compact layouts retain navigation/Help and indicate omitted commands. Below-minimum sizes retain a readable resize message. - [x] M10-A5: list/detail navigation, forms, progress and QR remain usable; footer rendering exposes no secrets and performs no application mutations. - [x] M10-A6: focused synthetic rendering checks and all required Rust gates pass; independent review closes actionable findings with recorded evidence. Planned validation: `cargo test --locked tui::`, `cargo test --locked --test tui_workflow`, then `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked`, `cargo build --locked --release` and `git diff --check`. Use synthetic fixtures and Ratatui test buffers; inspect styles as well as rendered text and geometry. No production or VM operations are needed for this presentation change. Actual integrated results and source identity are in [M10 evidence](docs/evidence/milestone-10.md). #### Review, evidence and handoff M10.1 initial implementation added visible styled footer rows and passed `cargo test --locked tui::render` (7/7), `cargo fmt --all -- --check`, and `git diff --check`. Independent review found **M10.1-R1 (P2, closed)**: compact rows advertised list navigation/Help during forms, QR, and pending work; and **M10.1-R2 (P2, closed)**: generic form Enter was false for draft review and Tab was missing from tabbed forms. Targeted repair added secret-free form key capabilities and context-specific compact rows; independent re-review closed both findings on current integrated HEAD `e141c03`. Render tests passed 9/9, forms tests 7/7, format and diff checks passed. M10.2 coverage remains; no milestone acceptance is claimed yet. M10.2 added synthetic TestBackend coverage for 120×30, 80×24, 40×8, 34×8, below-minimum fallback, all applicable saved-client list hints, QR Esc/footer rendering, and key/action styles. README now describes contextual shortcuts. `cargo test --locked tui::render` passed 13/13 and `cargo test --locked --test tui_workflow` passed 6/6 including PTY smoke; `cargo fmt --all -- --check` and `git diff --check` passed. Integrated review subsequently closed its findings. No VM or production operations were run. Fresh integrated review found **M10.3-R1 (P2, closed)**: Clippy failed on a collapsible `if`; repair preserved the action prerequisite and the gate passed. It also found **M10.3-R2 (P3, closed)**: README overstated narrow-screen Help availability; repair qualified the claim to list/details. Independent re-review closed both with no remaining actionable finding. A 34×8 QR assertion confirms Esc guidance and no fixture payload exposure; compact QR may be too large to display, but can be cleared. [M10 evidence](docs/evidence/milestone-10.md) records the integrated results and limitations. The user removed M9 as a prerequisite without accepting its unchecked M9-A1–A9 criteria. The project configuration specifies GPT-6-Sol/medium for the coordinator and GPT-6-Luna/high for all four custom roles. No new dependencies were added. #### Outcome Milestone 10 accepted for scoped TUI presentation on the integrated source identified in [M10 evidence](docs/evidence/milestone-10.md). M9 stays retired and unaccepted. The user's subsequent AllowedIPs classification request is handled in M11 below. ### Milestone 11 — Valid subnet routes shown as a capability limitation Status: accepted (2026-10-26T21:20:07Z). Prerequisite M10 accepted; M9 was retired by user direction and remains unaccepted. This is the subsequent user-requested behavior change. No VM work is in scope. #### Outcome and scope Keep a peer with valid extra subnet routes in the client list as `inspection only`, without treating those routes alone as a configuration anomaly or counting them on the issues screen. Details explain neutrally: “This peer contains subnet routes. Managing this configuration is not supported yet.” Malformed addresses, invalid keys, duplicate identities, document issues and other genuine errors remain visible. Preserve all source bytes and routes; do not expand the mutable client model, address allocation or restart verification, or remove safety checks. #### Context and implementation steps Read the parser, projection and TUI before editing. `ConfigDocument::parse` already distinguishes parse errors from valid `IpNet` values outside the client model. `Application::display_projection` retains all peer issues and marks projection failures inspection-only. The TUI currently treats every peer issue as an anomaly in list/details/`!` at the start of M11. Classify valid unsupported address shape separately at the presentation boundary, while retaining raw issues for edit/restart safety. A subnet-specific capability notice must be derived from parsed routes, not from parsing display strings. M2's historical “inspection issues” wording described visibility; this user-directed change refines their UI severity without rewriting M2's acceptance evidence. | Task | Owner | Scope and expected result | State | | --- | --- | --- | --- | | M11.1 | implementer | Add explicit error-versus-capability classification and focused parser/projection/TUI regressions for valid subnet routes, normal client and genuine errors; preserve mutation guards. | complete: parser/projection/TUI regressions and safety preservation inspected; integrated review closed | | M11.2 | reviewer / repairer | Independently review behavior, safety boundaries and tests; repair and re-review actionable findings. | complete: R1 withdrawn after source-based re-evaluation; R2 repaired and independently re-reviewed with no remaining finding | | M11.3 | coordinator | Run focused and full Rust gates, record source/evidence and make acceptance decision. | complete: integrated gates passed; source fingerprints/results in [M11 evidence](docs/evidence/milestone-11.md) | #### Acceptance and validation - [x] M11-A1: a peer with `10.9.0.10/32, 192.168.88.0/24, 192.168.8.0/24` is visible as `inspection only`, with original bytes/routes intact and the exact neutral details explanation. - [x] M11-A2: those valid subnet routes alone cause no `! anomaly`, error count or `UnsupportedAllowedIpShape` text in the list or issues screen. - [x] M11-A3: a normal supported peer remains mutable and unchanged; malformed addresses, invalid keys, duplicate identities and document issues remain visible as errors/ambiguities alongside any valid capability limitation. - [x] M11-A4: unsupported peers remain protected from TUI/application unsafe mutations; allocation and restart verification behavior are unchanged. - [x] M11-A5: focused regressions, independent review and all applicable Rust completion gates pass on the integrated result. Planned checks: focused document, application, TUI and config-preservation tests; `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked`, `cargo build --locked --release`, and `git diff --check`. Use synthetic fixtures only. No Alpine, VM or production checks are needed for this classification/presentation change. #### Outcome Accepted for the requested classification/presentation behavior. **M11-R1 (withdrawn)**: review initially called a second distinct `/32` route a configuration error; reviewer re-evaluated against the WireGuard `wg(8)` format and confirmed it is another valid shape beyond the client model, with no actionable finding. **M11-R2 (coordinator, closed)**: without any subnet, valid additional host routes or IPv6-only shapes still appeared as anomalies. The repair classifies parsed-valid shape variants as capability limitations for display, retains raw issues for safety, and adds a generic detail notice for non-subnet cases. Independent re-review found no actionable issue. [M11 evidence](docs/evidence/milestone-11.md) records the source, focused checks and all Rust gates. An external workspace actor advanced HEAD to `18f28ee` while M11.1 was underway; the review covered committed and uncommitted M11 inputs. This team created no commit. No Alpine, VM or production behavior is claimed. Stop after this milestone. ### Milestone 12 — Aligned, responsive client table Status: accepted (2026-10-26T21:39:45Z). M11 is accepted; M9 remains retired and unaccepted. The user requested this scoped presentation change. #### Outcome and scope Render client records with Ratatui's native `Table` and explicit column constraints. Show `State`, `Name`, `Public key`, `Mode`, and `Allowed IPs` headers when width permits. Keep columns aligned across short/long and Unicode aliases, supported and inspection-only peers, and all draft states. Truncate predictably by terminal cell width. At narrow widths prioritize State and Name, then omit or shrink secondary columns while preserving Enter access to full peer details. Retain diagnostics, document-level issue rows, draft-removal information, selected-row highlight, keyboard navigation, and the M10 bottom shortcut panel. This milestone changes rendering only; parser, projection, validation, persistence, allocation, restart and VPN behavior are unchanged. #### Context and steps `src/tui/render.rs::render_list` currently builds variable-length `ListItem` strings, so columns drift with alias/state length. `UiState` and `src/tui/terminal.rs` select real peers by `peer_index`; draft-removal and document-issue rows are appended after peer rows and are not peer selections. Keep that row order and mapping when replacing the widget with `Table`. `detail_lines` already retains full peer public key, alias, routes and issues. The list must continue escaping untrusted display text. Use the existing approved `ratatui` dependency and its terminal-width APIs; do not add a direct Unicode-width crate or literal tab delimiters. | Task | Owner | Scope and expected result | State | | --- | --- | --- | --- | | M12.1 | implementer | Replace list rendering with responsive Ratatui Table, preserve peer selection mapping, state/diagnostic information and footer; add focused mixed-width/Unicode/long-route/empty/narrow tests. | complete: render 20/20, terminal 8/8, TUI workflow 6/6 on final integrated source | | M12.2 | reviewer / repairer | Fresh independent review of layout, truncation, details access, draft/document rows and navigation; repair and re-review actionable findings. | complete: R1–R4 repaired and independently re-reviewed closed; no remaining actionable finding | | M12.3 | coordinator | Verify integrated files, run focused and Rust completion gates, record evidence and accept only if criteria pass. | complete: all Rust completion gates passed; source fingerprints/results in [M12 evidence](docs/evidence/milestone-12.md) | #### Acceptance and validation - [x] M12-A1: native Table has the five requested headers at ordinary width; row columns align across mixed alias lengths, states, duplicate aliases, supported and inspection-only peers, with no literal tabs. - [x] M12-A2: Unicode aliases and long values truncate at display-cell boundaries without corrupting adjacent columns; full peer alias, public key, routes and diagnostic explanations remain in details. - [x] M12-A3: 120×30, 80×24, 40×8, 34×8 and below-minimum layouts retain visible State/Name at supported sizes, adapt or omit secondary columns, preserve the M10 footer and readable resize fallback. - [x] M12-A4: Up/Down highlight and Enter-to-details stay tied to the selected peer (including unsupported/malformed peers), without enabling mutation; empty lists, draft additions/removals and document issues remain readable and do not disturb selection alignment. - [x] M12-A5: focused synthetic render/navigation tests and applicable Rust completion gates pass; independent review closes actionable findings. Planned checks: `cargo test --locked tui::render`, `cargo test --locked tui::terminal`, `cargo test --locked --test tui_workflow`, `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked`, `cargo build --locked --release`, and `git diff --check`. Synthetic Ratatui/PTY evidence only; no Alpine, VM, production, or live VPN checks are required for this presentation change. #### Outcome Accepted for scoped TUI presentation. **M12-R1 (P2, closed):** compact bordered table hid all peers; repair uses a borderless compact table with a visible selected row and footer. **M12-R2 (P2, closed):** empty/document-only views falsely highlighted row zero; selection is now `None`. **M12-R3 (P2, closed):** five columns omitted the two-cell marker budget; repair and buffer assertions cover the right edge. Independent re-review closed R1–R3. **M12-R4 (P2, closed):** at widths 72–78 the five-column threshold activated before the `Allowed IPs` header fit. Repair uses the full-header minimum width and buffer tests at 72/78/79/80; independent re-review closed R4 with no remaining finding. Final focused tests, format, Clippy, locked full suite, release build and diff check passed on the source in [M12 evidence](docs/evidence/milestone-12.md). Synthetic UI/PTY evidence does not establish physical terminal, Alpine or systemd behavior. Stop after this milestone; do not resume VM work, commit or deploy. ### Milestone 13 — Compact terminal QR disclosure Status: accepted (2026-10-27T21:13:30+02:00). M12 is accepted; M9 remains retired and unaccepted. This is the user's scoped terminal disclosure repair. #### Outcome and scope Render authorized QR modules with two vertical modules per terminal cell, explicit dark/light colors, and the complete four-module quiet zone. Fit must account for the actual bordered disclosure layout and wrapped delivery notice; retain file-backed fallback and Esc cleanup when it cannot fit. Existing file delivery, password authorization, and draft/saved/applied behavior remain intact. #### Tasks and acceptance | Task | Owner | State | Evidence required | | --- | --- | --- | --- | | M13.1 | implementer | complete for implementation scope | Compact rendering/layout and focused deterministic tests in `src/tui/render.rs` and test-only QR fixture in `src/delivery/qr.rs` | | M13.2 | independent reviewer; repairer | complete | R1–R5 repaired and independently re-reviewed; no actionable scoped finding remains | | M13.3 | coordinator | complete | Integrated files checked; pinned Rust gates passed; source and artifact recorded in [M13 evidence](docs/evidence/milestone-13.md) | - [x] M13-A1: all four two-module cell combinations, odd-height light padding, explicit colors and full quiet zone render correctly. - [x] M13-A2: a deterministic nonsecret 73-module matrix uses 73 columns and 37 QR rows, with exact fit/fallback dimensions from bordered layout and wrapped notice. - [x] M13-A3: fallback points to saved file; resizing redraws the same authorized QR; Esc clears it; no matrix or configuration enters logs, Debug or snapshots. - [x] M13-A4: independent review closes findings and focused plus required Rust completion gates pass. Automated render evidence is distinct from manual scanning. Planned checks: `cargo test --locked tui::render::tests::`, focused QR tests, `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked`, `cargo build --locked --release`, and `git diff --check`. Synthetic tests require no Docker or VM; real systemd is outside this scope. Evidence will be recorded in `docs/evidence/milestone-13.md`. #### Outcome and handoff M13-R1–R5 (P2, closed): fallback visibility at 34×12/20×8, Draft/Saved/Applied status, all quiet-zone edges, exact available-area reporting and uncertain delivery wording were repaired and independently re-reviewed. [M13 evidence](docs/evidence/milestone-13.md) records focused and integrated results. No production or host service was used. Accepted for the automated TUI rendering scope. A 73-module matrix needs 73×37 QR cells; the tested bordered layout with a one-row notice fits at 75×44. Longer notices increase the required terminal height. Physical terminal and phone/decoder scanning remain unverified; M9 systemd acceptance remains retired. ### Milestone 14 — Restart verification for valid legacy peers Status: accepted (2026-10-27T21:55:11+02:00). M13 is accepted; M9 remains retired and unaccepted. This is a scoped fix for restart preview and durable verification. #### Outcome and scope Use the complete saved technical peer set for restart eligibility and runtime verification, independently of optional managed alias metadata and the narrower management `Client` shape. Do not fabricate aliases/dates, rewrite saved peers, omit peers, or record a partial management snapshot as applied. Preserve the pre-restart backup, explicit choice, configured service/timeout, draft handling, and address-release boundary. Unsafe saved documents must stop before service mutation with secret-free identity/section and condition diagnostics. #### Tasks and acceptance | Task | Owner | State | Evidence required | | --- | --- | --- | --- | | M14.1 | implementer | complete for implementation scope | Restart projection and fixture regressions in `src/app.rs`, `src/domain.rs`, `tests/runtime_restart.rs` | | M14.2 | independent reviewer; repairer | complete | Independent behavior review found no issue; M14-R1 Clippy repair independently re-reviewed and closed | | M14.3 | coordinator | complete | Integrated files, pinned Rust gates and source/artifact identity recorded in [M14 evidence](docs/evidence/milestone-14.md) | - [x] M14-A1: saved managed client plus valid metadata-free legacy peer previews and restarts after full synthetic runtime match, without config rewrites. - [x] M14-A2: supported IPv4-only and technically valid route shapes outside the management model can be verified as a complete saved peer set. - [x] M14-A3: malformed/ambiguous peers and structural problems block before backup/service mutation with actionable public identity or section location. - [x] M14-A4: missing/extra/mismatched or incomplete runtime peers cannot be recorded as applied or release deleted addresses; successful full verification persists historical status and safe reservations across reopen. - [x] M14-A5: independent review closes findings; focused and required Rust completion gates pass. Fixture results remain separate from VM systemd evidence. Planned checks: focused `cargo test --locked app::tests::` and runtime restart tests, `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked`, `cargo build --locked --release`, `git diff --check`. Synthetic tests use fake service/runtime adapters; no Docker or VM is required. Detailed evidence will be recorded in `docs/evidence/milestone-14.md`. #### Outcome and handoff Accepted for the synthetic application/runtime-adapter scope. Complete `SavedPeerProjection` verification covers valid legacy and broader-route peers; no partial `Client` snapshot is recorded as applied. M14-R1 (P3, closed): Clippy's collapsible diagnostic guard was repaired, independently re-reviewed, and all integrated gates passed. [M14 evidence](docs/evidence/milestone-14.md) records source identity and tests. No disposable-VM systemd acceptance or production access is claimed; M9 remains retired and unaccepted. ### Milestone 15 — Diagnosable systemd restart and unloaded-unit resolution Status: accepted (2026-10-27T22:19:58+02:00) for synthetic adapter and application behavior. M14 is accepted; M9 real systemd acceptance remains retired and unaccepted. #### Outcome and scope Preserve the service adapter's typed failure through application reporting and readable TUI status, including operation phase and configured unit without raw D-Bus/service text. Resolve existing unloaded units through the documented systemd loading API while preserving alias identity, deadlines, signal ordering, conservative submission status, mandatory backup, explicit confirmation and full runtime verification before durable applied recording. #### Tasks and acceptance | Task | Owner | State | Evidence required | | --- | --- | --- | --- | | M15.1 | implementer | complete for implementation scope | `src/service.rs`, `src/service/dbus.rs`, `src/app.rs`, `src/tui/terminal.rs`, mechanical `src/container_driver.rs`; synthetic tests | | M15.2 | independent reviewer; repairer | complete | R1 uncertainty and R2 unloaded-unit test gap repaired and independently re-reviewed | | M15.3 | coordinator | complete | Integrated diff, pinned Rust gates and [M15 evidence](docs/evidence/milestone-15.md) | - [x] M15-A1: every service failure category survives into application report and readable unit/phase TUI status; raw bus/service text is excluded. - [x] M15-A2: synthetic API tests exercise the production `LoadUnit` resolution flow for unloaded and aliased units; missing units and access denial stop before restart, with shared deadline and subscription-before-request preserved. Real systemd remains unverified. - [x] M15-A3: failures retain conservative submitted/uncertainty flags and never mark applied; backup, confirmation, generation and runtime gates remain intact. - [x] M15-A4: independent review closes actionable findings; focused tests and required pinned Rust gates pass. Synthetic evidence and disposable-VM acceptance are reported separately. Checks: focused service/application/TUI tests; `cargo fmt --all -- --check`; `cargo clippy --locked --all-targets -- -D warnings`; `cargo test --locked`; `cargo build --locked --release`; `git diff --check`. All passed on the repaired Rust inputs. Tests used synthetic fixtures only; no host service or production configuration. Detailed outcomes and identities: [M15 evidence](docs/evidence/milestone-15.md). #### Outcome and handoff Accepted for the synthetic adapter and application scope after R1 (owner-change uncertainty) and R2 (unit-resolution test gap) were repaired and independently re-reviewed. Official systemd D-Bus documentation confirms `GetUnit` fails for an unloaded unit and `LoadUnit` loads it from disk if possible; the prior call was a confirmed API mismatch. The cause of the reported host failure and actual systemd acceptance remain unproven without a disposable-VM run. [M15 evidence](docs/evidence/milestone-15.md) records the reviewed source, tests and release artifact. ### Milestone 16 — Root systemd private peer restart transport Status: accepted by explicit user direction (2026-10-28) following a reported successful manual check. The manual environment, commands and transport coverage were not supplied; the previously recorded disposable-VM evidence gap remains a validation limitation, not a claimed passing VM run. M9 remains retired and unaccepted. #### Outcome and scope Support explicit restart on a root-managed systemd host without a system bus by connecting directly to systemd's root-only private Unix socket using pinned zbus. Retain the system-bus path when available. Audit destination, signal match, owner monitoring and disconnect behavior separately for each transport; choose transport before `RestartUnit` and never retry after possible submission. Keep backup, confirmation, generation, full runtime verification, durable applied state and Commit/restore isolation unchanged. No production or host service access. #### Tasks and acceptance | Task | Owner | State | Evidence required | | --- | --- | --- | --- | | M16.1 | explorer | complete | Pinned zbus 5.19.0 source and official systemd private-socket protocol findings | | M16.2 | implementer | complete for implementation scope | Transport implementation, zbus feature/docs and 16 focused synthetic tests | | M16.3 | independent reviewer; repairer | user-directed close with evidence gap | R1 closed on re-review; R2 private-path behavioral evidence remains unavailable and was not independently closed | | M16.4 | coordinator | complete for available checks | Integrated source, pinned Rust gates and [M16 evidence](docs/evidence/milestone-16.md); VM unavailable | - [ ] M16-A1: root-only private peer connection and system-bus path work against real systemd with correct method destination and job signal tracking. Source audit and synthetic selection checks pass; real transport checks remain unavailable. - [x] M16-A2: source and synthetic checks preserve subscribe-before-request, canonical alias ID, bounded deadline, disconnect/uncertainty behavior, and no retry after possible submission. - [ ] M16-A3: synthetic selection/failure and existing job-tracker tests pass, but the private `SystemdBus` branch was not exercised end to end because local peer credential access returned sandbox `EPERM`. - [ ] M16-A4: R1 was closed; R2 lacks independent behavioral closure. Pinned Rust gates passed, but disposable-VM private and system-bus acceptance is unavailable. User-directed milestone acceptance waives this outstanding evidence rather than proving the criterion. Available checks: focused service/application tests, `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked`, `cargo build --locked --release`, `git diff --check` all passed on repaired inputs. Synthetic checks used no production socket or keys. [M16 evidence](docs/evidence/milestone-16.md) records exact results and identities. Disposable-VM checks were not run. #### Outcome and handoff Official systemd source confirms root `systemctl` connects to `/run/systemd/private` directly when PID 1 is systemd; official `systemd(1)` docs caution this socket is private to systemd. Pinned zbus 5.19.0 exposes peer mode behind its approved crate's `p2p` feature. The implementation uses destination-free peer calls and local signal matching without bus owner monitoring; the bus path remains available. M16-R1 (P2, private permission category) was repaired and independently closed. M16-R2 (P2, private-branch behavioral evidence) remains unverified by the recorded automated evidence: local zbus peer credential access returned sandbox `EPERM`, and the prior disposable Debian VM has no installed/verified guest. The user reported a successful manual check and directed that M16 be considered done; its details and transport scope are unavailable. This user-directed acceptance does not convert the missing disposable-VM checks into passing evidence. No production or host socket was touched by this execution. ### Bounded TUI maintenance TUI-FL-001 — Stable keyboard footer Status: complete (2026-10-27T22:59:34+02:00). This is a user-requested presentation repair to M7 while M16 remains blocked on independent systemd validation; it does not advance numbered milestones. Scope: `src/tui/render.rs` footer sizing, hint packing, and focused rendering tests only. Reserve two hint rows plus borders from the first standard render. Keep that height fixed across list selection, empty/populated views and action availability. At narrow widths show essential actions and an explicit route to existing Help without unreserved wrapping or silent clipping. Preserve the very-short-terminal compact path, shortcut behavior and context accuracy. | Task | Owner | State | Evidence required | | --- | --- | --- | --- | | TUI-FL-001.1 | implementer | complete for implementation scope | Fixed footer layout and synthetic buffer/height regressions | | TUI-FL-001.2 | independent reviewer; repairer | complete | R1 marker clipping, R2 nine-row content and R3 false omission repaired and independently closed | | TUI-FL-001.3 | coordinator | complete | Integrated diff, pinned Rust gates and [footer evidence](docs/evidence/tui-footer-layout.md) | - [x] Standard footer stays four rows total (two hints plus borders), with stable client-list height at fixed terminal size across selection and action changes. - [x] Hints have readable, consistent ordering; narrow and very short layouts show context-accurate essentials and Help without extra wrapping or silent clipping. - [x] Independent review closes findings and focused plus required Rust gates pass; evidence is recorded in [footer evidence](docs/evidence/tui-footer-layout.md). Checks: focused `tui::render::tests` and TUI workflow tests, `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked`, `cargo build --locked --release`, `git diff --check` all passed on the final Rust input. No service or production access. Exact outcomes and identities are in [footer evidence](docs/evidence/tui-footer-layout.md). First independent review found TUI-FL-001.2-R1 (P2, omission marker overflow) and R2 (P2, nine-row terminals lose client-list interior). A separate repairer fixed both and independent re-review closed them. That re-review found R3 (P3, unconditional marker reserve omits an action at width 102–103 even when all fit); the two-pass packing repair and regression were independently re-reviewed and closed. Final pinned Rust gates passed. [Evidence](docs/evidence/tui-footer-layout.md) separates synthetic render checks from any physical terminal verification. ### Milestone 17 — Adaptive client list, persistent details and clear states Status: accepted (2026-10-28T23:09:37+02:00) after independent review, R1/R2 repair and re-review, and pinned Rust gates. Evidence: [M17](docs/evidence/milestone-17.md). Prerequisites: M16 accepted under the sequential execution policy; pinned Rust toolchain and existing TUI fixture environment; actual GPT-6-Sol/medium coordinator and GPT-6-Luna/high roles. M16 was accepted by explicit user direction after a reported successful manual check; its details remain unavailable and are not M17 test evidence. The user confirmed this session's active coordinator model and effort; project TOML and the configured subagent roles specify GPT-6-Luna/high. #### Outcome and scope Make the client list use terminal space effectively: adaptive columns, an always-present selected-client detail pane when dimensions permit, and explicit Draft / Saved / Applied presentation. Governed by [SPEC](docs/SPEC.md) sections Data sources, Operations and states, and Client screen and statistics, all three [ADRs](docs/adr/), and [DEVELOPMENT](docs/DEVELOPMENT.md). All product text and identifiers remain English. Scope is presentation and secret-free view/navigation support. Preserve the two-row standard footer delivered by TUI-FL-001, action authorization, keyboard shortcuts, confirmations, QR disclosure and full-screen forms. No search, sorting controls, new telemetry collection, dependency changes, persistence schema, runtime/service behavior, configuration writes, terminal-theme overhaul or inferred online status. Handshake and current counters reuse existing observations in details; new table statistic columns are deferred. #### Context and implementation steps Repository inspection found `src/tui/render.rs::client_table_columns` fixes Name at 13 cells on wide screens and gives remaining width to combined Allowed IPs. `render_list` repeats supported/inspection-only Mode and derives row state from the narrower `client_states` map; peers outside that map must not be presumed absent from saved files. `render_details` and `detail_lines` already expose full public data, routes, limitations and observations, but only on a separate screen. `state_summary` counts managed saved clients and document issues, not all visible peers or peer issues. `SessionView.applied` is global; periodic statistics explicitly do not mark state applied. The proposed work must preserve these distinctions rather than relabel unknown facts as verified. Read before dispatch: AGENTS.md, SPEC.md, CONTEXT.md, all three ADRs, docs/IMPLEMENTATION.md, docs/EXECUTION_RECORDS.md, docs/PROJECT_LAYOUT.md, docs/DEVELOPMENT.md, docs/DEPENDENCIES.md, docs/ENVIRONMENT.md, docs/TESTING.md and docs/CODEX.md. Coordinator owns PLANS.md and acceptance; one implementation writer at a time. Use actual configured GPT-6-Luna/high implementer, separate reviewer and repairer roles after verifying the coordinator/model prerequisites. Do not silently substitute models. | Task | Owner | State | Files and deliverable | | --- | --- | --- | --- | | M17.1 | implementer | complete for implementation scope | `src/tui/render.rs`: adaptive wide/medium/compact columns, growing Name, host address columns and route/inspection markers; focused checks passed | | M17.2 | implementer | complete for implementation scope | `src/tui/render.rs`, `src/tui/state.rs`, `src/tui/terminal.rs`: dimension-only panes, shared visible-row/selection resolver, draft removal and duplicate-key navigation; focused checks passed | | M17.3 | implementer | complete for implementation scope | Explicit Draft/Saved/Applied and scoped issue/count/detail text in TUI, saved peer count from installed bytes, README help and synthetic legacy workflow; focused checks passed | | M17.4 | independent reviewer; repairer | complete | R1 loading-state and R2 Clippy findings repaired by separate repairer and independently closed | | M17.5 | coordinator | complete | Integrated gates passed on fingerprinted inputs; [M17 evidence](docs/evidence/milestone-17.md), acceptance and resume update | 1. Establish one deterministic layout policy based on terminal dimensions, not selected content. Wide mode (initial target at least 140 columns and 20 rows) places the list left and details right, with a useful detail minimum of 48 interior columns and the remaining width assigned to the list. Normal narrower mode (at least 80 columns and 24 rows) places a fixed-height detail pane below the list, reserving at least five visible client rows. At smaller sizes prioritize a usable list, show a concise selected-client summary where it fits, and retain Enter for full details. Keep the existing 34 x 8 minimum guard and very-short behavior. Final breakpoint constants may be adjusted during implementation only to meet measured cell budgets; record them and their boundary tests. Pane heights must not change with label, issue or route length. 2. Allocate table widths from the list pane's actual inner width, reserving borders, selection marker and gaps. Prioritize Name and change state, keep shortened public identity visible where feasible, and use separate IPv4/IPv6 columns when they fit. Give Name a growing share instead of a fixed 13-cell cap. Remove the repetitive Mode column; retain an explicit text marker for inspection-only peers. At constrained widths progressively compact/hide address and key columns with full values available in details. Truncate by display cells with an ellipsis; escape control text. Never treat a subnet as a client's host address; list a route summary/marker and preserve all routes in details. 3. Reuse a shared selected-record resolver and detail content for the persistent pane and Enter screen. Highlight and pane must refer to the same first row even before navigation. Up/Down continue selecting clients; Enter opens scrollable full details, Esc returns with selection retained. Preserve identity across accepted refreshes, handle disappearance and empty views, and inspect ambiguous/malformed rows by document position without enabling mutation. Include draft removals in the unified visible-row model so a row and its details remain reachable. Document issue rows must never be treated as client action targets. Persistent details show full label/key, all routes, draft/saved facts, global apply verification, available handshake/counters, and specific management limitations. Long content uses a visible continuation instruction to Enter; no new focus shortcut is required. 4. Replace Debug-style global state text with explicit labels: Draft: no changes / pending; Saved: clearly defined peer and managed-client counts; Applied: verified / not verified / observation unavailable according to existing state. Clarify that global verification refers to saved configuration, not a current draft. Use stable text markers for draft added/changed/removed and inspection limitations, with colors supplementary. A saved row is not proof of applied access; handshake presence is not apply verification. Unknown must not be displayed as pending restart unless saved-versus-applied divergence is known. Metadata/vault-only drafts must still appear pending even if semantic client-change count is zero. If necessary, add a minimal public projection of existing facts; do not infer saved membership from the draft document or add a new durable model. 5. Define count labels and issue accounting explicitly: distinguish visible/draft peers, saved peers where known, managed clients, document/peer problems and capability limitations. Count each chosen category consistently; do not report a capability limitation as corruption. If existing projections cannot supply a count correctly, use an accurately scoped label instead of inventing a total. Explain inspection-only reasons in details using current capability rules. #### Acceptance and validation - [x] M17-A1: wide buffers use extra width for names; long Latin/Cyrillic/wide-character labels remain aligned, with full identity and values accessible in details. Address columns and route markers are accurate for dual-stack, IPv4-only and subnet-route peers. - [x] M17-A2: persistent side/bottom details update with the highlighted row; empty/loading, first render, duplicate aliases/keys, malformed peers, document-only issues, draft removals and refresh/removal do not select or authorize the wrong client. - [x] M17-A3: at fixed terminal dimensions, selection, long notices/routes, action availability and periodic statistics do not change pane or footer geometry. Resizing between wide/stacked/compact modes keeps selection, navigation and content reachable without overlap, wrapping table rows or out-of-bounds rendering. - [x] M17-A4: tests distinguish no draft, added/renamed/deleted drafts, metadata/vault-only draft, saved-but-unverified state, known unapplied changes if available, verified saved state with a new draft, unavailable observations and refresh without apply verification. No saved/handshake/inspection marker falsely claims active access or saved absence. - [x] M17-A5: full details explain capability limits separately from actual document/peer errors, display all routes and existing handshake/RX/TX observations, preserve secret-free escaping, and use consistent scoped counts including nonmanaged peers. - [x] M17-A6: Enter/Esc and existing actions/forms/QR retain behavior; standard footer remains two hint rows plus borders. Independent review findings are repaired and rechecked, and all focused and Rust gates pass on integrated inputs. Planned synthetic buffer matrix: 34x8, 40x9, 60x16, 80x24, 100x30, 139x40, 140x40, 180x50 and 220x50, plus final breakpoint minus/exact/plus dimensions. Use representative secret-free fixtures for state combinations and long content; assert visible semantics, selection and geometry rather than snapshots containing payloads. Add a synthetic workflow/PTY resize scenario only where buffer tests cannot verify navigation or terminal lifecycle. Validation: pinned Rust/Cargo 1.97.1, rustfmt 1.9.0 and Clippy 0.1.97; `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked`, `cargo build --locked --release` and `git diff --check` all exited 0 on the repaired inputs. Focused `cargo test --locked tui::` passed 71 tests; `cargo test --locked --test tui_workflow` passed 8. Full test suite passed 384 with 3 explicitly ignored. Exact source/artifact identities and synthetic/PTY limits are in [M17 evidence](docs/evidence/milestone-17.md). No production, Docker or VM checks were run for this presentation scope. #### Progress - [x] M17.1: adaptive table implementation and focused synthetic checks complete; coordinator inspected the actual `src/tui/render.rs` diff. - [x] M17.2: persistent pane and shared navigation implementation plus focused synthetic checks complete; coordinator inspected the actual state/terminal diff. - [x] M17.3: explicit state/count/detail implementation and focused synthetic checks complete; coordinator inspected the actual state/README/workflow diff. - [x] M17.4: independent review found R1; separate repairer fixed it and independent re-review closed it with 39 render tests and fmt/diff checks passing. - [x] M17.4: integrated Clippy reported R2 at two renderer closures; targeted repair and independent re-review closed it. Exact Clippy gate now passes. - [x] M17.5: integrated pinned Rust gates and release build passed; source/binary identities and limitations recorded in [M17 evidence](docs/evidence/milestone-17.md). Milestone accepted. - [x] User confirmed the active GPT-6-Sol/medium coordinator; configured GPT-6-Luna/high roles handled implementation, review and repairs in sequence. #### Review and repair | Finding | Iteration | Location and impact | Owner | Resolution and evidence | State | | --- | --- | --- | --- | --- | --- | | M17-R1 (P2) | R1 | `src/tui/render.rs`: loading list conflicted with persistent empty-detail message | repairer | Early loading branch plus 140×20/80×24 buffer regression; independent re-review closed, 39 render tests passed | closed | | M17-R2 (P2) | gate | `src/tui/render.rs:914,1141`: redundant closures failed `cargo clippy --locked --all-targets -- -D warnings` | repairer | Function items preserve semantics; exact Clippy gate passed and independent re-review closed | closed | #### Evidence and handoff Current evidence: [M17 evidence](docs/evidence/milestone-17.md) records fingerprinted source/release binary, 71 focused TUI tests, 8 workflow tests, 384 full-suite passes with 3 ignored, all pinned Rust gates and R1/R2 independent closure. Synthetic PTY coverage is distinct from manual terminal-emulator or real-system acceptance. #### Decisions and discoveries The user's requested subset is adaptive columns, persistent details and clear states. Existing detail observations can be reused without adding collection. A dedicated full-details screen remains necessary for long routes and small terminals. A global AppliedState cannot safely become an independent verified flag on every draft row. M16's user-directed acceptance is separate from M17 fixture evidence; no dependency or specification change is planned. #### Outcome Accepted for the presentation and synthetic fixture scope after independent review, R1/R2 closure and pinned Rust gates. No real WireGuard/systemd, Alpine, disposable-VM or manual terminal-emulator behavior is claimed. Stop after M17; do not commit, deploy, resume retired M9 VM work or access production. ### Milestone 18 — Readable, complete in-app help for implemented workflows Status: accepted (2026-10-29T00:00:18+02:00) after independent review, R1/R2 repair and re-review, and pinned Rust gates on final inputs. Evidence: [M18](docs/evidence/milestone-18.md). Prerequisites: M17 accepted; existing TUI and pinned Rust environment. The user previously confirmed this coordinator session is GPT-6-Sol/medium; project configuration and role definitions specify GPT-6-Luna/high for implementer, reviewer and repairer. M9 remains retired by user direction. #### Outcome and scope Replace the `?` screen's dense prose with a readable English guide to the application, its implemented functions, and exact keyboard workflows. The guide must describe actual shipped behavior, including M17 adaptive list/details and existing footer, rather than planned features. Follow [SPEC](docs/SPEC.md), [CONTEXT](CONTEXT.md), all three [ADRs](docs/adr/), and development/testing/execution-record rules. Do not change product requirements or claim real-system validation from fixture evidence. Scope: help content, styling, responsive layout, help navigation, focused tests, and synchronization of relevant user-facing documentation. Preserve actions, authorization, drafts, saved/applied semantics, forms, QR privacy and the stable footer. No new management commands, dependencies, runtime transport work, search/sorting, localization system or application log/history feature. CLI `--help` is not the target of this milestone. #### Context and implementation steps At the start of M18, `src/tui/render.rs::render_help` rendered a single literal containing long paragraphs and an inline shortcut list. The draw branch called it without UiState; `src/tui/terminal.rs` changed `scroll_offset` for Help but the renderer did not consume it, and page keys moved one line. Help footer hints omitted scrolling. The implementation corrected these behaviors after tracing current terminal/form handlers and guards. Read before dispatch: AGENTS.md, docs/SPEC.md, CONTEXT.md, docs/adr/0001-explicit-restart.md, docs/adr/0002-client-secret-storage.md, docs/adr/0003-server-config-authority.md, docs/IMPLEMENTATION.md, docs/EXECUTION_RECORDS.md, docs/DEVELOPMENT.md, docs/TESTING.md, docs/ENVIRONMENT.md, docs/DEPENDENCIES.md, docs/PROJECT_LAYOUT.md and docs/CODEX.md. Coordinator owns plan and acceptance. Use one implementation writer, configured GPT-6-Luna/high implementer, a separate reviewer, and repairer for actionable findings; verify actual model prerequisites without silent substitution. | Task | Owner | State | Owned scope and deliverable | | --- | --- | --- | --- | | M18.1 | implementer | complete | Read-only handler/form/guard audit; shortcut inventory in [evidence](docs/evidence/milestone-18.md) | | M18.2 | implementer | complete | `src/tui/render.rs`: nine structured sections, styled/wrapped visual rows, row-range title, Help footer and shared navigation metrics | | M18.3 | implementer | complete | `src/tui/terminal.rs`, tests and README: bounded visual-row/page scrolling, resize, reset/Esc; synthetic PTY Help flow | | M18.4 | independent reviewer; repairer | complete | M18-R1 and R2 repaired by separate repairer and independently re-reviewed closed | | M18.5 | coordinator | complete | Integrated gates passed on R2 repair; final source/binary identities and limits in [M18 evidence](docs/evidence/milestone-18.md); accepted | 1. Inventory actual global, screen-specific and form-specific shortcuts and their availability conditions. Cover `?`, arrows, Enter, Esc, q/handled Ctrl-C; n/r/d; p/c/x/R; o/O; b/B; s/i/!. Verify exact setup/import/delivery/confirmation behavior in forms instead of guessing field navigation. Explicitly distinguish lowercase/uppercase pairs. A help entry documents existing availability; it must not enable an action where it was previously unavailable. 2. Organize content with a short application introduction and these clearly separated sections: quick start; list and selected-client details; Draft/Saved/Applied states and statistics; client creation/rename/deletion; preview/Commit/discard/explicit restart; first delivery/password reissue/file and optional QR; backup/previewed restore; vault setup/import/anomalies; navigation, cancellation and exit. Each action gets a visible key, short purpose, prerequisites where applicable, and expected result. Include concise walkthroughs: create -> preview -> Commit -> first delivery -> explicit restart (delivery warns until verification), change/delete -> Commit -> restart, reissue with password, and backup selection -> restore preview -> confirmation -> separate restart when desired. 3. Explain that aliases may repeat and public keys identify clients; inspection-only restrictions differ from malformed configuration. Explain unknown/unavailable/verified state without implying that Saved or a handshake means verified access. Handshakes and RX/TX are current read-only observations, not online status or historical totals. Commit and restore save files only; mandatory backups stop protected actions on failure; deleted addresses remain reserved until verified restart. Explain per-client password reissue, first-delivery retry within its creation session, original imported-file preservation, masked password entry and Esc clearing explicit QR disclosure. Use concise practical language, not internal Rust enums, architecture details or promises about unsupported features. 4. Render section headings, whitespace, aligned key/action entries and wrapped explanatory text with theme-safe styles. Colors supplement structure. Prefer a single ordered vertical document, avoiding dense multi-column prose on wide screens. Use display-cell widths and the actual inner viewport; long explanations must wrap without key labels colliding. Keep essential help navigation visible in the existing fixed-height footer, with a position/continuation indicator when content exceeds the viewport. Do not expand the footer or move the list layout based on help length. 5. Wire Help to its scroll position. Calculate wrapped visual rows consistently for rendering, maximum offset and page movement; clamp at both ends, prevent scrolling to a blank page, and clamp after resize. Up/Down move one visual row, PageUp/PageDown move a viewport page with useful overlap if chosen and documented. Opening `?` starts at the top; Esc returns to the client list preserving the selected client and drafts. Reset/clamp help position without leaking it into other screens. Avoid unsolicited changes to non-Help scrolling or action dispatch. Every section must remain reachable on supported small terminals; retain the existing terminal-too-small guard. #### Acceptance and validation - [x] M18-A1: content accurately describes the implemented application and all listed shortcut families; inventory maps entries to current handlers/guards. R2 completed required state/password/delivery explanations and independent re-review closed it. - [x] M18-A2: introduction, section headings, key/action entries, workflow steps and warnings are visually distinguishable in wide and narrow synthetic buffers; key case is unambiguous and text is neither overlapped nor silently inaccessible. - [x] M18-A3: scrolling visibly changes the rendered Help, reaches the last section, stops at valid bounds, pages by viewport rather than one line, and remains valid after shrinking/growing the terminal. Footer accurately advertises available Help navigation. - [x] M18-A4: `?` resets Help to its beginning and Esc restores the list with the same selected identity and unchanged draft/saved/applied state. Opening, reading and closing Help performs no file, vault, service or runtime mutation. Existing action/form/QR behavior and fixed two-row footer remain intact. - [x] M18-A5: cover 34x8, 40x9, 60x16, 80x24, 100x30 and 180x50 with synthetic buffers, plus focused navigation/workflow checks. Verify all help sections can be reached; prefer semantic/geometry assertions over brittle full-document snapshots. Fixtures contain no production keys, payloads or QR matrices. - [x] M18-A6: independent content/implementation review closed R1/R2; focused tests and required integrated Rust gates passed on final inputs, with evidence and user documentation updated. Validation: pinned Rust/Cargo 1.97.1, rustfmt 1.9.0 and Clippy 0.1.97; `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked`, `cargo build --locked --release` and `git diff --check` all exited 0 on final inputs. Focused `cargo test --locked tui::` passed 79; `cargo test --locked --test tui_workflow` passed 8. Full suite passed 392 with 3 explicitly ignored. Exact source/artifact identities and synthetic/PTY limits are in [M18 evidence](docs/evidence/milestone-18.md). No Docker, root, VM or production checks were run for this presentation milestone. #### Progress - [x] M18.1: current shortcut/form/guard audit completed without edits; content inventory recorded in [evidence](docs/evidence/milestone-18.md). - [x] M18.2: structured Help rendering and focused buffer tests completed in `src/tui/render.rs`; coordinator inspected changed-file scope and the navigation-metrics interface. - [x] M18.3: visual-row/page scrolling, bounds, resize, `?` reset, Esc preservation and synthetic PTY Help flow completed; coordinator inspected terminal/README/workflow diff. - [x] M18.4: independent review found R1 in the narrow footer; coordinator audit and reviewer confirmed R2 content gaps. A separate repairer fixed both, and independent re-review closed both with focused regressions. - [x] M18.5: final integrated pinned Rust gates and release build passed; refreshed source/binary identity and limitations are in [M18 evidence](docs/evidence/milestone-18.md). Milestone accepted. #### Review and repair | Finding | Iteration | Location and impact | Owner | Resolution and evidence | State | | --- | --- | --- | --- | --- | --- | | M18-R1 (P2) | R1 | `src/tui/render.rs`: 34×8 compact Help footer omitted Esc return key | repairer | Help-specific narrow packing keeps scroll/Esc and honest overflow; 34×8/40×9 regression passed; independent re-review closed | closed | | M18-R2 (P2) | coordinator audit | `src/tui/render.rs::help_document`: required password, observation and delivery-warning explanations were incomplete | reviewer; repairer | Four claims and 34×8 reachability test added; focused test passed and independent re-review closed | closed | #### Evidence and handoff Final [M18 evidence](docs/evidence/milestone-18.md) records shortcut audit, fingerprinted source/release binary, 79 focused TUI tests, 8 workflow tests, 392 full-suite passes with 3 ignored, all pinned Rust gates and independent R1/R2 closure. Synthetic PTY coverage is distinct from manual terminal-emulator or real-system acceptance. #### Decisions and discoveries A more detailed guide requires functional scrolling as well as rewritten text. Keep application UI/help in English, as required by AGENTS.md. Reuse current public presentation facts and existing widgets rather than introducing a documentation framework. Describe actual action guards and cancellation semantics; help is guidance, not a second implementation of authorization rules. #### Outcome Accepted for presentation and synthetic fixture scope after independent review, R1/R2 closure and pinned Rust gates. No manual terminal-emulator, Alpine, disposable-VM, live WireGuard or production result is claimed. Stop after M18; do not commit, publish, deploy, access production or resume VM work. ## Approved domain refactoring programme — milestones 19–27 Status: M19–M20 accepted; M21 in progress; M22–M27 pending. The user approved the proposed direction and requested detailed milestones on 2026-10-29. This paragraph records that plan's origin; current task progress is in the milestone records and resume point below. M18 remains accepted. These milestones preserve the approved product contract and all existing acceptance limitations, including retired/unaccepted M9 and unverified real-system paths. They do not resume VM work. ### Shared execution contract for M19–M27 Read before each dispatch: AGENTS.md, docs/SPEC.md, CONTEXT.md, the three docs/adr/0001–0003 decisions, docs/PROJECT_LAYOUT.md, docs/DEVELOPMENT.md, docs/TESTING.md, docs/ENVIRONMENT.md, docs/DEPENDENCIES.md, docs/CODEX.md, docs/IMPLEMENTATION.md, docs/EXECUTION_RECORDS.md, this section, the active milestone and its predecessor's evidence. Read only relevant historical evidence/diagnostics when a concrete question requires it. The coordinator must use the configured model/effort prerequisites; no silent substitution. Implementer/reviewer/repairer are separate GPT-6-Luna/high agents. One writer at a time; coordinator owns PLANS.md, task boundaries, findings and acceptance. Each table row is a bounded dispatch, not a request to implement the whole programme. Supply exact owned files, relevant interfaces, prerequisites and expected checks to every agent. Inspect the current tree before dispatch because paths below describe planned destinations, not modules already present. **Fixed constraints.** No product/UI workflow, shortcut, persisted schema/version, archive member, default path, cryptographic primitive, authentication policy, dependency, Cargo feature or transport behavior change. No new crates/workspace, generic repository framework, event sourcing, CQRS infrastructure, plugin system, speculative traits or arbitrary file-size targets. Concrete internal owners and narrow functions are preferred. Keep byte-preserving editing in wireguard, secure I/O and recovery in their adapters, and temporary secrets out of public projections/Debug/errors. Do not change Cargo.toml/Cargo.lock for this programme. If a correctness defect or requirement conflict appears, record it and isolate affected work; do not hide a behavior change in a move. Existing external API/test consumers must be inventoried before removing a public escape hatch. Retain a narrow forwarding API where needed during migration; remove it only after all actual callers have migrated. **Target responsibilities.** Names are working names to finalize in M19. `ServerPeerRecord` means an authoritative document record, potentially malformed/ambiguous/inspection-only; it is not a second independently mutable registry. The existing `Client` remains a restricted management projection, not a universal representation of every WireGuard peer. `RuntimePeer` remains an observation. `DraftChanges` is a boundary for coordinated document/client/vault edits, not a pure-domain holder of filesystem or crypto operations. `AddressReservations` owns reservation rules. `ApplyStatus` describes present knowledge; historical verification and raw observations remain distinct. `FirstDeliverySession` owns creation-session eligibility and short-lived secrets. Pure rules belong in domain; use cases coordinate adapters. The server document remains authoritative. **Preservation matrix (required throughout).** Check creation/rename/delete/discard/Commit; IPv4-only and dual-stack clients; duplicate aliases; metadata-free, subnet-route, missing-IPv4, malformed and duplicate-identity peers; import anomalies and mismatches; original file preservation; mandatory backup failure; partial install/recovery; restore cancellation/stale preview/settings path restrictions; first-delivery retry/overwrite/cleanup/publication uncertainty; password-per-client reissue; shared restart deadline, service submission uncertainty, complete-peer verification and durable publication failure; reservation release only after verified restart; reopening does not infer current application from historical success; responsive worker cancellation/shutdown, generation checks and explicit QR disclosure. Preserve successful behavior and honest failures, not just function signatures. **Validation profiles.** `DOC` means inspect Markdown local links/anchors, register/status/resume consistency, task numbering and factual claims, then `git diff --check`. `RUST` means environment checks in docs/ENVIRONMENT.md, focused existing behavioral tests for owned responsibilities, followed by `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked`, `cargo build --locked --release`, and `git diff --check` on final integrated inputs. Commands assume the verified pinned-toolchain environment; do not use a different system Cargo silently. Expected outcome is exit 0; record actual counts/ignored tests rather than copying M18 results. Do not run full gates for every diagnostic edit. Use existing test adapters and synthetic fixtures; add tests only for uncovered behavioral boundaries, not private field names or forwarding methods. Existing tests may move with their owner, but do not weaken/delete assertions to make the refactor pass. Review final production paths independently of test results. **Evidence and acceptance.** At each milestone, coordinator creates/updates docs/evidence/milestone-N.md with source identity including dirty inputs, commands and results, acceptance mapping, independent review and limitations. Create docs/diagnostics/milestone-N.md only when needed. Review findings use stable M-R IDs with location, impact, expected behavior and reproduction; repair and re-review before acceptance. No claims from commands not run. Root, production keys/configs/services and live VPN testing are forbidden. Alpine is separate opt-in evidence and never systemd evidence; unavailable prerequisites remain incomplete validation. A public item is not made private merely to satisfy a structural goal without checking binary, integration tests and container-driver callers. Update docs/PROJECT_LAYOUT.md when actual responsibilities move; keep CONTEXT.md a glossary, without module paths or implementation notes. ### Milestone 19 — Refactoring contract, terminology and behavior map Status: accepted (2026-10-29T22:24:30+02:00). Prerequisite: M18 accepted; no code refactoring was performed in this milestone. #### Outcome and scope Provide a precise migration contract so subsequent workers can perform bounded changes without inventing domain semantics. Inspect current source and test callers; documentation only. Planned owned files: CONTEXT.md, docs/architecture/domain-refactoring.md (new, created for substantive content), docs/evidence/milestone-19.md. Coordinator alone updates PLANS.md. Do not rewrite SPEC/ADRs or historical evidence. #### Tasks and implementation steps | Task | Owner | State | Bounded deliverable | | --- | --- | --- | --- | | M19.1 | implementer `/root/m19_inventory` | complete | Inventoried authority, application/domain/TUI ownership, test callers and public escape hatches; no edits or tests | | M19.2 | implementer `/root/m19_inventory` | complete | Added glossary and source-referenced architecture contract with transitions, ownership, caller audit and M20–M27 test map; no code changes | | M19.3 | fresh reviewer `/root/m19_reviewer`; coordinator | complete | R1–R6 closed by independent re-review; integrated DOC passed; M19 accepted | 1. Trace src/domain.rs, src/app.rs, src/runtime.rs, src/wireguard/{document,edit}.rs, src/persistence.rs, src/backup.rs, src/vault{,/import,/source}.rs, src/delivery.rs and src/tui/{state,events,forms,terminal,render}.rs. Include startup.rs, lib.rs, container_driver.rs and tests as API consumers. Distinguish production lines from colocated tests; file length alone is not a reason to split. 2. In the architecture document, make a table: current type/function/field -> business meaning -> authoritative source -> proposed owner -> readers/writers -> existing tests -> migration milestone. Cover Application fields, ClientStates fields/mutators, display/projection logic, authorization contexts and worker generations. Use verified source references. Record public escape-hatch callers (`draft_mut`, `vault_draft_mut`, `add_client`, `open`) and whether they can be replaced safely. 3. Add glossary entries for saved state, applied state/verification, server peer record, manageable client, runtime observation, reserved address and first delivery. Preserve existing meanings and avoid introducing user/account identities. Explain that a malformed peer can lack a usable public key and must still be inspectable; a valid subnet-route peer can be restart-verifiable without being manageable. Unknown creation dates remain unknown. Implementation names belong in the architecture document only. 4. Write transition tables with trigger, preconditions, next state, side effects, failure/cancel outcome and existing proof. Cover draft lifecycle, reservation creation/release, current vs historical verification, first delivery stages (including publication uncertainty/manual cleanup), restore preview/confirmation and restart attempt identity. Distinguish cancellation before execution from a submitted service job that may still finish. 5. Agree concrete destination modules and dependency direction; prefer domain child modules, app child modules and an application-owned read model. Record incremental API compatibility/removal strategy, persisted-format compatibility fixtures, gaps needing focused tests, and responsibility-specific test commands discovered from actual test names. No empty modules or architecture-only traits. #### Acceptance and validation - [x] M19-A1: every proposed owner/transition maps to actual source and tests; unsupported peers and historical verification are explicitly covered, with the unprojectable-peer restore gap recorded for M23. - [x] M19-A2: glossary is domain-only; architecture map specifies authority and prevents an independent client registry. - [x] M19-A3: each later milestone has an unambiguous migration surface and existing behavioral baseline; no product changes or new dependency are implied. - [x] M19-A4: separate reviewer checked actual source, R1–R6 closed; integrated DOC passed. Review and repair: M19-R1 (P2, restore state) closed by corrected rewrite/history contract and re-review. M19-R2 (P2, historical M18 handoff) closed by plan clarification/re-review. M19-R3 (P2, unprojectable-peer restore reservations) closed by documenting current behavior, focused test gap and M23 repair under the existing requirement; re-review confirmed. M19-R4 (P3, programme/M17 handoff) closed by plan clarification/re-review. M19-R5 (P3, stale M19.3 task row) closed by correction and re-review. M19-R6 (P2, future task/test-map fidelity) closed after tuple arity and actual test targets were aligned and independently rechecked. Evidence/handoff: [M19 evidence](docs/evidence/milestone-19.md) identifies the dirty source inputs, exact DOC checks, reviewer dispositions and limitations. The user confirmed the coordinator uses GPT-6-Sol/medium; workers were dispatched with GPT-6-Luna/high. M18 is accepted; M9 is retired/unaccepted and is not an M19 prerequisite. No Rust tests/build, Alpine or VM checks were required or run for this documentation-only milestone. Outcome: accepted for the migration contract; next action, only on a separate request, is M20.1. ### Milestone 20 — Explicit vocabulary and named operation contexts Status: accepted (2026-10-29T22:40:28+02:00). Prerequisite: M19 accepted (2026-10-29T22:24:30+02:00). Coordinator model GPT-6-Sol/medium confirmed earlier in this session by the user; worker dispatches selected GPT-6-Luna/high explicitly. Initial tree clean at `ede45d224a9ad255a919900e556a03d7e575430f`; pinned Rust/Cargo 1.97.1, rustfmt 1.9.0 and Clippy 0.1.97 used. #### Outcome and scope Remove name ambiguity and opaque tuples without moving business rules. Planned owners: src/domain.rs and optional domain child modules, src/runtime.rs, src/app.rs, src/tui/{events,state}.rs, actual callers/tests, docs/PROJECT_LAYOUT.md. Keep public forwarding names only where the M19 caller map justifies them. | Task | Owner | State | Bounded deliverable | | --- | --- | --- | --- | | M20.1 | implementer `/root/m20_vocab` | complete | Renamed domain knowledge to ApplyStatus, removed duplicate TUI enum and unnecessary observation aliases, migrated callers; focused tests passed | | M20.2 | implementer `/root/m20_vocab` | complete | Replaced prepared-operation tuples with private named contexts/mode; focused behavior and workflow tests passed | | M20.3 | fresh reviewer `/root/m20_review`; repairer `/root/m20_repair`; coordinator | complete | M20-R1 repaired and closed on independent re-review; final integrated RUST gates passed | 1. Rename the domain `RuntimeObservation` (currently Unknown/Unavailable/Verified) to the M19 canonical knowledge name, such as `ApplyStatus`; retain runtime::RuntimeObservation for measured interface data. Decide whether tui::AppliedState is a necessary presentation mapping or an exact redundant copy. Remove only redundant representation; do not merge raw statistics and verification status. Inventory every `as Statistics`/`as TechnicalObservation` alias and change only aliases made unnecessary by clarified names. 2. Replace ApplicationExecutor.prepared_destination's six-element tuple with named fields for identity, QR choice, delivery mode, retry attempt, generation and PreparedDestination. Replace restore_preview's tuple similarly. Preserve secret ownership, clear-on-unrelated-command behavior, stale-generation refusal, overwrite confirmation and cancellation. Avoid adding Clone/Debug to secret-bearing types. 3. Introduce only justified value types from M19 (for example a typed delivery mode replacing `reissue: bool` inside the prepared context). Migrate constructors/pattern matches/callers atomically for each type; do not redesign public commands or retry flags in this milestone. 4. If splitting domain.rs, move coherent definitions/tests mechanically, preserve exports needed by callers and keep implementation separate from semantic edits. Inspect diff for changed branches/order/errors; those belong to later milestones or separately recorded defects. #### Acceptance and validation - [x] M20-A1: present apply knowledge and measured observation have distinct canonical names; UI labels retain their meaning. - [x] M20-A2: prepared contexts use named fields; generation/cancel/confirmation behavior and secret redaction remain equivalent. - [x] M20-A3: actual consumers compile, dependencies/formats untouched; focused worker/state tests and final RUST gates passed after independent review. Review and repair: Fresh independent source review found no behavior findings. M20-R1 (P2, `src/app.rs` test and `src/container_driver.rs` test-only path) is closed: repairer replaced two `clone_on_copy` calls with dereferences; independent re-review and affected tests/Clippy passed. Evidence/handoff: [M20 evidence](docs/evidence/milestone-20.md) records the clean base, dirty source patch/artifact fingerprints, focused tests, R1 closure, exact final checks and limits. Final coordinator commands `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked` (391 passed, 3 ignored plus 2 doctests) and `cargo build --locked --release` all exited 0; `git diff --check` passed. Outcome: accepted for host/synthetic scope; stop after M20. Next action only on a separate request is M21.1; no fresh Alpine/VM result is claimed. ### Milestone 21 — Domain ownership of reservations and apply knowledge Status: accepted (2026-10-29T23:00:09+02:00; independent M21-R1 re-review and integrated Rust gates passed). Prerequisite: M20 accepted (2026-10-29T22:40:28+02:00). The user confirmed this session's coordinator model GPT-6-Sol/medium earlier; dispatches selected GPT-6-Luna/high explicitly. Initial tree clean at `8413b382b178991d2e1e597f1422d147f3454581`; pinned Rust/Cargo 1.97.1, rustfmt 1.9.0 and Clippy 0.1.97 available. M9 is retired/unaccepted by user direction and is not an M21 prerequisite. #### Outcome and scope Encapsulate address retention and verification transitions without changing persistence or technical matching. Planned production owners: src/domain.rs, src/domain/{reservations,apply}.rs as needed, app.rs and persistence.rs caller migration. Runtime matching stays in runtime; serialization stays in persistence. Creation-session secret state is out of scope. | Task | Owner | State | Bounded deliverable | | --- | --- | --- | --- | | M21.1 | implementer `/root/m21_reservations` | complete | Domain-owned committed/pending reservation sets, exact-host policy and allocator/Commit/discard integration; focused tests passed | | M21.2 | implementer `/root/m21_reservations` | complete | Explicit current/historical verification transitions and caller migration; focused tests passed | | M21.3 | reviewer `/root/m21_reviewer`; repairer `/root/m21_repair`; coordinator | complete | M21-R1 closed on independent re-review; integrated RUST and diff check passed | 1. Extract `AddressReservations` with private storage and intention-revealing operations to retain removed host addresses, restore conservative persisted reservations and expose read-only iteration/availability. Preserve conflict rejection, draft unsupported-peer reservations, Commit timing, discard behavior, IPv4/IPv6 allocation and broad-route deletion restrictions. Do not enumerate networks or infer free addresses from unavailable runtime data. 2. Keep draft-only pending reservations distinct from committed reservations until publication. Follow M19's transition table; preserve reservations from unprojectable exact host routes, and reject unsafe broad-route deletion as before. Move policy, not filesystem/parsing/crypto, into domain. 3. Separate current apply knowledge, optional last applied management snapshot and historical verified timestamp/projection. Preserve the valid state in which complete technical matching is verified but no complete management Snapshot can represent all peers. A single enum must not force a fake/partial snapshot. 4. Replace scattered field assignments with narrow transitions: saved state changed; runtime knowledge lost/unavailable; verified result durably published. Technical comparison success alone must not clear reservations or publish applied state before required internal-state persistence succeeds. Keep attempt authorization/checks private to the restart scenario until M25; do not expose an unrestricted public `mark_applied` method. 5. Migrate PersistedState conversion without changing TOML fields/version/order semantics or archive compatibility. Round-trip pre-refactor synthetic state, including reservations and historical timestamp. Reopen must restore conservative reservations and Unknown current knowledge. Keep existing failure paths intact. #### Acceptance and validation - [x] M21-A1: only named domain transitions can release committed reservations; backup/service/read/mismatch/persistence failures cannot release them. - [x] M21-A2: legacy technical verification without a management snapshot, reopening and current vs historical status retain prior semantics. - [x] M21-A3: allocator, reservation, persisted round-trip and app failure tests pass; independent invariant review and RUST pass. M21.1 and M21.2 focused checks passed. Independent reviewer found M21-R1: `commit_with_adapters` reset current apply status on unchanged Commit, changing prior Verified/Unavailable semantics. Repair removed that call and added three behavioral tests. Independent re-review closed M21-R1 with no remaining scoped finding. Final coordinator commands `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked` (399 passed, 3 ignored) and `cargo build --locked --release` all exited 0; `git diff --check` passed. [M21 evidence](docs/evidence/milestone-21.md) records final dirty source and artifact fingerprints, review and validation limits. Outcome: accepted for host/synthetic scope; stop after M21. Next action only on a separate request is M22.1; no fresh Alpine/VM result is claimed. ### Milestone 22 — Transactional draft editing and client creation vertical slice Status: accepted (2026-10-29T23:14:00+02:00; independent review and integrated Rust gates passed). Prerequisite: M21 accepted (2026-10-29T23:00:09+02:00). The user previously confirmed the coordinator GPT-6-Sol/medium session; project configuration and explicit dispatch selected GPT-6-Luna/high for workers. Initial tree clean at `2766c62`; pinned Rust/Cargo 1.97.1, rustfmt 1.9.0 and Clippy 0.1.97 verified. M9 is retired/unaccepted by user direction and is not an M22 prerequisite. #### Outcome and scope Give document/client/vault drafts one coordinated publication boundary and make create -> preview -> Commit readable end to end. Planned owners: src/app.rs, src/app/{draft,clients,import}.rs as warranted, necessary domain APIs, startup/test callers and focused tests. Preserve app::Application public entry points during extraction. Commit internals are refactored in M23; delivery internals in M24. | Task | Owner | State | Bounded deliverable | | --- | --- | --- | --- | | M22.1 | implementer `/root/m22_draft_owner` | complete | DraftChanges owns document/states/vault; staged replacement API; app (62), fmt and diff checks passed | | M22.2 | implementer `/root/m22_draft_owner` | complete | Create/rename/delete/discard and setup/import use staged publication; focused checks passed | | M22.3 | reviewer `/root/m22_review`; coordinator | complete | Independent review found no actionable issue; integrated RUST and evidence passed | 1. Build an internal `DraftChanges` owner containing the lossless document, restricted client projection, encrypted vault draft and draft-only reservation changes, or the concrete composition agreed in M19. It belongs to the application boundary because it coordinates crypto/document components. Avoid deep-cloning plaintext keys and avoid generic transactions/closures that obscure what is staged. 2. For each edit, prepare all fallible replacements locally, validate the edited document and corresponding public projection, then replace live draft components together. No file/runtime mutation is allowed. Document is authoritative: never rebuild it from Snapshot, and do not drop malformed/unsupported records merely because projection fails. 3. Migrate prepare_new_client first: resolve settings as currently required, calculate occupancy from full document plus reservations/server addresses, validate manual dual-stack addresses or allocate defaults, generate key, encrypt record, stage document/client/vault updates, and register session-first-delivery material only on successful publication. Preserve timestamp validation and key/error redaction. Keep first-delivery ownership temporarily behind its existing interface for M24. 4. Migrate add_client/rename_client/delete_client/discard_draft one at a time. Preserve alias validation/duplicates, metadata preservation, fallback labels, deletion restrictions, retained-address policy and precise revocation of session issue rights. Cancel/discard must restore saved data without writes or recreating consumed first-delivery eligibility. 5. Migrate initial vault setup and selected-file import through the same draft publication boundary; preserve all-or-nothing staging, key matching, anomaly reports and source-file preservation. Keep source scanning/reconciliation and encryption in existing adapters. Restrict `draft_mut`/`vault_draft_mut` only after replacing all real callers with intentional operations; tests must not be granted a production bypass. 6. Keep Application a facade delegating to cohesive owners; avoid moving the same large impl into a differently named catch-all file. Add focused failure tests only where no existing test proves that an error leaves every draft component and first-delivery eligibility unchanged. Update actual layout documentation. #### Acceptance and validation - [x] M22-A1: create/rename/delete/import/setup/discard use coordinated draft publication; intermediate errors leave no partial draft or newly authorized issue. - [x] M22-A2: source bytes outside allowed edits, existing client parameters, unsupported records and original import files remain preserved. - [x] M22-A3: synthetic create -> preview -> Commit -> first delivery -> explicit fixture restart and deletion/discard flows still pass; no live runtime operation occurs during draft/Commit. - [x] M22-A4: independent review checks authority/secret lifetimes; RUST passes with unchanged formats/dependencies. M22.1 added the internal owner and staged replacement API; M22.2 migrated assigned flows and narrowed draft mutation. Fresh independent review reran the five required focused suites and diff check and found no actionable issue. Final coordinator commands `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked` (401 passed, 3 ignored), and `cargo build --locked --release` exited 0; `git diff --check` passed. [M22 evidence](docs/evidence/milestone-22.md) records focused results, final dirty source and artifact fingerprints, review and limits. Outcome: accepted for host/synthetic scope; stop after M22. Next action only on a separate request is M23.1; no fresh Alpine/VM result is claimed. ### Milestone 23 — Readable saved-state Commit and restore scenarios Status: accepted (2026-10-29T23:33:11+02:00; independent M23-R1 re-review, M23-R2 evidence verification and integrated Rust gates passed). Prerequisite: M22 accepted (2026-10-29T23:14:00+02:00). The user previously confirmed this session's coordinator GPT-6-Sol/medium setting; project configuration and explicit worker dispatch selected GPT-6-Luna/high. Initial tree clean at `c1d4f93`; pinned Rust/Cargo 1.97.1, rustfmt 1.9.0 and Clippy 0.1.97 verified. M9 is retired/unaccepted by user direction and is not an M23 prerequisite. #### Outcome and scope Extract explicit saved-state scenarios with visible ordering and honest failure publication. Planned owners: src/app.rs, src/app/{commit,restore,backups}.rs, facade/private session interfaces, affected tests. Persistence journal, archive format, path security and low-level I/O algorithms stay in persistence.rs/backup.rs; only necessary call-site adjustments there. | Task | Owner | State | Bounded deliverable | | --- | --- | --- | --- | | M23.1 | implementer `/root/m23_commit` | complete | Commit prepare/validate -> backup -> install -> publish; focused checks passed | | M23.2 | implementer `/root/m23_commit` | complete | Backup listing/creation and preview-confirm restore extraction; exact-host reservation gap test/repair; focused checks passed | | M23.3 | reviewer `/root/m23_review`; repairer `/root/m23_repair`; coordinator | complete | M23-R1 closed on independent re-review; M23-R2 evidence verified; integrated RUST passed | 1. Extract Commit as readable preparation -> validate proposed saved bytes -> completed pre-Commit backup of installed state -> journaled install -> publish new session state. Keep a concrete prepared result if it clarifies which fallible work precedes mutation. Do not turn backup confirmation into a public forgeable permission or allow callers to skip it. 2. Preserve failure behavior: draft remains retryable, readback after failed install conservatively retains/revokes first-issue rights, saved/application history is not advanced on failure, and recovery on reopen follows the existing journal. Avoid an apparently infallible final publication that hides parsing or state-install errors. 3. Extract preview_restore and confirm_restore. Preview remains read-only and secret-safe; confirmation validates the bound saved generation/installation paths, creates precautionary backup and fully replaces the saved components (including absent optional components). Preserve draft warning/cancel and settings re-resolution. Restore never requests a vault password or touches the running interface. 4. Move manual/pre-restart backup entry points only as needed to establish a clear backup responsibility. Historical verification labels must refer to the archived generation, not the newest archive or current statistics. Keep cloned SavedBytes secret-bearing and out of ordinary read models/Debug. 5. Keep service/runtime dependencies unavailable to Commit and restore modules except shared public status types when necessary; reviewer must inspect actual dependency/call paths. Preserve existing callback failure seams with concrete narrow interfaces; do not add a general storage/service framework. #### Acceptance and validation - [x] M23-A1: completed backup precedes protected mutation for Commit/restore; failures and cancel retain documented draft/saved/runtime behavior. - [x] M23-A2: restore compatibility, path restrictions, absent components, recovery and historical status tests retain their assertions. - [x] M23-A3: fixture old-state backups and state/vault/archive compatibility checks pass; no runtime/service call in saved-only paths; independent review and RUST pass. M23.1 and M23.2 focused checks passed. Fresh reviewer found M23-R1: over-reservation of exact routes retained by an unprojectable restored peer. Repair separated archived from newly carried reservations; independent re-review closed M23-R1. M23-R2, the missing evidence artifact, was closed after the reviewer verified the report and source/artifact hashes. Final coordinator commands `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked` (404 passed, 3 ignored) and `cargo build --locked --release` exited 0; `git diff --check` passed. [M23 evidence](docs/evidence/milestone-23.md) records focused runs, final source, review and limits. Outcome: accepted for host/synthetic scope; stop after M23. Next action only on a separate request is M24.1; no fresh Alpine/VM result is claimed. ### Milestone 24 — Session-owned first delivery and password reissue scenarios Status: accepted (2026-10-30T21:24:34+02:00; independent security-path review, M24-R1 re-review and integrated Rust gates passed). Prerequisite: M23 accepted. M1–8 and M10–23 are accepted; M9 is retired by user direction and is not a prerequisite. The user confirmed this session's GPT-6-Sol/medium coordinator; `.codex/config.toml` specifies GPT-6-Luna/high workers, dispatched explicitly. Initial tree clean at `ed602f0`; pinned Rust/Cargo 1.97.1 environment verified. #### Outcome and scope Isolate the session-first-delivery state machine and expose readable authenticated delivery scenarios. Planned owners: src/app.rs, src/app/{first_delivery,reissue}.rs or cohesive delivery children; affected facade/TUI adapter callers and tests. Cryptography, secure publication and QR encoding remain in vault/delivery adapters. No workflow or password policy change. | Task | Owner | State | Bounded deliverable | | --- | --- | --- | --- | | M24.1 | implementer `/root/m24_state` | complete | Private `FirstDeliverySession` owns issue keys, contexts, stages, tokens and transitions; app/client-delivery tests, fmt and Clippy passed | | M24.2 | implementer `/root/m24_scenarios` | complete | First-file/QR and password reissue moved to cohesive modules; all five focused commands, fmt, Clippy and diff check passed | | M24.3 | reviewer `/root/m24_review`; coordinator | complete | No source findings; M24-R1 evidence gap closed on independent re-review; integrated RUST passed | 1. Move InitialIssue/IssueStage/IssueContext/token bookkeeping and matching/promote/retain helpers under one private owner. Keep ephemeral private keys in zeroizing/secret wrappers with no ordinary Clone/Debug/serialization. Use M19's transition table rather than simplifying PendingDraft/AwaitingFirstFile/RetryAvailable/Authorizing/overwrite/manual-cleanup states into one bool. 2. Expose narrow internal transitions for draft publication, successful Commit, discard/delete, changed peer/encrypted record, begin attempt and completed/failed publication. Tokens remain single-use, session-local, bound to client and saved context; no caller can manufacture a first-issue authorization. Preserve unrelated clients' rights when rename/restart does not invalidate their context. 3. Keep final saved-state authorization check adjacent to secure file publication with exclusive session ownership across the boundary. Extracting a component must not allow mutations between checking authorization and publishing a secret. Preserve retry to another directory and explicit overwrite/cleanup acknowledgements after uncertain failures. 4. Extract password reissue separately: selected client + password for this action -> decrypt/validate -> render -> mandatory file publication -> optional QR from the same payload. Never cache an unlocked vault or fall back to original plaintext files. Preserve imported parameters, discrepancy blockers and authoritative server values. 5. Preserve independent outcomes: file publication may succeed while QR fails; file may exist while bookkeeping is uncertain; warnings about not-yet-verified activation must survive failed/uncertain publication. Only successful mandatory publication consumes eligibility according to existing conservative uncertainty rules. Do not remove existing detailed failure variants or replace them with success/failure bools. #### Acceptance and validation - [x] M24-A1: all first-delivery transitions/invalidation rules have one owner; pre-Commit, stale, consumed and reopened sessions cannot deliver without authorization. - [x] M24-A2: retry/uncertain publication/manual cleanup/bookkeeping failure and same-payload QR behavior remain covered; passwords and secrets absent from errors/Debug/projections. - [x] M24-A3: reissue asks per client/action and respects import mismatch policy; independent security-relevant path review and RUST pass. Focused commands `cargo test --locked app::` (67), `delivery::` (22), `vault::` (24), `--test client_delivery` (5) and `--test tui_workflow` (8) exited 0 on the integrated tree. Final coordinator `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked` (library 337 passed, 2 ignored; integration/doc tests passed), `cargo build --locked --release`, and `git diff --check` exited 0. Fresh reviewer found no source issue. M24-R1: specified evidence file absent; coordinator created it and the reviewer independently verified all source/artifact hashes, check records and limits, closing the finding. [M24 evidence](docs/evidence/milestone-24.md) identifies inputs and host/synthetic scope. No fresh Alpine or VM/systemd run is claimed. Outcome: accepted for M24 host/synthetic scope. Stop after M24; M25 starts only on a separate request. ### Milestone 25 — Explicit restart verification scenario Status: accepted (2026-10-30T21:35:56+02:00; independent source review, M25-R1 evidence re-review and integrated Rust gates passed). Prerequisite: M24 accepted. M1–8 and M10–24 are accepted; M9 is retired and not a prerequisite. This session's coordinator GPT-6-Sol/medium was user-confirmed; `.codex/config.toml` and explicit worker dispatch use GPT-6-Luna/high. Initial tree clean at `7cef400`; Rust/Cargo 1.97.1 verified. #### Outcome and scope Make restart orchestration independently readable while preserving complete technical verification and every uncertainty boundary. Planned owners: src/app.rs, src/app/restart.rs and optional private restart children, facade/state transition interfaces, affected tests/container-driver imports. Netlink and systemd transports/decoders remain unchanged. | Task | Owner | State | Bounded deliverable | | --- | --- | --- | --- | | M25.1 | implementer `/root/m25_prepare` | complete | Preview/types and confirmed preparation through backup, attempt binding, optional discard and shared deadline moved to private restart owner; app/runtime-restart tests, fmt and Clippy passed | | M25.2 | implementer `/root/m25_sequence` | complete | Service/job, bounded observation, complete comparison and durable publication moved into restart owner; all five focused commands, fmt, Clippy and diff check passed | | M25.3 | reviewer `/root/m25_review`; coordinator | complete | No source findings; M25-R1 evidence gap closed on independent re-review; integrated RUST passed | 1. Move preview/report/choice/diagnostic types and restart implementation coherently. Preserve current public imports with re-exports until callers migrate. Preview validates unsafe SaveConfig, structural ambiguity and complete SavedPeerProjection; it must not require every saved peer to fit Client/Snapshot. 2. Read the scenario in this order: validate preview against saved state -> honor cancel/draft choice -> complete backup -> discard draft at existing safe point -> submit configured service restart -> successful job completion -> bounded interface observation -> compare complete saved peer set -> durable verification bookkeeping -> publish applied knowledge/release reservations. Preserve actual existing ordering where this outline omits preparation; document it instead of introducing behavior changes. 3. Keep one shared monotonic deadline, existing clock/failure injection, restart attempt identity, generation checks and stale/duplicate-result rejection. An accepted job request is not a completed job. Timeout/submitted/may-still-complete/manual-recovery reporting must not collapse into one generic error. 4. Keep complete-peer technical projection/comparison in its existing responsibility; management metadata/aliases must not become technical match requirements. Preserve metadata-free, IPv4-only, subnet-route and missing-IPv4 valid peers, malformed/duplicate identities, incomplete dumps and peer mismatch behavior. Current statistics must not assert verification. 5. Persistence failure after successful matching must retain conservative apply knowledge/reservations and honest report. Do not introduce rollback or a second restart. Fixture apply callback must remain cfg(test)-only and unreachable from the production executable. Review both real transport call paths for unchanged use, without executing host services. #### Acceptance and validation - [x] M25-A1: backup/job/read/verify/persist/publication sequence and cancellation semantics unchanged; current/saved generations and attempt identity remain bound. - [x] M25-A2: full-peer legacy cases, shared deadlines, service/transport distinctions, mismatch and post-verification persistence failure tests pass; reservations release only after durable verified success. - [x] M25-A3: no production fixture bypass, rollback or transport change; independent review and RUST pass. Real-system/IPv6 limits remain explicit. Focused commands `cargo test --locked app::` (67), `runtime::` (25), `service::` (26), `--test commit_restore` (9) and `--test runtime_restart` (3) exited 0 on the integrated tree. Final coordinator `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked` (library 337 passed, 2 ignored; integration/doc tests passed), `cargo build --locked --release`, and `git diff --check` exited 0. Fresh reviewer found no source defect. M25-R1: specified evidence file absent; coordinator created it and reviewer independently verified source/artifact hashes, check records and limits, closing the finding. [M25 evidence](docs/evidence/milestone-25.md) identifies inputs and host/fixture scope. No fresh Alpine or VM/systemd run is claimed. Outcome: accepted for M25 host/fixture scope. Stop after M25; M26 starts only on a separate request. ### Milestone 26 — Application read models and thin TUI command adapter Status: accepted (2026-10-30T23:01:09+02:00; independent R1–R4 re-review, evidence verification and integrated Rust gates passed). Prerequisite: M25 accepted. M1–8 and M10–25 are accepted; M9 is retired and not a prerequisite. This session's coordinator GPT-6-Sol/medium was user-confirmed; `.codex/config.toml` and explicit worker dispatch use GPT-6-Luna/high. Initial tree clean at `9a143e9`; pinned Rust/Cargo 1.97.1 verified. TUI previously reparsed saved config for peer count; app deletion already supports some exact-host-route peers outside manageable `ClientStates`. #### Outcome and scope TUI consumes public application facts and action eligibility without parsing saved configuration or owning authorization rules. Planned owners: src/app/read_model.rs, app facade, src/tui/{state,events,forms,terminal,render}.rs and new cohesive TUI children, focused tests. Preserve output wording/geometry/keys/help, fixed footer and worker lifecycle. | Task | Owner | State | Bounded deliverable | | --- | --- | --- | --- | | M26.1 | implementer `/root/m26_read_model` | complete | `ApplicationView` and per-action eligibility built from authoritative peer and session facts; app/TUI/workflow focused checks, fmt and Clippy passed | | M26.2 | implementer `/root/m26_tui_projection` | complete | TUI view consumes app read model; selection-bound guards/hints use per-action eligibility; TUI/workflow/client-delivery focused checks, fmt and Clippy passed | | M26.3 | implementer `/root/m26_command_adapter` | complete | Application command/prepared-context adapter extracted under events; TUI/workflow/client-delivery focused checks, fmt and Clippy passed; broad terminal/render split deferred as no cohesive gain | | M26.4 | reviewer `/root/m26_review`; repairer `/root/m26_repair`; coordinator | complete | M26-R1 removed unprojectable saved peer, R2 unrelated issue overblock, R3 false Removed row and R4 capability-issue reissue mismatch closed on re-review; evidence and integrated RUST verified | 1. Move ClientDisplay/SessionDisplay projection responsibilities to an application-owned read model. Include saved peer count, draft/saved public client differences, issue classification, reservation/apply facts and vault-initialized state. Keep UI-only selection, scroll, dialog focus, rendered strings/styles and worker generation in TUI. Raw ConfigDocument/SavedBytes/vault/password/payload must not be fields of the public view. 2. Represent action eligibility explicitly per action with typed reasons where useful. Audit current guards first: rename, delete, first delivery and restart do not necessarily have identical eligibility. In particular inspection-only management projection must not accidentally prohibit an already-supported deletion by public key. Never silently enable actions for malformed/ambiguous peers. Eligibility is guidance for presentation; application execution revalidates its actual preconditions. 3. Replace SessionView::from_application reparsing of saved bytes and recovery of business facts with the application view. Preserve unsupported peers without public keys, duplicate-identity records, removed-draft rows, selected public identity and safe peer-index inspection. Application view does not infer applied from handshake/counters or historical timestamp. 4. Extract ApplicationExecutor/prepared contexts/result adaptation from worker transport into a focused command adapter if it improves ownership. Keep the bounded single-owner worker, statistics generation filtering, prepared-preview/destination lifetime, cancellation, duplicate-submit protection, shutdown and terminal cleanup unchanged. No independent executor may share mutable Application or plaintext secrets. 5. Separate terminal lifecycle from key navigation/result messages only after the application boundary is clear. Split render.rs into cohesive list/details/help/footer modules if their responsibilities warrant it; preserve existing metrics, wrapping, QR disclosure and footer packing. Avoid generic dispatch registries or purely mechanical many-file splits. Each subtask must stay within one bounded responsibility and compile before the next begins. 6. Update internal help/user docs only for factual architecture references; no product wording/shortcut redesign. Keep masks and secret forms short-lived. Compare existing synthetic buffers and PTY workflows across established terminal dimensions, including 34x8, 40x9, 60x16, 80x24, 100x30 and 180x50. #### Acceptance and validation - [x] M26-A1: TUI no longer parses saved server bytes to obtain application facts; read model is secret-free and includes all authoritative records. - [x] M26-A2: per-action availability matches existing handlers, with authoritative revalidation; selection/cancel/draft/QR behavior preserved. - [x] M26-A3: worker generation/continuation/shutdown tests, responsive help/list/footer buffer tests and synthetic PTY workflows pass without weakened assertions. - [x] M26-A4: reviewer checks UI parity and boundary direction; RUST passes; actual module layout documented. Focused commands `cargo test --locked tui::` (88), `--test tui_workflow` (9), `--test client_delivery` (5), and affected `app::` (74) exited 0 on the integrated tree. Final coordinator `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked` (library 352 passed, 2 ignored; integration/doc tests passed), `cargo build --locked --release`, and `git diff --check` exited 0. Independent reviewer closed M26-R1 (removed unprojectable peer omission), R2 (unrelated issue reissue overblock), R3 (false Removed row from repair) and R4 (capability-issue reissue mismatch); all 11 evidence hashes and final gate results were rechecked. [M26 evidence](docs/evidence/milestone-26.md) identifies inputs, fixes and host/synthetic limits. No fresh Alpine or VM/systemd run is claimed. Outcome: accepted for M26 host/synthetic scope. Stop after M26; M27 starts only on a separate request. ### Milestone 27 — Integrated refactoring acceptance and architecture handoff Status: accepted (2026-10-30T23:21:10+02:00; independent R1–R4 re-review, A4 evidence check and final integrated Rust gates passed). Prerequisite: M26 accepted. Initial tree clean at `7123197`; pinned Rust/Cargo 1.97.1, rustfmt 1.9.0 and Clippy 0.1.97 verified. This session's coordinator GPT-6-Sol/medium was user-confirmed; project configuration and explicit M27 worker dispatch selected GPT-6-Luna/high. M1–8 and M10–26 are accepted; M9 remains retired/unaccepted by user direction and is not a prerequisite. #### Outcome and scope Verify the integrated refactoring against the original behavior map and demonstrate that common operations can be understood from their owning scenario. Owned scope: actual migrated APIs/tests, docs/architecture/domain-refactoring.md, CONTEXT.md, docs/PROJECT_LAYOUT.md and evidence. No new features or broad cleanup. Repairs must have named findings and bounded ownership. | Task | Owner | State | Bounded deliverable | | --- | --- | --- | --- | | M27.1 | implementer `/root/m27_implement` | complete | Reconciled architecture map, reading guide, glossary and layout; caller audit retained active fixture APIs; DOC checks passed; no Rust edits | | M27.2 | fresh reviewer `/root/m27_review` | complete | A2 passed on source inspection; M27-R1/R3 actionable documentation/comment findings and R2 plan consistency finding recorded | | M27.3 | repairer `/root/m27_repair`; reviewer; coordinator | complete | R1–R4 closed; six focused suites, final RUST gates and independent A4 evidence/plan check passed | 1. Compare every M19 source/test mapping with actual final owners. Remove obsolete forwarding APIs only after checking all binary, library, integration and container-driver callers. Remove stale milestone comments that incorrectly describe implemented paths as future work. Do not delete historical evidence or turn old verification limitations into new success claims. 2. Record a concise reading guide for create -> Commit -> first delivery -> explicit restart, delete -> Commit -> verified address release, and preview -> confirm restore. For each, name the entry point, rule owner, side-effect boundary and failure tests. A reviewer must be able to follow the scenario without discovering hidden UI authorization or independent metadata authority. Record justified exceptions instead of hiding them behind a size metric. 3. Independent reviewer audits the full preservation matrix and baseline-to-final diff, not only the latest milestone. Confirm persisted schemas/versions, archive members, cryptography/dependencies/defaults, public-key identity, byte-preserving authority, imported originals and short-lived secrets. Check for redundant mutable state and accidental widening of pub visibility. Existing fixtures from before the refactor must still load with unchanged meaning. 4. Run focused integrated tests and final RUST on identified final inputs. Establish which M8 Alpine evidence was invalidated by changed application inputs. If an explicitly authorized disposable Alpine environment is available, rebuild the current driver and follow docs/TESTING.md; otherwise record Alpine revalidation as incomplete, not a host-gate pass. No VM work is included, no systemd/IPv6 verification is inferred, and no release-readiness claim is authorized by this programme. 5. Accept the refactoring only for its documented host/synthetic scope after all required RUST checks and independent findings close. Report Alpine state separately; if any task explicitly made an Alpine check mandatory, keep that task incomplete until run. Final plan/evidence must state real-system limitations and identify the tested source. Stop after M27; do not commit/publish/deploy or launch another programme. #### Acceptance and validation - [x] M27-A1: all M19 mappings resolved to actual owners/tests; glossary/layout/reading guide agree with code; obsolete bypasses removed or explicitly justified. - [x] M27-A2: integrated independent audit finds no unresolved behavior, authority, authorization or secret-handling regression. - [x] M27-A3: preserved-format synthetic fixtures and all required host RUST gates pass on final source; exact outcomes and separate Alpine/VM limitations recorded. - [x] M27-A4: milestone register, outcomes and current resume point agree; evidence contains reproducible source identity and final review disposition. Review and repair: independent M27-R1 (missing `display_projection` caller audit), R2 (stale resume point), R3 (implemented paths described as future) and R4 (fixture-only public backup API) were repaired and re-reviewed. R4's first repair exposed a Clippy dead-code child helper; the bounded follow-up made both levels test-only, and final Clippy/full gates passed. Independent reviewer confirmed A4 after verifying evidence hashes, artifact, gates and plan consistency. [M27 evidence](docs/evidence/milestone-27.md) records source identity, exact host results and Alpine/VM limits. Outcome: accepted for host/synthetic refactoring scope. Handoff: stop after M27; no subsequent milestone is authorized by this plan. ## Current progress and resume point - M27 is accepted for host/synthetic refactoring scope after independent R1–R4 closure, six focused suites, final Rust gates and A4 evidence/plan verification. [M27 evidence](docs/evidence/milestone-27.md) identifies tested source and limitations. Stop here; no next milestone or deployment is authorized. Current Alpine revalidation and disposable-VM systemd checks remain incomplete; M9 remains retired/unaccepted. - M18 was accepted on 2026-10-29: structured, accurate in-app Help, responsive wrapping and bounded scrolling passed independent R1/R2 closure, synthetic/PTY checks and pinned Rust completion gates. [M18 evidence](docs/evidence/milestone-18.md) records final inputs and limits. At that handoff, M19 had not started. - M17 was accepted on 2026-10-28: adaptive columns, persistent selected-client details and explicit state/count presentation passed independent R1/R2 closure, synthetic/PTY checks and pinned Rust completion gates. [M17 evidence](docs/evidence/milestone-17.md) records final inputs and limits. At that handoff, M18 had not started. - TUI-FL-001 is complete (2026-10-27): fixed-height two-row keyboard footer, narrow Help/overflow behavior and compact nine-row handling passed synthetic rendering tests, independent R1–R3 closure and pinned Rust gates. [Footer evidence](docs/evidence/tui-footer-layout.md) identifies source/artifact inputs. - Milestone 16 is accepted by explicit user direction following a reported successful manual check. Earlier independent review/repair and pinned Rust gates passed; private-peer and both real transport paths still lack recorded disposable-VM evidence. [M16 evidence](docs/evidence/milestone-16.md) records the source and artifact. No production result is claimed by this execution. - Milestone 15 is accepted (2026-10-27) for synthetic service-adapter and application behavior: typed failure/phase/unit reporting, documented `LoadUnit` resolution, conservative uncertainty, independent R1/R2 closure and pinned Rust gates. [M15 evidence](docs/evidence/milestone-15.md) records source/artifact identity. Disposable-VM systemd acceptance remains incomplete and unclaimed. - Milestone 14 is accepted (2026-10-27) for fixture restart orchestration: complete saved-peer projection, legacy/IPv4-only/broader-route eligibility, safe blockers and durable verified history passed independent review and pinned Rust gates. [M14 evidence](docs/evidence/milestone-14.md) records source/artifact identity. No disposable-VM systemd acceptance or production result is claimed. - Milestone 13 is accepted (2026-10-27): compact, explicitly colored QR disclosure, complete quiet zone and dimensioned fallback passed synthetic rendering tests, independent R5 closure and pinned Rust gates. [M13 evidence](docs/evidence/milestone-13.md) records the reviewed source and release artifact. No manual scan, Alpine rerun, VM or production validation is claimed. - Bounded TUI maintenance TUI-TS-001 (2026-10-27): new-client form creation timestamps now have whole-second UTC precision before strict metadata rendering. A fixed fractional-second instant and a synthetic blank-IP form-to-draft workflow passed; saved bytes stayed unchanged. Independent review found no actionable issue, and pinned-toolchain Rust completion gates passed. [Timestamp repair evidence](docs/evidence/tui-creation-timestamp.md) identifies the source and release artifact. This repairs M7 behavior without claiming new Alpine or VM validation; earlier artifact identities remain historical after this Rust change. - Bounded TUI maintenance (2026-10-26): repaired list arrows opening client details. Up/Down now only move the highlight; Enter opens details and Esc returns. Initial navigation follows the renderer's first-row highlight. Independent review closed NAV-001 with no remaining scoped findings; validation is recorded in [navigation repair evidence](docs/evidence/tui-navigation.md). This does not advance M9 or supply VM/Alpine acceptance evidence; prior artifact identities remain historical after this Rust change. - Milestones 1–8 are accepted; milestone 9 is retired by user direction and unaccepted. Its real systemd/D-Bus checks remain unverified and are not a prerequisite to M10. - Milestone 10 is accepted: the styled keyboard quick reference passed independent review, focused synthetic/PTY checks and Rust completion gates. - Milestone 11 is accepted for valid but unsupported AllowedIPs route presentation; focused and full Rust gates and independent re-review passed. Real systemd/D-Bus and IPv6 behavior remain unverified. - Milestone 12 is accepted: native responsive client table, independent R4 closure and all scoped Rust gates passed; source and synthetic UI/PTY evidence are linked above. - The approved dependency list is in [DEPENDENCIES.md](docs/DEPENDENCIES.md); container testing methodology is in [TESTING.md](docs/TESTING.md). - Milestone 1 is expanded and the Rust environment is pinned. - Milestone 2 delivered a lossless in-memory WireGuard configuration editor, versioned metadata, and behavioral preservation tests. - Milestone 3 delivered secret-safe key handling, a versioned encrypted vault, caller-buffer reconciliation, and a bounded read-only source-directory import adapter. - Milestone 4 delivered versioned private backups, journaled saved-file Commit and recovery, previewed full restore, durable address reservations and historical verification bookkeeping, with independent R5 closure and coordinator gates. - Milestone 5 delivered public runtime observations and technical comparison, an approved-rustix bounded read-only netlink adapter, a zbus systemd job adapter, restart orchestration, and synthetic integration tests. Independent integrated review and repairs closed all actionable scoped findings; all planned gates passed. Real kernel/systemd validation remains deferred to M8/M9. - Milestone 6 delivered session-bound creation/first-file authorization, saved-state rendering and password-per-action reissue, secure plaintext file output, optional same-payload QR, and synthetic lifecycle tests. Independent integrated review and repair closed all actionable findings; all planned gates passed. TUI and real-system validation remain M7–M9. - M1–M6 integrated gates and independent reviews passed. No Alpine or VM results are claimed. - Milestone 7 delivered the native terminal workflow, safe startup/session projection, bounded single-owner worker, setup/import/draft/Commit/restart/delivery/backup/restore forms, synthetic PTY and workflow tests, and documentation. Independent R2 closed its list-status finding; all planned coordinator gates passed. Real WireGuard and systemd remain M8/M9. - Milestone 8 delivered reviewed opt-in Alpine Testcontainers coverage for real IPv4 WireGuard peers, application lifecycle, saved/runtime separation, restoration, import/reissue, backup failures and the real Rustix reader. Host gates and three fresh real-container runs passed on one stable source fingerprint; M8-A1–A10 accepted. Real IPv6 behavior is unverified by user-authorized scope. - Milestone 9 is retired and unaccepted: the reviewed disposable VM preparation and guarded first start did not establish Debian/systemd acceptance. The exact VM was powered off after the user stopped work. M9.2–M9.3 were not started; M9-A1–A9 are unchecked. - M18–M27 handoffs are complete. Stop after M27 acceptance; no next milestone is authorized. Keep M16 validation limitations separate from TUI evidence. Do not access production, commit, deploy or resume VM work. ## Plan maintenance Follow [Execution records and diagnostic workflow](docs/EXECUTION_RECORDS.md). Update the current snapshot in place after a task result, review disposition, blocker/scope change, acceptance, or handoff. Do not append command-by-command attempts or worker transcripts. Keep register, milestone outcome and resume point consistent. Preserve final evidence and scope-change reasons; archive superseded history with a link before removing it. Change the specification only with actual user authorization. Planning reference: [OpenAI — Using PLANS.md for multi-hour problem solving](https://developers.openai.com/cookbook/articles/codex_exec_plans). The specific model roles, milestone numbering, and review policy above are project decisions requested by the user.