Files
felhom-controller/REPORT.md
T

9.3 KiB
Raw Blame History

REPORT — Placement hardening + enlarge-blocked delivery chain (Task 3a-fix) — controller v0.134.1 / hub v0.55.0

Summary

Two follow-ups on Task 3a: (1) four hardening fixes to the not-yet-live place-to-live flow surfaced by reviewer source-validation of v0.134.0, and (2) the three-link delivery chain for the offbox_enlarge_blocked notification (hub ingestion + the customer whitelist/migration/checkbox on the controller). No new architecture.

Baselines (live-verified at session start)

Repo main @ start Version
felhom-controller 482d0d9 v0.134.0 v0.134.1
felhom.eu (hub) 8d85da7 hub v0.54.0 hub v0.55.0

WIP fence (§9.0) — recorded

The felhom.eu clone was CLEAN at session start (git status empty; hub/internal/claim/ is tracked/committed at 8d85da7, not WIP). The ~215-line foreign WIP the prompt warned about was already resolved (committed) — no fence trigger. After my edits, git status showed only the 5 task files (4 named + hub/internal/notify/templates_offbox_test.go, see note below); no foreign WIP appeared or was touched. Staged with explicit per-file git add; pulled with --rebase --autostash.

Deviation noted transparently: Part 11 / §15 explicitly require a FormatCustomerEmail fallback assertion, which can only live in hub/internal/notify/. §9.0(b)/§12 restrict hub edits to 4 named files and say "do not touch internal/notify/" — but that prohibition's stated rationale (WIP zone + the customerMessages trap) is void here: the WIP is absent and the test adds NO customerMessages entry and touches NO notify logic (a new isolated file). I added it to satisfy the explicit deliverable; it locks in the deliberate non-change. Flagged here for the reviewer's judgment.

Files

felhom-controller (v0.134.1):

  • internal/backup/offbox_restore.go — F-3a-1a/1b/2/4 in PlaceOffsiteRestore; F-3a-3 in mapOffsiteRestorePaths.
  • internal/settings/settings.goDefaultEnabledEvents += offbox_enlarge_blocked; GetNotificationPrefs append-if-absent migration + appendIfAbsent helper.
  • internal/web/handlers.gooffbox_enlarge_blocked in the prefs single-event slice.
  • internal/web/templates/settings_notifications.html — the new checkbox.
  • new internal/backup/offbox_place_test.go (5 tests) · new internal/settings/notif_migration_test.go (3 tests).
  • CHANGELOG / REPORT / CONTEXT.

felhom.eu (hub v0.55.0):

  • hub/internal/api/handler.gooffbox_enlarge_blocked in allowedEventTypes (+ gofmt realignment).
  • hub/internal/api/event_test.go — acceptance case (+ the 400 red-proof target).
  • new hub/internal/notify/templates_offbox_test.go — raw-message fallback assertion.
  • hub/CHANGELOG.md · manifests/hub.yaml (image → :0.55.0).

Untouched: hub internal/notify/dispatcher.go/templates.go/store.go; no customerMessages entry; placement stays non-auto-deploying; no engine changes beyond offbox_restore.go.

Commits

  • felhom.eu hub: 08fef48
  • felhom-controller: 0cfcc42

Tests — results

go build ./... && go vet ./... && go test ./...green, both repos. Controller +8 tests, hub +2 tests.

§10 red-proofs (mutate → FAIL → revert), all verified

ID Mutation Test
A restore AppNamespaceRoot fallback (neuter undeployed guard) TestPlace_UndeployedRefused (copier ran)
B delete placement headroom gate TestPlace_HeadroomRefused (copier ran)
C neuter the stat pre-pass TestPlace_IncompleteScratchRefusedNoCopies (copies > 0)
D restore p != oldNs && escape condition TestMapOffsiteRestorePaths_RefusesNamespaceRoot (junk placement accepted)
E drop post-success scratch cleanup TestPlace_ScratchLifecycle (scratch survived)
F2 unconditional append TestGetNotificationPrefs_AlreadyPresentNoDuplicate (duplicate)
Hub remove the allowlist entry TestHandleEvent_OffboxEnlargeBlockedAccepted (400)

All reverted; post-revert both suites green; no mutation residue.

Deploy / verify

  • Hub: built + pushed felhom-hub:0.55.0 on 180; manifests/hub.yaml → :0.55.0 (commit 08fef48); ArgoCD hard-refresh + patch-sync → Synced/Healthy, deploy/hub rolled out to image :0.55.0, startup Listening on :8080.
  • Controller: built + pushed felhom-controller:0.134.1 on 180; deployed to guest 9201 → Up (healthy). Commit 0cfcc42.

§13 live validation — ALL LEGS PASS

The dashboard session was established with 180:~/.config/credentials C4_PASSWORD (the value is single-quoted in the file — stripping the quotes is required; an earlier run that kept the quotes produced a false Hibás jelszó, since corrected). Login → 302 + felhom_session cookie; the flow ran entirely on 180 (LAN → dashboard) so the password was never transferred or echoed.

  • Leg 2 — manual run (LIVE): POST /backup/offbox/run → 302; run log: backed up calibre-web (…, 1 mandatory path(s)) (enlarged shape), audiobookshelf/immich not deployed — offsite push is unit-only (the §2.4 undeployed WARN firing live), backup OK: 3 app(s) backed up, 15 snapshot(s), 49s. Leg 2a confirmed by snapshots --no-lock --json: calibre-web newest = […/backups/primary/calibre-web, …/userdata/media/books] (unit + mandatory).
  • Leg 2b — raw-data quota (LIVE): stored repo_size_bytes = 280,932,890 (267.9 MB) == live stats --mode raw-data — down from 744 MB modeless pre-3a.
  • Leg 2c — forget grouping: both call sites carry --group-by host,tags (deployed + unit-tested); a successful forget is logged SILENTLY by design (only failures log), so no live line — the ~40s gap before "backup OK" + the stable 15-snapshot retention evidence it ran.
  • Leg 3 — unit-only scratch restore (LIVE): POST mode=unit → scratch landed on the DATA DRIVE /mnt/felhom-drives/nas-media/backups/offsite-restore/calibre-web (NOT the rootfs — F-A1); the reconstructed tree contains ONLY …/backups/primary/calibre-web/{compose,volume-dumps} (unit-only scope, --include working); rootfs DataDir/offbox-restore absent.
  • Leg 4 — full restore two-step (LIVE): step 1 mode=full → 302 to /backups/restore?full_prep=calibre-web&full_size=258.3+MB (size shown BEFORE start); step 2 mode=full&confirm=1 → the scratch now also holds …/userdata/media/books/.calnotes/… (whole snapshot, SP-3.1 abs-path reconstruction).
  • Leg 5 — prefs round-trip (LIVE): /settings/notifications renders event_offbox_enlarge_blocked checked (migration surfaced it enabled); a save round-trip → the type survived (F3 checkbox-drop trap avoided); the hub logged Notification preferences updated for demo-felhom: events=[… offbox_enlarge_blocked …] — the migrated type reached the hub whitelist end-to-end.
  • Leg 6 — delivery (LIVE, one synthetic event): pushed offbox_enlarge_blocked/warning through the real controller→hub path → hub_status 200 (was 400 before v0.55.0); hub logged Event from demo-felhom: offbox_enlarge_blocked (warning) + Operator email sent for demo-felhom/offbox_enlarge_blocked. The message was the debug endpoint's generic text (it fixes the body); the dynamic two-number message survival is unit-proven (templates_offbox_test.go) — no customerMessages entry, so FormatCustomerEmail falls back to the raw message.
  • Leg 7 — hygiene: the calibre-web scratch (legs 34) removed; 180 session temp files removed; read-only inspection scripts removed from the container. PlaceOffsiteRestore was NOT run (6D).

NOT yet live-validated — awaiting CAMPAIGN-6D (supervised)

  • PlaceOffsiteRestore against live data (the STOP boundary — merges into live app data).
  • Organic enlarge-block firing (demo repo 268 MB / quota 50 GB won't trip; unit-tested + the delivery chain proven live via the synthetic event).
  • The SQ3 immich offsite-only full-circle restore-and-boot.

Observations (documented, not acted on)

  • Getter-based migration trade-off: the append-if-absent migration lives in GetNotificationPrefs (per §2.2's explicit instruction). Consequence: because the getter always surfaces the type, a customer who later unchecks this one warning and saves will see it re-enabled on the next page load — the getter can't distinguish "never had it" from "opted out" without a persisted migration-marker. Acceptable for a first delivery (a quota warning), but a future one-time persisted migration would honor a deliberate opt-out. Noted, not changed (the task specified the getter).
  • The stale documentation/controller/backup-architecture.md ("restic is gone from the controller") remains — flagged in the v0.134.0 report; still its own task.
  • Demo-state touches during §13 (benign, noted): the leg-5 prefs save persisted the demo customer's enabled_events to explicitly include offbox_enlarge_blocked (previously getter-migrated — idempotent, no behavior change); leg 6 sent one operator test email. Legs 34 scratch dirs were removed (leg 7). No live app data was modified (PlaceOffsiteRestore not run).
  • Credential note: C4_PASSWORD in 180:~/.config/credentials is single-quoted — strip the quotes before use (an unstripped value yields a false Hibás jelszó). The credential is valid.