v0.55.0: Phase 3 — auto off-drive Tier 2 (rootfs-headroom guard)

Tier 2 rsync-mirrors each HDD app's recovery unit + appdata to a DIFFERENT physical
disk (the only off-drive protection bind-mounted userdata can get; PBS can't reach it).
Auto-enabled, auto-target: prefer another registered drive (different physical disk via
system.SamePhysicalDevice), else the internal SSD for SMALL units only — with a
size-aware headroom guard that REFUSES rather than fill the ~8G guest rootfs, recording
an honest "needs 2nd HDD" status. Status persisted via the surviving CrossDriveBackup;
"2. mentés" UI card now populated. Daily tier2-backup job + POST /api/backup/tier2.

- backup/tier2.go (engine+selection+headroom), tier2_test.go (headroom arithmetic)
- system.SamePhysicalDevice (linux Stat_t.Dev + stub)
- handlers.go Tier2 UI population + tier2DestLabel; backups.html honest no-target reason
- fixed stale TestBackupCopiesOnPath (old felhom-data layout -> in-guest layout)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-13 13:24:49 +02:00
parent d8fe8f5ead
commit d2071430ea
12 changed files with 446 additions and 5 deletions
+27
View File
@@ -1,5 +1,32 @@
## Changelog
### v0.55.0 — Phase 3: auto off-drive Tier 2 (rootfs-headroom guard, durable off-disk target) (2026-06-13)
Tier 2 = an **off-drive copy** of each HDD app's recovery unit + bulk userdata to a **different physical
disk** — the only off-drive protection browsable HDD userdata can get (PBS can't reach bind mounts).
Auto-enabled for every HDD app; the target is auto-picked and the dangerous case (the small guest
rootfs) is refused rather than filled.
- **Engine** `internal/backup/tier2.go` (`RunTier2`/`RunAllTier2`): rsync `-a --delete` of the recovery
unit (`backups/primary/<app>/`) and the app's `appdata/<app>/` to `<target>/backups/secondary/<app>/`.
restic is **not** revived — plain browsable mirror.
- **Auto target selection:** prefer another registered user-data drive on a **different physical disk**
(can hold bulk userdata); else fall back to the internal SSD for **small units only**. Off-disk is
enforced by `system.SamePhysicalDevice` (block-device identity; new exported helper, linux + stub) —
defense-in-depth re-checked before the copy.
- **Rootfs-headroom guard (the key safety):** the SSD target is the ~8 GB guest rootfs, so a size-aware
guard (`tier2FitsHeadroom`, unit-tested) **refuses** unless the unit fits while leaving a reserve free
(`max(2 GB, 20% of total)`). When nothing fits, it records an **honest** "needs a 2nd HDD" status
rather than silently doing nothing or endangering the rootfs.
- **Status + UI:** results persist via the surviving `settings.CrossDriveBackup` (rsync method, dest,
last-run/status/size). The "2. mentés" card is now **populated** (`buildAppBackupRows`): real target
("belső SSD (csak DB/konfiguráció)" vs an external drive) on success, or the honest no-off-drive-target
reason. Notifications via the surviving `NotifyCrossDrive{Completed,Failed}` hooks.
- **Scheduling + trigger:** daily `tier2-backup` job (03:30, after the DB dump); manual
`POST /api/backup/tier2`.
- Fixed a stale pre-existing test (`TestBackupCopiesOnPath`) that still used the old
`felhom-data/backups/secondary` layout — now the Model-A in-guest layout the Tier 2 copies actually use.
### v0.54.0 — Phase 2b: restore-from-recovery-unit + fail-closed data-key gate (2026-06-13)
Restore now recreates an app from its on-drive recovery unit **plus the guest's own secrets** — never