Implement backups, commit and restoration

This commit is contained in:
2026-10-20 00:13:39 +03:00
parent c879a8101b
commit 5418841b92
17 changed files with 5049 additions and 38 deletions
+377 -16
View File
@@ -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 | 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 |
| ID | Milestone | Prerequisites | State |
| --- | -------------------------------------------------------- | ----------------------------------------- | -------- |
| 1 | Rust foundation, settings, and domain states | None | accepted |
| 2 | Server configuration editing with byte preservation | 1 | accepted |
| 3 | Client keys, encrypted vault, and existing-client import | 2 | accepted |
| 4 | Backups, safe Commit, and full restoration | 3 | accepted |
| 5 | Runtime observation and systemd restart adapter | 4 | 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
@@ -1025,9 +1025,369 @@ 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.
Status: accepted (2026-10-19T21:10:25Z; independent R5 closed all findings and final coordinator gates passed).
Prerequisites: milestones 1–3 accepted; pinned native Rust environment and
available approved dependencies. This milestone uses synthetic temporary
filesystems and injected failures, not root, production files, or running services.
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.
#### Outcome and scope
Deliver library/application operations that install prepared M3 vault data,
save an M2-edited server document through Commit, preserve internal allocation
state, create/list backups, and preview/confirm full restoration without a vault
password. Writes must be honest about partial failure and recoverable from the
mandatory pre-operation backup. Neither Commit nor restore applies VPN changes.
Read SPEC.md Data locations, Data sources, Operations and states, Encrypted vault,
and Backups and restoration; all three ADRs; VAULT_FORMAT.md, CONFIG_FORMAT.md,
DEVELOPMENT.md, DEPENDENCIES.md, ENVIRONMENT.md, and TESTING.md. Distinguish the
application's Commit action from Git commits, which are not authorized here.
Out of scope: TUI, output files/QR, password prompts, reissue reconciliation,
real WireGuard/systemd observation or restart, automatic failed-restart rollback,
concurrent editor detection/locking, compression, retention cleanup, migration to
different server paths, and production access. The startup management placeholder
remains safe until later milestones; expose testable APIs rather than a new CLI.
#### Existing code and integration constraints
- `config::ResolvedSettings` provides executable-relative server/client/data paths
and data/archive directories; settings live at `wireguard-admin.toml` beside
the executable. Use explicit resolved paths in adapters, never process cwd.
- M2 `ConfigDocument` and edits preserve original bytes and report unsupported
peers. Commit persists the prepared document without reserializing server
settings, silently projecting away anomalies, or rewriting unchanged comments.
- M3 `VaultBundle::to_toml`/`from_toml` encode/load encrypted v1 bundles without
password entry. VAULT_FORMAT.md fixes the intended path as data/vault.toml.
Preserve recipient, encrypted identity, records, and authenticated blocked flags;
backups/restore do not decrypt or rebuild them from plaintext imports.
- `PreparedImportSetup` contains bundle/report; read-only scans never installed
application state. M4 owns initial installation through the same persistence
and pre-Commit backup boundary as later prepared imports.
- Vault records are private and there is currently no public removal operation.
Add a narrow public-key removal API as needed; do not serialize/reparse internals
to bypass encapsulation or expose plaintext. Do not automatically purge file-only
imported records merely because they are absent from the server configuration.
- M1 `ClientStates::commit_draft` updates the in-memory saved snapshot and address
reservations only. Invoke/reflect that transition after successful disk commit,
never before it. Current reservation state is private and has no persistence
codec; add explicit validated state encode/load support, not generic secret or
whole-document serialization. Preserve unsupported M2 peers outside narrow Client.
- Historical verified/applied snapshots are not fresh runtime observations. On
reopening/restoring, preserve needed reservation/history data but mark current
runtime unknown until M5 verifies it. Restore never frees a pending deleted IP.
- Cargo already includes M1–M3 libraries and tempfile for tests. Approved `tar`
and `time` may be introduced with minimal features, and tempfile/rustix used
for secure writes. Moving tempfile to normal dependencies is allowed if needed.
No new hash/UUID/compression/database/framework dependency is implicitly approved.
#### Context and implementation steps
Use sequential tasks and one writer. GPT-6-Sol/medium coordinates; GPT-6-Luna/high
implements, reviews independently, and repairs. Supply explicit ownership, scope,
accepted interfaces, validation, and task report requirements on each dispatch.
| Task | Owner | Files and deliverable |
| --- | --- | --- |
| M4.1 | implementer | `src/backup.rs` and child modules as needed, `docs/BACKUP_FORMAT.md`: versioned allowlisted archive, limits, inventory, validated preview |
| M4.2 | implementer | narrow persistence module, `src/config.rs`/`src/domain.rs` adapters as needed, `docs/PERSISTENCE.md`: safe path handling, validated state, staged writes and recovery contract |
| M4.3 | implementer | `src/app.rs`, `src/vault.rs` narrow deletion API, library exports: prepared change sets, initial setup/Commit and mandatory backup ordering |
| M4.4 | implementer | application/backup/persistence modules: manual and pre-restart backup APIs, previewed confirmed full restore, restart-history boundary |
| M4.5 | implementer | `tests/commit_restore.rs`, test-only failure adapters and README: round trips, fault injection, archive attacks, permission and reservation scenarios |
| M4.6 | reviewer, repairer | Independent integrated review, bounded repairs, re-review of failure paths and regressions |
| M4.7 | coordinator | Integrated gates, evidence/findings ledger and acceptance/handoff in PLANS.md |
M4.1 defines a versioned uncompressed tar archive with a bounded TOML manifest.
Use logical fixed member names for settings, server configuration, internal state,
and encrypted vault; never restore directly to tar-supplied paths. Record UTC
creation time, operation reason (manual/pre-Commit/pre-restore/pre-restart), member
inventory/lengths, and historical verification status without inventing working
state. Define collision-safe archive naming without overwriting an existing file.
A new archive is visible in the list only when fully written and finalized.
Capture the currently installed saved bytes, not the proposed draft or decrypted
records. Settings and server config may contain secrets: archives require 0600.
Exclude source client files, drafts, previous archives, staging/journal artifacts,
and delivered plaintext output. Initial setup can lack vault/internal state:
represent genuine absence explicitly, not as corruption or an empty fake vault.
Restoring that backup must restore absence for those owned components. Missing
required settings/server config or read errors are not optional absence.
Bound archive/member size and entry count before reading/extracting everything.
Reject unsupported manifest versions, missing/duplicate/unexpected members,
absolute or traversal paths, links, device/special entries, inconsistent sizes,
truncation, and unsafe extension/header interpretations. Parse allowed files in
staging before mutation using existing settings/document/vault validators. Do not
reject a losslessly supported anomalous peer simply because project_client fails.
The tar checksum is not authentication; do not advertise tamper-proof backups or
silently add a signing/hash dependency. Bounds/structural validation remain required.
M4.2 documents file layout and a versioned internal-state schema for reservations,
historical apply evidence, and operation recovery if needed. Keep aliases and
client configuration authoritative in the server file. Reject unknown or malformed
state rather than silently losing reservations. A file loaded from disk must not
self-assert current RuntimeObservation::Verified without runtime verification.
Separate orchestration from a narrow filesystem adapter for deterministic errors.
Use safe no-follow/anchored path operations, regular-file checks and private
staging. Detect target aliasing/overlap (for example vault and server resolving to
the same file) before mutations. Do not traverse a symlink or use archive metadata
to chmod/chown arbitrary targets. Create files/directories with 0600/0700 from
creation; preserve protected directory access and handle existing permissions
explicitly. Do not recursively delete the data directory to implement restore.
Use same-filesystem temporary files, complete writes, flush/sync, atomic rename,
and directory sync where applicable. A rename is not a transaction across server,
settings, and data directories. Document ordering, durable recovery markers or an
explicit equivalent, and recovery from interruption between replacements before
implementing it. Never claim multi-file atomicity just because each rename is
atomic. Backup-before-mutation and write recovery are not optimistic locking.
On replacement failure, either leave the old set intact or return an explicit
incomplete/recovery-required result with the preserved pre-operation backup.
Do not publish success, advance saved memory, or accept another mutation over
unresolved partial state. Recovery must be deterministic and tested after reopening
from partial files. It may finish/restore a local write transaction as documented;
it must never apply runtime changes or become automatic failed-restart rollback.
Report cleanup failures honestly and keep recoverable evidence if cleanup is unsafe.
M4.3 builds a prepared change set containing edited server bytes, staged encrypted
vault, and validated internal state. Preview/validate it before installation.
Complete the pre-Commit backup before the first protected destination replacement.
Failure to create/finalize the backup stops Commit and preserves installed bytes
and saved in-memory state. Then stage/install the set and only after success
advance saved snapshots and expose successful Commit. A failed Commit retains
retryable draft intent without treating possibly partial disk state as consistent.
Deletion removes the explicitly selected client record and vault secret in the
same committed operation; unknown/missing vault records need clear behavior, since
server-only clients are legitimate. Unrelated imported/file-only records remain.
Earlier archives remain unchanged. Prepared imports/setup never overwrite an
existing vault identity silently and never clear authenticated reissue blocking.
Discarding an uncommitted preparation must not change installed files. Persistent
settings updates are not a new product feature; Commit handles agreed client and
internal changes while settings are included in backups/restoration.
M4.4 exposes listing and manual backup plus a reusable pre-restart backup operation
for M5, without restarting anything here. Sorting/time labels do not imply a backup
is working. Accept historical verification only from an explicit verified-apply
boundary intended for M5; represent no verified state honestly. Any identity/token
used to associate a saved generation with verification must not be a fresh runtime
claim or a new crypto dependency. Never mark pre-restart bytes known-working based
on the backup reason alone.
Restore has a read-only preview followed by explicit confirmation of that prepared
archive. Validate the chosen content and determine affected files before writes;
then complete the precautionary backup, stage all replacement/deletion actions,
and install. Restore settings, whole server file, internal state, and opaque vault
as a complete saved set, not a client merge. Whole-file restoration of the archived
server is intentional; ordinary Commit still obeys M2 client-only editing rules.
Use current trusted destination mapping and enforce the installation's invariant
paths. Archived settings cannot redirect writes outside that mapping or switch data
roots during restore; report incompatible paths instead of adding migration logic.
Keep the selected validated archive stable between preview and confirmation so
confirmation cannot apply different bytes. Do not silently discard the active draft
until confirmed; cancellation/validation/backup failure preserves it. Successful
restore discards it and reloads the restored saved state, retaining conservative
pending reservations across the still-unchanged runtime boundary. Historical
restored state cannot assert the current interface matches it. Caller never needs
or supplies a password for archive listing, backup, preview or restore.
M4.5 injects failures at backup creation/write/finalization, staging writes, sync,
each destination replacement, recovery and cleanup. Verify exact file sets and
memory transitions rather than only error messages. Avoid permission tests that
pass accidentally as root; run unprivileged and inject deterministic failures.
Use temporary private directories and generated encrypted bundles. Do not log
server fixture contents or decrypted data in failing assertions.
#### Acceptance and validation
- [x] M4-A1: documented versioned archive/state layouts round-trip settings,
server bytes, encrypted vault and internal state without any password/decryption;
initial missing vault/state is represented and restored explicitly.
- [x] M4-A2: complete backups precede Commit/restore and the future restart boundary;
backup/read/finalization failure prevents protected replacements and successful
memory transitions. Manual backup/listing works without mutations to saved data.
- [x] M4-A3: successful Commit writes the prepared client changes, vault and state;
untouched server spans remain identical; saved memory advances only on success,
runtime stays unchanged/unknown, and draft discard does not write files.
- [x] M4-A4: explicit client deletion removes only its current vault record while
server-only and file-only anomalies remain supported; old archives are unchanged.
- [x] M4-A5: confirmed restore fully replaces owned saved components, including
removal of components absent in the archive, without merging or reading plaintext
client sources. Preview/cancel does not mutate or silently discard the draft.
- [x] M4-A6: original client source files, unrelated files and all archives outside
the selected/pre-operation artifacts are protected from traversal, symlink/link
escapes, target aliasing and unsafe tar entries; test bounded parsing and rejection.
- [x] M4-A7: 0600 secret-bearing files/archives and 0700 private directories apply
from creation, including staging; errors and Debug redact secret-bearing bytes.
- [x] M4-A8: injected failures/interruption between multi-file replacements produce
no false success or premature saved-memory change; reopening detects incomplete
work and follows the documented deterministic recovery path before new mutations.
- [x] M4-A9: saved/deleted address reservations survive reopen and restore across
unapplied changes; neither action releases them or trusts historical verification
as current runtime. Missing/malformed state cannot silently permit address reuse.
- [x] M4-A10: listing records UTC reason and explicit historical apply status;
pre-Commit/pre-restart archives and newest archives are not automatically working.
- [x] M4-A11: incompatible archived destination settings are rejected; restored
server/config/vault stay at trusted invariant paths, and no whole-directory wipe
or recursive archive inclusion occurs.
- [x] M4-A12: M1–M3 regressions and integrated gates pass; independent review closes
actionable persistence, archive, backup-order and failure-recovery findings.
Planned commands from the repository root after M4 targets and lockfile exist:
```sh
cargo test --locked backup
cargo test --locked persistence
cargo test --locked --test commit_restore
cargo test --locked --test vault_import
cargo test --locked --test config_preservation
cargo fmt --all -- --check
cargo clippy --locked --all-targets -- -D warnings
cargo test --locked
cargo build --locked --release
./target/release/wireguard-admin --help
./target/release/wireguard-admin --version
git diff --check
```
Filters must execute actual focused tests. All commands should exit zero;
negative cases are assertions within passing tests. Resolve approved added crates
and update Cargo.lock before locked checks. Record actual counts, commands and
failure-injection coverage. Tests are unprivileged filesystem/library checks only;
no container/VM or live restart acceptance is required or claimed in this milestone.
#### Progress
- [x] Description prepared from accepted M1–M3 interfaces and current documents.
- [x] M4.1 — `/root/implementer_m4_1` added `src/backup.rs`,
`docs/BACKUP_FORMAT.md`, approved `tar`/`time` dependencies, and six focused
archive tests (2026-10-19T20:23:23Z). Coordinator inspected the actual code;
final integrated review and gates subsequently passed.
- [x] M4.2 — `/root/implementer_m4_2` added `src/persistence.rs`,
`docs/PERSISTENCE.md`, and validated reservation restoration in `src/domain.rs`
(2026-10-19T20:30:34Z). Integrated review subsequently passed.
- [x] M4.3 — `/root/implementer_m4_3` added `src/app.rs`, a narrow vault
record-removal API, and one trusted archive-directory accessor (2026-10-19T20:35:58Z).
Commit backup/install ordering and draft tests passed integrated review.
- [x] M4.4 — `/root/implementer_m4_4` added list/manual/pre-restart backup
boundaries and byte-bound restore preview/confirmation, with path-invariant
validation and focused tests (2026-10-19T20:42:20Z); integrated review passed.
- [x] M4.5 — `/root/implementer_m4_5` added nine cross-module tests and fixed
restore reservations plus interface-name invariant (2026-10-19T20:49:14Z).
Documentation was synchronized; independent review passed.
- [x] M4.6 — fresh `/root/reviewer_m4_r1` reviewed the integrated result;
separate `/root/repairer_m4_r1` repaired R1 and R4 findings; R2/R3/R5
re-review closed all with no new actionable findings (2026-10-19T21:10:25Z).
- [x] M4.7 — coordinator ran all planned integrated gates and accepted M4
(2026-10-19T21:10:25Z).
#### Review and repair
| Finding | Iteration | Location and impact | Owner | Resolution and evidence | State |
| --- | --- | --- | --- | --- | --- |
| M4-R1-01 | R1/R2 | `src/persistence.rs` saved reads accepted existing secret-bearing files with broad permissions. | `/root/repairer_m4_r1` | R2 confirmed fail-closed server/vault mode checks and regression tests. | closed |
| M4-R1-02 | R1/R2/R3 | Archive and installation failure paths lacked deterministic evidence. | `/root/repairer_m4_r1` | R3 confirmed partial restore interruption after server replacement, unchanged session saved/draft/applied state, failed rollback and fresh reopen recovery, and intact pre-restore archive; focused test passed. | closed |
| M4-R4-01 | R4/R5 | `src/app.rs` lost verified saved-generation history on reopen and mislabeled pre-Commit backups. | `/root/repairer_m4_r1` | R5 confirmed durable history after mandatory backup, truthful archive labeling, reopen history with runtime Unknown, and failure/clear tests. | closed |
R1 independently inspected the integrated code; `cargo test --locked --test
commit_restore` passed 9 tests and `git diff --check` was clean. Reviewer did not
claim Alpine, VM, live WireGuard, or host-service evidence. R2/R3/R5 completed
the required re-reviews and closed all findings.
#### Evidence and handoff
Planning inspected Cargo.toml, M3 vault serialization and source/import APIs,
VAULT_FORMAT.md, M1 state methods, M3 recorded acceptance and governing documents.
M3 records independent R7 review, 64 unit tests plus 9 preservation and 5 vault
import integration tests, and passing coordinator gates. These are historical
results; this planning task did not rerun tests or modify application code,
dependencies, files in configured runtime directories, or installed vault state.
M4.1 worker reported `cargo test --locked --offline` (70 unit, 9 preservation,
5 vault-import), `cargo test --locked --offline backup` (6), Clippy, format,
and `git diff --check` passing. These are worker results, not final integrated
acceptance. The archive API accepts explicit saved bytes and validates a bounded
fixed-member UStar format without extraction; app-level semantic validation and
trusted path handling remain for M4.2–M4.4. M4.2 worker reported 10 persistence
tests, full suite (80 unit + 9 preservation + 5 vault-import), Clippy, release
build, format and diff checks passing. It supplies `SavedStore` with validated
read/install/recover, a private journal, and explicit recovery-required results;
per-syscall fault injection remains for M4.5. These are worker results only.
M4.3 worker reported formatting, Clippy, full tests (89 unit + 14 integration),
release build, and diff checks passing. Its `Application` stages public-key edits,
pre-Commit backups exact installed bytes, and advances saved memory only after
install. Broad-route legacy deletion fails closed because the persisted reservation
format holds single addresses; exact-host legacy routes are retained. This is
worker evidence, not integrated acceptance. M4.4 worker
reported 95 unit + 14 integration tests, formatting, Clippy, offline release
build and diff checks passing. Coordinator inspection identified additional
reservation/path failure cases for M4.5 tests and independent review. M4.5
worker reported 95 unit + 23 integration tests, focused backup (11), persistence
(10), and commit_restore (9), plus format, Clippy, release build, help/version,
and diff checks passing. Its tests reproduced and fixed restore dropping current
saved-client reservations and accepting an archived interface-name switch.
Syscall-level write/fsync fault injection is not yet exhaustive; this remains
an explicit review concern. These are worker results, not final acceptance.
Next action: fresh independent M4 reviewer, then repair/re-review as needed.
R1 returned M4-R1-01 and M4-R1-02 as actionable. Next action: a separate
repairer fixes those findings, then the reviewer rechecks. Repairer reported
both fixed, with 102 unit + 23 integration tests and formatting, Clippy,
release build and diff checks passing (2026-10-19T20:57:23Z). The coordinator
has not yet accepted those claims; next action is independent R2 re-review.
R2 (2026-10-19T20:58:52Z) closed M4-R1-01 and partially closed M4-R1-02.
Focused persistence (15), backup (12 unit + 2 integration), commit_restore (9),
and injected Commit test (1) passed. Reviewer requires a restore partial-install
failure/recovery test at the application boundary. Next action: targeted repair
and R3 re-review; milestone remains in progress.
Targeted repair added a private restore-install test seam and regression. R3
(2026-10-19T21:01:08Z) closed M4-R1-02, found no new actionable defects,
and passed the focused test plus `git diff --check`. Next action: coordinator
integrated gates and A1–A12 decision.
Coordinator gates passed on the R3 revision, but a targeted historical-status
audit found M4-R4-01 (2026-10-19T21:03:13Z). Those gate results will be rerun
after repair. The milestone remains in review; next action is targeted repair
and independent re-review.
Final integrated evidence after R5 repair (coordinator, 2026-10-19T21:10:25Z):
`cargo test --locked backup` passed 14 unit + 2 integration matches;
`cargo test --locked persistence` passed 15; `cargo test --locked --test
commit_restore` passed 9; `cargo test --locked --test vault_import` passed 5;
`cargo test --locked --test config_preservation` passed 9. `cargo fmt --all
-- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo
test --locked` (108 library unit + 9 Commit/restore + 9 preservation + 5 vault
import, 0 doctests), `cargo build --locked --release`, release `--help` and
`--version`, and `git diff --check` all exited zero. All three findings are
closed by independent R2/R3/R5; R5 found no new actionable issue. Evidence is
unprivileged synthetic/unit-fixture only. No Alpine, disposable systemd VM,
live VPN, host service, production file or production key was accessed or
claimed. The terminal workflow and real runtime/systemd checks belong to later
milestones. M4 has no remaining blocker.
#### Decisions and discoveries
Vault v1 already fixes data/vault.toml and authenticated conflict behavior.
Preserve that format and opaque ciphertext. New state/backup formats belong in
their own documents rather than altering vault or managed-comment v1 without need.
The persistence layer spans directories and cannot assume a single atomic rename.
Interruption recovery and conservative address reservations are required write
integrity, not concurrent editing support or automatic failed-restart rollback.
Confirmed archive restoration replaces server settings as part of explicit recovery;
normal client Commit continues to preserve administrator-owned bytes.
The v1 internal-state historical timestamp follows the application's
single-administrator save boundary; it is not a digest check against manual
out-of-band file changes. Reopen treats history as historical, not current
runtime verification. Restoration carries forward pending address reservations
for safety while replacing archived client/settings/vault saved content.
#### Outcome
Accepted after independent R5 review, closure of M4-R1-01/M4-R1-02/M4-R4-01,
and all M4-A1–A12 gates. Stop after M4. Begin M5 only on a separate request;
no Git commit, deployment or production access occurred.
### Milestone 5 — Runtime observation and systemd restart adapter
@@ -1061,13 +1421,14 @@ Acceptance: real D-Bus job handling, separate restart verification, address rele
## Current progress and resume point
- Milestones 1, 2 and 3 are accepted; milestones 4–9 remain pending.
- Milestones 1–4 are accepted; milestones 5–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.
- Milestone 4 delivered versioned private backups, journaled saved-file Commit and recovery, previewed full restore, durable address reservations and historical verification bookkeeping, with independent R5 closure and coordinator gates.
- Next action: stop after accepted M4; begin M5 only if separately requested.
- M1–M4 integrated gates and independent reviews passed. No Alpine or VM results are claimed.
## Plan maintenance