dr: recovered WG-key install + host_loss directive→restore-PLAN (S5 safe halves)

wgtunnel.InstallRecoveredKey: write an escrow-recovered WG private key (create-
only, refuse-overwrite) so the tunnel re-establishes with the same identity/pubkey
(same /32), no keygen. Wired into identity-consume -install-wg-key (opt-in;
pre-S3 blob → logged fresh-keygen fallback). Value never logged.

internal/dr (new): consume the host_loss restore_directive (was logged-ignored)
into an inspectable RestorePlan via the AddConsumer raw seam — per guest
{vmid,archive,target,sizing} + per drive {durable_id→mount} + offsite PBS coord.
DERIVE-AND-SURFACE only; the Consumer has no restore/destroy dependency (execute-
nothing is structural). guest_loss/absent → no plan.

Tests + red-proofs (WG create-only overwrite; plan mode-gate). No secrets on
argv/stdout/logs. The destructive in-place restore is a separate operator-present
STOP-gated drill.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
2026-07-04 21:06:31 +02:00
parent 567cf9f401
commit bd4bced771
7 changed files with 360 additions and 3 deletions
+21
View File
@@ -1,3 +1,24 @@
## v0.69.0 — S5: host-loss DR — recovered WG-key install + directive→restore-PLAN (safe halves) (2026-07-04)
The two safe, non-destructive mechanical links for host-loss DR (the destructive in-place restore is
a separate operator-present, STOP-gated drill).
- **`internal/wgtunnel.InstallRecoveredKey`** — writes an escrow-recovered WG private key (32-byte
base64, re-encoded canonical) to the key file so the tunnel re-establishes with the SAME
identity/pubkey (→ the same hub `/32`), no fresh keygen. **CREATE-ONLY** — refuses if a key file
exists (a present key may be a live identity); value never logged. Wired into `--selftest=identity-
consume -install-wg-key` (opt-in; after `UnwrapIdentityBundle`, installs `bundle.WGPrivateKey`;
pre-S3 blob with no WG key → logged fallback to fresh keygen + re-register, which keeps the /32).
- **`internal/dr`** (new) — consumes the host_loss `restore_directive` (was logged-and-ignored) into
an inspectable **RestorePlan** via the `desired.Syncer.AddConsumer` raw seam: per guest →
{vmid, archive, target storage, sizing}; per drive → {durable_id → expected mount}; + the offsite
PBS coord. **Derive-and-surface only** — the `Consumer` has NO restore/destroy dependency, so
"execute nothing" is structural. `guest_loss`/absent → no plan. Recipe fetched on-demand (rare
directive) via a fresh `Collect`.
- Tests + red-proofs: WG install (same pubkey/no-keygen; present-key refuse — red-proofed against
allow-overwrite); plan (host_loss builds; guest_loss/absent/nil-recipe → none — red-proofed against
a relaxed mode gate). No secrets on argv/stdout/logs (field names only).
## v0.68.0 — S4.1: tier-aware restore-task deadline (unattended offsite restore-test) (2026-07-04)
The offsite restore-test couldn't complete on the scheduler path because a WAN restore of a large