Add server configuration editing with byte preservation
This commit is contained in:
@@ -108,17 +108,17 @@ Actual systemd/D-Bus acceptance runs in a disposable VM. Never use production to
|
||||
|
||||
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 | pending |
|
||||
| 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 |
|
||||
| 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
|
||||
|
||||
@@ -337,9 +337,285 @@ later milestones. Stop here; next milestone is 2 and has not started.
|
||||
|
||||
### Milestone 2 — Server configuration editing with byte preservation
|
||||
|
||||
Implement document-preserving parsing/editing and versioned metadata comments. Server configuration remains authoritative, duplicate aliases are permitted, and only managed client portions change.
|
||||
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.
|
||||
|
||||
Acceptance: synthetic fixtures prove byte preservation of untouched settings/comments, client addition/deletion/rename, unknown-field preservation, missing creation dates, and rejection of metadata/terminal injection. No complete generic INI reserialization.
|
||||
#### 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
|
||||
|
||||
@@ -385,10 +661,11 @@ Acceptance: real D-Bus job handling, separate restart verification, address rele
|
||||
|
||||
## Current progress and resume point
|
||||
|
||||
- Milestone 1 is accepted; milestones 2–9 remain pending.
|
||||
- Milestones 1 and 2 are accepted; milestones 3–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.
|
||||
- Next action, only on a later request: prepare Milestone 2; no work on it has started.
|
||||
- Milestone 2 delivered a lossless in-memory WireGuard configuration editor, versioned metadata, and behavioral preservation tests.
|
||||
- Next action: stop after accepted M2; begin M3 only if separately requested.
|
||||
- M1 integrated gates and independent re-review passed. No container or VM results are claimed.
|
||||
|
||||
## Plan maintenance
|
||||
|
||||
Reference in New Issue
Block a user