v0.104.0: off-box unit discovery (durable, deployment-independent) + no-silent-success

offbox located each toggled app's recovery unit via AppNamespaceRoot→GetAppDrivePath,
which reads the app's LIVE app.yaml HDD_PATH and silently falls back to systemDataPath
when the app isn't deployed → looked on the wrong drive, backed up nothing, reported
ok/0 (DIAG root cause). Now:

- discoverOffboxUnit/offboxCandidateNSRoots scan the durable storage registry
  (schedulable non-decommissioned paths ∪ systemDataPath) for backups/primary/<app>,
  independent of deploy state; newest-by-manifest-CreatedAt wins on drive churn.
- RunOffboxBackup: runOffboxInternal returns (backedUp, missing, err); 0-of-N toggled →
  hard error + operator alert; partial → ok + new OffboxTarget.LastWarning (shown on
  /backups, preserved across config edit).
- AppNamespaceRoot + primary WRITE paths unchanged.
- 6 non-hollow tests (A-E + edge) + both companion red-proofs run (reverted).
- NOT yet live-validated against the Storage Box (spike creds torn down).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
2026-07-09 12:32:05 +02:00
parent 59eb3bea76
commit 908e4b906a
8 changed files with 390 additions and 26 deletions
+13
View File
@@ -703,6 +703,19 @@ not just those with HDD data. Non-HDD apps can configure destination, method, an
> - init-if-absent (idempotent), `forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune`,
> single-flight (shares `m.running`) + migration-guard, restic's own exit code checked, restore to a
> scratch dir (non-destructive).
> - **Unit discovery (v0.104.0) — durable, deployment-independent.** Each toggled app's recovery unit is
> **discovered** by scanning the durable storage registry — every registered *schedulable* storage path
> (`GetSchedulableStoragePaths`) the system-data fallback drive — for `backups/primary/<app>`, rather
> than *inferring* the drive from the app's live `app.yaml` `HDD_PATH` (which silently fell back to
> `systemDataPath` for a toggled-but-undeployed app → offbox looked on the wrong drive, backed up
> nothing, and reported `ok`/0). If the same app's unit exists on two drives (drive churn), the **newest
> by manifest `CreatedAt`** is used and the stale one is WARN-logged. The WRITE side (`CaptureRecoveryUnit`
> / dumps) is unchanged — this only changes offbox's read/discovery path. Boundary: decommissioned or
> non-schedulable drives are not searched.
> - **No silent success (v0.104.0):** a run where ≥1 app is toggled but **0** were backed up (no unit found
> anywhere) is a **hard error** → `LastStatus="error"` + operator alert (was a misleading `ok`/0 snapshots).
> A *partial* run (some units missing) stays `ok` but sets a Hungarian **`LastWarning`** naming the skipped
> apps, shown on `/backups`.
> - **Secrets** (SSH key + auto-gen repo password) are **0600 files in the data dir** — never logged/committed;
> they ride DR via the PBS whole-CT snapshot of the rootfs (the recovery-unit/dr-recipe stay secret-free).
> - **Injection guard** (`ValidateOffboxTarget`): host/user/repo must not start with `-` (ssh