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
+30
View File
@@ -1,5 +1,35 @@
## Changelog
### v0.104.0 — off-box unit discovery (durable, deployment-independent) + no-silent-success (2026-07-09)
Fixes the off-box mis-resolution + silent-success landmine surfaced by the Storage-Box spike and pinned by
the DIAG report (`felhom.eu/documentation/audits/SPIKE-storagebox-restic-direct-2026-07-09.md`). Root cause:
`runOffboxInternal` located each toggled app's recovery unit via `RecoveryUnitPath(AppNamespaceRoot(stack),
stack)`; `AppNamespaceRoot`→`GetAppDrivePath` reads the app's **live** `app.yaml` `HDD_PATH` and returns `""`
for a not-currently-deployed app, **silently falling back to `systemDataPath`**. So a toggled-but-undeployed
app was looked for on the wrong drive → `os.Stat` failed → skipped → the run returned `nil` → status `ok`
with 0 snapshots (no alert).
- **Discovery over inference** (`internal/backup/offbox.go`): new `discoverOffboxUnit` / `offboxCandidateNSRoots`
scan the durable storage registry — every registered *schedulable, non-decommissioned* path
(`GetSchedulableStoragePaths`) `systemDataPath`, deduped by resolved nsRoot — for `backups/primary/<app>`,
independent of live deploy state. Multiple copies of the same unit (drive churn) → the **newest by manifest
`CreatedAt`** (mtime fallback) is backed up, the stale one WARN-logged. `AppNamespaceRoot` and the primary
WRITE paths (`CaptureRecoveryUnit`/dumps) are **unchanged**.
- **No silent success** (`RunOffboxBackup`): `runOffboxInternal` now returns `(backedUp, missing, err)`.
≥1 toggled but `backedUp==0` → a **hard error** (`LastStatus="error"` + `offboxNotify` fires with a non-nil
err → operator alert). A *partial* run stays `ok` but sets a new customer-visible **`OffboxTarget.LastWarning`**
(`internal/settings/settings.go`, `last_warning,omitempty`) naming the skipped apps; rendered on `/backups`
in the `--warn` style (`internal/web/templates/backups.html`), preserved across a config edit
(`internal/web/offbox_handlers.go`).
- Tests (`internal/backup/offbox_test.go`): six non-hollow cases (A discovery-on-registered-drive, B 0/N
hard-error+alert, C partial→warning, D newest-of-two-copies, E happy path, edge 0-toggled), asserting the
exact discovered `src` + `LastStatus`/`LastError`/`LastWarning` + notify-err. **Companion red-proofs run:**
(A) reverting to `AppNamespaceRoot` resolution → 0 backups → FAIL; (B) `if false` on the 0/N promotion →
silent `ok` → FAIL; both reverted.
- **NOT yet live-validated against the Storage Box** — awaiting supervised re-provision + endpoint round-trip
(box repos/creds were torn down with the spike). Unit suite fully covers the discovery + status logic.
### v0.103.0 — F-C2-1: config loader no longer corrupts a bcrypt password_hash (silent auth bug) (2026-07-07)
Fixes campaign-2 finding **F-C2-1** (`felhom.eu/documentation/tests/CAMPAIGN-2-2026-07-07.md`).