9.3 KiB
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 inPlaceOffsiteRestore; F-3a-3 inmapOffsiteRestorePaths.internal/settings/settings.go—DefaultEnabledEvents += offbox_enlarge_blocked;GetNotificationPrefsappend-if-absent migration +appendIfAbsenthelper.internal/web/handlers.go—offbox_enlarge_blockedin the prefs single-event slice.internal/web/templates/settings_notifications.html— the new checkbox.- new
internal/backup/offbox_place_test.go(5 tests) · newinternal/settings/notif_migration_test.go(3 tests). - CHANGELOG / REPORT / CONTEXT.
felhom.eu (hub v0.55.0):
hub/internal/api/handler.go—offbox_enlarge_blockedinallowedEventTypes(+ 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.0on 180;manifests/hub.yaml→ :0.55.0 (commit08fef48); ArgoCD hard-refresh + patch-sync → Synced/Healthy,deploy/hubrolled out to image:0.55.0, startupListening on :8080. - Controller: built + pushed
felhom-controller:0.134.1on 180; deployed to guest 9201 →Up (healthy). Commit0cfcc42.
§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/immichnot 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 bysnapshots --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) == livestats --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,--includeworking); rootfsDataDir/offbox-restoreabsent. - 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 2mode=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/notificationsrendersevent_offbox_enlarge_blockedchecked (migration surfaced it enabled); a save round-trip → the type survived (F3 checkbox-drop trap avoided); the hub loggedNotification 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 loggedEvent 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) — nocustomerMessagesentry, soFormatCustomerEmailfalls back to the raw message. - Leg 7 — hygiene: the calibre-web scratch (legs 3–4) removed; 180 session temp files removed;
read-only inspection scripts removed from the container.
PlaceOffsiteRestorewas NOT run (6D).
NOT yet live-validated — awaiting CAMPAIGN-6D (supervised)
PlaceOffsiteRestoreagainst 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_eventsto explicitly includeoffbox_enlarge_blocked(previously getter-migrated — idempotent, no behavior change); leg 6 sent one operator test email. Legs 3–4 scratch dirs were removed (leg 7). No live app data was modified (PlaceOffsiteRestorenot run). - Credential note:
C4_PASSWORDin180:~/.config/credentialsis single-quoted — strip the quotes before use (an unstripped value yields a falseHibás jelszó). The credential is valid.