From 5b2d824aac05292340aa9c70d012c810eb1201f9 Mon Sep 17 00:00:00 2001 From: librelad Date: Thu, 27 Aug 2026 12:16:48 +0100 Subject: [PATCH] docs: record the two silent no-ops the end-to-end restore run found Both were checks whose failure mode was to not run, and both produced output indistinguishable from a healthy report. Worth writing down as a shape rather than as two bugs. Co-Authored-By: Claude Opus 5 --- docs/roadmap/first-run-restore.md | 22 ++++++++++++++++++++++ docs/roadmap/storage-locations.md | 22 ++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/docs/roadmap/first-run-restore.md b/docs/roadmap/first-run-restore.md index dc7c407..7251e85 100644 --- a/docs/roadmap/first-run-restore.md +++ b/docs/roadmap/first-run-restore.md @@ -88,6 +88,28 @@ Output is a verdict list, then one confirmation: Continue? [Y/n]: ``` +### 3.1 — Two silent no-ops, and what they have in common + +Both were found by running the installer's restore path end to end against a +real repository, not by reading the code — which is the point of doing that. + +1. **The manifest is pretty-printed.** `"size_bytes": 123` carries a space, so + a `"key":[0-9]*` pattern missed it. Size came back empty, and the fit check + is guarded by `[[ -n "$size_bytes" ]]` — so it was skipped for every app. +2. **The manifest was never read at all.** `storageSnapshotSourcePath` passed a + snapshot id into an app-tag filter (see storage-locations §9), so it returned + 1 every time and the preflight fell back to a `?` size. + +The shared shape: **a check whose failure mode is to not run.** Neither printed +an error, and the report they produced — thirteen green ticks — is exactly what +a healthy run looks like. The only visible tell was the `?` in a column nobody +had a reason to distrust. + +So the regression test (`scripts/dev/lp-preflight-test`) asserts the *negative* +cases: an app too big for the disk must be refused, an app this version no +longer ships must be refused, an app whose location is gone must be marked as +moved. Reintroducing either historical bug fails it. + ## 4. The password problem, stated plainly **An encrypted repository cannot be opened with anything inside itself.** `CFG_BACKUP_LOC__PASSWORD` lives in the system config — which is *inside the backup*. So on a fresh machine the user must supply the repository password by hand. There is no way around this and it is not a bug; it is what encryption means. diff --git a/docs/roadmap/storage-locations.md b/docs/roadmap/storage-locations.md index 22afc21..a98b190 100644 --- a/docs/roadmap/storage-locations.md +++ b/docs/roadmap/storage-locations.md @@ -349,6 +349,28 @@ The fix is needed for this feature and is worth doing on its own: restore into ` Also worth noting: an app's restic snapshot paths change when it moves. Snapshot *history* stays intact and restorable (each snapshot knows its own path), but path-based filters in the snapshot browser need to accept either. +**Built — and then found not to work, which is the part worth remembering.** +`storageSnapshotSourcePath` / `storageRestoreAppTo` landed as described above, +but the resolver asked for a snapshot with + +```bash +engineSnapshotsJson "$idx" "$snapshot_id" +``` + +and that function's second parameter is an app **tag** filter. So it ran +`restic snapshots --tag app=`, matched nothing, and returned 1 on +every call. `storageRestoreAppTo` has a fallback for engines that cannot report +paths (borg genuinely cannot), so it took that fallback *always* — restoring in +place, which is exactly the blocker above, reinstated in the code written to +remove it. + +It survived because nothing failed: the fallback is a legitimate branch, and the +restore preflight's only symptom was printing every app's size as `?`. Fixed by +`engineSnapshotPaths`, an engine call that takes a snapshot id because that is +what it is for. The lesson generalises — **a check with a fallback needs a test +that the check itself fires**, not just that the command succeeds +(`scripts/dev/lp-preflight-test`). + ## 10. Risks, in the order they'll bite **10.1 — An unplugged drive is the top data-integrity risk.** `crontab_boot_app_reconcile.sh` brings **every** installed app up at boot. If a location's drive isn't mounted yet — or at all — docker cheerfully creates the bind-mount directories on the bare mountpoint and the app boots **empty**. Plug the drive back in and there are now two divergent copies, with the good one hidden underneath the mount. Mitigation is mandatory, not optional: