1077 lines
79 KiB
Markdown
1077 lines
79 KiB
Markdown
# 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 <next integer> — <observable outcome>
|
||
|
||
Status: pending
|
||
Prerequisites: <accepted milestone IDs and environment requirements>
|
||
|
||
### Outcome and scope
|
||
<What becomes possible, boundaries, and links to governing requirements.>
|
||
|
||
### Context and implementation steps
|
||
<Actual paths, current state, interfaces, task ownership, and ordered steps.>
|
||
|
||
### Acceptance and validation
|
||
- [ ] <Observable behavior and test/demonstration proving it.>
|
||
- [ ] <Relevant failure behavior and how it is verified.>
|
||
Commands: <exact commands, working directory, prerequisites, expected results>
|
||
|
||
### Progress
|
||
- [ ] <Task ID and owner/agent ID; timestamp when updated>
|
||
|
||
### Review and repair
|
||
| Finding | Iteration | Location and impact | Owner | Resolution and evidence | State |
|
||
| --- | --- | --- | --- | --- | --- |
|
||
|
||
### Evidence and handoff
|
||
<Commands actually run, exit/results, artifact paths, reviewer disposition,
|
||
remaining limitations, next concrete action.>
|
||
|
||
### Decisions and discoveries
|
||
<New facts/trade-offs, reasons, and ADR references when warranted.>
|
||
|
||
### Outcome
|
||
<Coordinator acceptance decision and timestamp, or exact blocker.>
|
||
```
|
||
|
||
Use ISO 8601 timestamps with offsets for progress. Keep evidence concise and reproducible, with 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 dual-stack networking, and scoped `NET_ADMIN`. The real runtime adapter executes inside the server container. Missing Docker/kernel/IPv6 prerequisites are incomplete validation, not success. 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 | needs_changes |
|
||
| 3 | Client keys, encrypted vault, and existing-client import | 2 | pending |
|
||
| 4 | Backups, safe Commit, and full restoration | 3 | pending |
|
||
| 5 | Runtime observation and systemd restart adapter | 4 | pending |
|
||
| 6 | Client configuration delivery and reissue lifecycle | 5 | pending |
|
||
| 7 | Complete terminal management workflow | 6 | pending |
|
||
| 8 | Alpine Testcontainers integration | 7; suitable disposable Docker environment | pending |
|
||
| 9 | Real systemd acceptance and release readiness | 8; disposable systemd VM | pending |
|
||
|
||
### 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<String>`; 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
|
||
|
||
Implement versioned tar archives/manifests, mandatory pre-operation backups, internal state persistence, Commit orchestration, and previewed full restoration without decryption. Define partial-write handling and archive validation. Persist address reservations and verified-state bookkeeping as needed.
|
||
|
||
Acceptance: backup failure prevents protected mutations; Commit/restore never change the running VPN; archive composition matches the specification; malformed/traversal/link entries are rejected; restore returns saved settings/configuration/vault without a password. Tests demonstrate draft cancellation and deletion of current secrets while older archives remain unchanged.
|
||
|
||
### Milestone 5 — Runtime observation and systemd restart adapter
|
||
|
||
Implement `wireguard-uapi` runtime queries and the `zbus` systemd adapter behind narrow interfaces. Track service job completion and timeout, then verify interface/client correspondence before recording applied state and releasing addresses. Require backup before restart.
|
||
|
||
Acceptance: ordinary tests exercise successful and failed jobs, missing/mismatched interfaces, unavailable statistics, and no automatic rollback using controlled adapters. Real runtime/systemd acceptance remains explicitly pending until milestones 8 and 9; mocks cannot close those later gates.
|
||
|
||
### Milestone 6 — Client configuration delivery and reissue lifecycle
|
||
|
||
Generate full-tunnel IPv4/IPv6 configurations using settings and preserved existing parameters where applicable. Save a root-only plaintext file and optionally render a matching QR code. Enforce post-Commit initial delivery and per-client password-based reissue with session-scoped retry after file errors.
|
||
|
||
Acceptance: file/QR payloads agree; no delivery before Commit; output failure permits directory change/retry; successful initial delivery or session exit ends password-free eligibility; overwrite requires confirmation; unrelated plaintext imports cannot bypass authentication.
|
||
|
||
### Milestone 7 — Complete terminal management workflow
|
||
|
||
Connect tested application operations to Ratatui/Crossterm screens: list and statistics, forms, anomalies/import, draft/diff, Commit, restart, output/QR, and backup selection/restore. Keep expensive work off the render/input loop and maintain public-key-based selection.
|
||
|
||
Acceptance: keyboard workflows and cancellation preserve the required states, statistics refresh without claiming online status, destructive actions identify targets, passwords are masked, and terminal cleanup/resize/non-TTY/error behavior is verified. No implicit apply or save on exit.
|
||
|
||
### Milestone 8 — Alpine Testcontainers integration
|
||
|
||
Implement opt-in Rust container tests and a minimal pinned Alpine image with `wireguard-tools` and `iproute2`. Cache images, use fresh isolated resources, build an Alpine-compatible test executable (prefer musl), and exercise the real runtime adapter within the server container. Avoid new helpers outside approved dependencies without agreement.
|
||
|
||
Acceptance: prerequisite checks and cleanup work; real dual-stack tunnels produce handshake/counter changes; creation/Commit leaves runtime unchanged until explicit test-only application; deletion retains access/address reservation until application; restored saved state does not implicitly apply. No host network, host D-Bus, production mounts, privileged default, or automatic module loading. Systemd coverage is explicitly excluded.
|
||
|
||
### Milestone 9 — Real systemd acceptance and release readiness
|
||
|
||
Prepare a repeatable disposable VM procedure and run the actual application/systemd adapter against synthetic WireGuard state. Finish operating/recovery documentation, safe settings examples, native target release build, and review of executable runtime dependencies.
|
||
|
||
Acceptance: real D-Bus job handling, separate restart verification, address release, and manual backup recovery pass in the VM. All specification acceptance criteria have linked evidence. Required default checks and Alpine tests pass on the integrated revision; unresolved defects or unavailable required VM checks prevent final acceptance. Production is never the test environment.
|
||
|
||
## Current progress and resume point
|
||
|
||
- Milestones 1, 2 and 3 are accepted; milestones 4–9 remain pending.
|
||
- 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.
|
||
- Next action: stop after accepted M3; begin M4 only if separately requested.
|
||
- M1 integrated gates and independent re-review passed. No container or VM results are claimed.
|
||
|
||
## Plan maintenance
|
||
|
||
Update progress, findings, decisions, blockers, and the next action after each meaningful execution/review cycle and before handing off or ending a session. Preserve acceptance evidence when summarizing old iterations. Append scope changes with their reason; update the specification only when the user actually authorizes a requirement change.
|
||
|
||
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.
|