register: file R-156..R-164 in one pass, ranked; and record what mp1 is actually for
Nine rows into OPEN-ITEMS.md and ROADMAP.md, matching each file's column shape. R-156 and R-157 had lived only in audit documents - the identical "minted in a spike doc and never carried across" failure the register already records for R-153/R-154/R-155, caught by the catalog sweep's own section 8.0 while it was happening. R-158 was minted by a second session the same day for an unrelated finding, which is why the sweep's proposals were renumbered R-159..R-162 at filing time. All nine IDs verified free in BOTH backlog files before use. Part 0 settled the question the sizing item depended on, by reading: mp1 is RETENTION, not staging, and neither of the two framings was right. A unit is the KEPT copy on the app's OWN drive (backup.go:245-255); for an app with no HDD_PATH the namespace falls back to the system SSD - "the SSD-only system-data fallback" (appbackup/paths.go:26-27). There is no post-copy deletion: the only prune is F5 residue-on-old-drives when an app MOVES (backup.go:1053-1112). So mp1 retains the units of driveless apps only - not every app, but not transient either. Confirmed against the spike: sys_drive held exactly the four driveless apps and not calibre-web, which had a drive and was still backed up. A unit is volume tars + DB dumps only, never mp8 userdata (recovery_unit.go:20-25), so a 1 TB photo library can never overflow one. And mp1 gates the WHOLE chain, not just Tier 1: Tier-2 mirrors the unit "(always)" from RecoveryUnitPath (tier2.go:302,368) and Tier-3 carries it, so a unit that cannot be written leaves both with nothing to copy. Part 2 fired on both triggers - retention, and the fallback undocumented - so 07-backup-architecture.md gains section 7.5. Section 6.1 said a unit lives "on the app's own drive", which is true and was the whole story only for drive-resident apps; the no-drive case was undocumented, as was the sizing constraint. 7.5 records the mp0-50G-vs-mp1-20G mismatch, the measured ratios (DB app up to ~2x, 21.1GB -> 40.2GB; file-only 1.00x), and the bound this puts on D5's Lane-1 independence: restorable from the drive alone only while the unit still fits - about 19 GB file-only, about 10 GB DB-backed. No number proposed; the ratio is the operator's ruling (R-163). R-159/R-160 marked SHIPPED only after verifying the template changes are in app-catalog origin/main, and R-156's gate likewise (check-volume-persistence.py present). papra is NOT fixed - referred - so R-156 stays open on that one app. Ranked, with one line of reasoning each: R-157 first (an app can stay down indefinitely with mechanism B silent on every channel), then R-161 (the gate exists and nothing runs it, which is why R-156's class recurs - R-29's record is three orphaned gates and one enforced), R-156, R-163, R-158, R-164, R-162.
This commit is contained in:
@@ -484,6 +484,58 @@ Until D5 is actually implemented, §7.1's chain stands as the model.
|
||||
|
||||
---
|
||||
|
||||
### 7.5 Where a recovery unit is RETAINED — and the size bound it puts on Lane 1
|
||||
|
||||
Added 2026-08-02 from `audits/SPIKE-recovery-unit-space-2026-08-02.md` and
|
||||
`audits/CAMPAIGN-10-closeout-2026-08-02.md`. §6.1 says a Tier-1 unit lives *"on the app's own drive"*,
|
||||
which is true and is the whole story for a drive-resident app. **It is not the whole story for an app
|
||||
with no data drive, and that case was undocumented until now.**
|
||||
|
||||
**The unit is RETAINED, not staged.** `GetAppDrivePath` (`felhom-controller/internal/backup/backup.go:245-255`)
|
||||
returns the app's `HDD_PATH` if it has one and **`systemDataPath` otherwise** — what
|
||||
`internal/appbackup/paths.go:26-27` calls *"the SSD-only system-data fallback"*. Nothing deletes a unit
|
||||
after it is copied onward: the only prune is F5 (`backup.go:1053-1112`), which removes residue on an
|
||||
**old** drive when an app **moves**. So for every app without a data drive, `mp1` holds the kept copy
|
||||
indefinitely.
|
||||
|
||||
**What a unit contains, which bounds the problem** (`internal/backup/recovery_unit.go:20-25`):
|
||||
`compose/` + `db-dumps/` + `volume-dumps/` (named-volume tars) + `manifest.json`. **It never contains
|
||||
`mp8` / `HDD_PATH` userdata** — a photo library on a 1 TB drive is not in a unit, and cannot make one
|
||||
overflow.
|
||||
|
||||
**The resulting mismatch, on a default appliance:**
|
||||
|
||||
| | size | holds |
|
||||
|---|---|---|
|
||||
| `mp0` `/var/lib/docker` | **50 G** | every app's live volumes — the thing a unit copies |
|
||||
| `mp1` `/mnt/sys_drive` | **20 G** | the retained units of every **driveless** app |
|
||||
|
||||
A DB-backed app's unit is up to **~2×** its data, because it carries the volume tar **and** the SQL
|
||||
dump (measured: 21.1 GB of app data → a **40.2 GB** unit). A file-only app's is **1.00×** (measured:
|
||||
homebox 2305 MB → 2305 MB). `--sysdata-grow` (`felhom-agent/cmd/felhom-agent/main.go:178`) defaults to
|
||||
**0**, is not derived from the physical drive, and no observed box passes it — demo-hp's production
|
||||
guest 9201 ships `mp0 50G / mp1 20G`.
|
||||
|
||||
**`mp1` gates the entire app-data chain, not just Tier 1.** Tier-2 mirrors the unit *"(always)"* from
|
||||
`RecoveryUnitPath` (`internal/backup/tier2.go:302,368`) and Tier-3 carries it too, so a unit that
|
||||
cannot be written to `mp1` leaves Tier-2 and Tier-3 with nothing to copy.
|
||||
|
||||
**The bound this places on Lane 1 (§3).** Lane 1's promise is that a customer can restore an app from
|
||||
the drive alone, without the operator and without the guest — D5 (§7.4) made that true by putting the
|
||||
portable secrets in the unit. **That independence is bounded by app size**, and the bound is:
|
||||
|
||||
> On a default appliance, an app can be restored by Lane 1 only while its recovery unit still fits the
|
||||
> retained space — **≈ 19 GB of app data for a file-only app, ≈ 10 GB for a DB-backed one.**
|
||||
> Past that the unit cannot be captured, and the app falls back to Lane 2's operator-driven
|
||||
> whole-guest route.
|
||||
|
||||
Two things are deliberately **not** recorded here. **The sizing ratio is the operator's ruling**
|
||||
(**R-163**) — this section states the constraint, not a number. And **the same-device placement is
|
||||
intended, not a defect**: a driveless app's unit sits on the same SSD as its volumes, but Tier-2's
|
||||
cross-drive mirror, the whole-guest tier (which covers `rootfs + mp0 + mp1`, §6.1) and Tier-3 all cover
|
||||
device loss. What was missing was that this case existed at all, and that nothing warns when an app
|
||||
crosses the line — **R-158**.
|
||||
|
||||
## 8. The failure → recovery matrix
|
||||
|
||||
**This is the core artifact.** One row per failure. It is authoritative for recovery routes;
|
||||
|
||||
Reference in New Issue
Block a user