53 lines
3.8 KiB
Markdown
53 lines
3.8 KiB
Markdown
# felhom.eu — task reports
|
||
|
||
> **Overwrite** this file with a summary of the most recent task only (uniform with the other repos; not cumulative). The cumulative hub history lives in [hub/CHANGELOG.md](hub/CHANGELOG.md); the scripts history lives in [scripts/CHANGELOG.md](scripts/CHANGELOG.md).
|
||
|
||
## SPIKE — referential-coupling backup classification — 2026-07-14
|
||
|
||
**controller 0.130.0 (demo 9201) / agent 0.88.0 · findings-only, no production code · full audit:
|
||
`documentation/audits/SPIKE-backup-classification-2026-07-14.md`**
|
||
|
||
Empirical ground-truth pass for the userdata-backup-classification redesign
|
||
(mandatory=COUPLED / optional=DECOUPLED-precious / excluded=DECOUPLED-bulk).
|
||
|
||
### What was established
|
||
|
||
- **SQ1 — offsite carries no userdata today, no exception (source + live).** All 12 restic
|
||
snapshots are `backups/primary/<app>` units only; a full `restic ls` of the newest immich
|
||
snapshot shows compose + `immich-postgres.sql` + manifest — **immich is offbox-toggled and its
|
||
photo library is in none of its snapshots** (the latent broken-restore gap, now live-proven).
|
||
- **NEW F-S1:** tier-2 does **not** copy bulk userdata — `RunTier2` rsyncs only the unit +
|
||
`appdata/<stack>`; the `tier2.go:17` "recovery unit + bulk userdata" header and the
|
||
`main.go:1360` "namespace wholesale" comment are stale. Browsable HDD userdata has **zero
|
||
automated off-drive protection today** (live: `secondary/{radarr,jellyfin}` hold only
|
||
`recovery-unit/`).
|
||
- **NEW F-S2:** tier-2's appdata leg keys on the stack name → **paperless-ngx**
|
||
(`appdata/paperless`) is silently skipped (live contrast: `secondary/nextcloud` has `appdata/`,
|
||
`secondary/paperless-ngx` doesn't) — its document originals get no tier-2 copy.
|
||
- **SQ3 — the coupling model is VALIDATED (scratch Immich, catalog images v2.5.5).** DB-present /
|
||
library-absent restore: the server **refuses to boot** (its own `.immich` folder-check, with the
|
||
mount state remembered *in the DB*); with the skeleton recreated it boots into a **lying
|
||
gallery** — timeline reports all 3 assets, every thumbnail/original fetch is HTTP 500. No repair/
|
||
re-import path. Restoring the files → all 200 again. **Worse-than-empty: confirmed.**
|
||
- **SQ2** — full per-bind classification for all 55 catalog apps (13 hdd + 42 volume-only);
|
||
shared-media-tree consistency resolved by the "class belongs to the bind; the writing app is the
|
||
authority; `:ro` readers back up nothing" rule.
|
||
- **SQ4** — size split measured live (state 0.5 M–933 M vs bulk tree ~100 MB demo /
|
||
TB-scale prod); the two-number estimate reuses the existing `estimate.go` per-mount du +
|
||
container-view volume sizer — zero new measurement plumbing.
|
||
- **SQ5 — round-trip verdict: EXCLUSION scoping** (tar the userdata root minus excluded subtrees).
|
||
Per-bind nested tars cannot ride the v1 manifest (`HDDSubdirs` basenames; CTRL-001 forbids path
|
||
separators); exclusion scoping needs no manifest/import change and stays backward-compatible.
|
||
Proposed `backup:` block schema + **two-level default** (no block → legacy per-tier behavior;
|
||
block present → unlisted writable binds mandatory) — the task's single-level mandatory-default
|
||
premise would have shipped unmigrated media trees offsite (quota blow-up) and was corrected.
|
||
- **SQ6** — `.fab` over-capture quantified on the live tree (radarr bundle carries ~1.6× its own
|
||
data even on the small demo tree, unbounded in prod); the live marker-file export leg is blocked
|
||
on the customer-owned dashboard login (same blocker as the open Viktor full-circle test).
|
||
|
||
### Cleanup
|
||
|
||
Scratch Immich fully removed (containers/volumes/network/files/images; verified 0 spike containers,
|
||
controller healthy). Restic inspection read-only (`--no-lock`), 12 snapshots before and after. No
|
||
drive namespace, tier-2 target, or deployed app touched.
|