Files
felhom-controller/REPORT.md
T

132 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.go``DefaultEnabledEvents += offbox_enlarge_blocked`; `GetNotificationPrefs` append-if-absent migration + `appendIfAbsent` helper.
- `internal/web/handlers.go``offbox_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.go``offbox_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
**Credential blocker (honest):** the sanctioned credential `180:~/.config/credentials` `C4_PASSWORD`
(len 14) is **stale** — the demo customer changed their dashboard password after the one-time claim
(login returns `Hibás jelszó`, no session). So the dashboard-session legs (manual run trigger, the two
scratch-restore legs, the UI prefs round-trip, the synthetic delivery event) could NOT be driven. I
did **not** reset the customer's password or extract the controller's hub API key to force a session
(both would alter demo state / overreach). What was validated read-only / organically instead:
- **Leg 2a — enlarged snapshot shape (LIVE, organic):** the daily 04:15 CEST scheduled run (2026-07-15
02:15 UTC, v0.134.0) produced the multi-path shape. `restic snapshots --no-lock --json` newest per
tag: **calibre-web** = `["…/nas-media/backups/primary/calibre-web", "…/nas-media/userdata/media/books"]`
(unit + MANDATORY userdata); the 2026-07-14 calibre-web snapshot was unit-only — the shape change is
visible across runs. immich / audiobookshelf newest = unit-only (no resolvable mandatory HDD bind).
- **Leg 2b — raw-data quota (LIVE):** stored `repo_size_bytes = 280,932,901` (267.9 MB) == live
`stats --mode raw-data` (280,932,901) exactly — the raw-data switch refreshed the stored value (was
744,763,144 / 710 MB modeless before 3a). 15 snapshots retained.
- **Leg 2c — forget grouping:** in the deployed code (both call sites) + unit-tested; the live log line
was not capturable (the container restarted at the v0.134.1 deploy, rotating the 04:15 run's logs).
- **Delivery chain (partial, read-only):** hub v0.55.0 allowlist entry is LIVE (deployed Synced/Healthy
+ the hub acceptance test). Controller notifier enabled (`Notifier enabled (hub: https://hub.felhom.eu)`).
The prefs migration is confirmed getter-only (stored `enabled_events` does NOT persist
`offbox_enlarge_blocked` — matches the design; surfaced at read).
- **Hygiene:** no scratch dirs were created (legs 34 did not run); the login-attempt temp files on 180
were removed; read-only inspection scripts removed from the container.
## NOT yet live-validated — awaiting a supervised session with the CURRENT customer password / 6D
- Manual run trigger via the UI endpoint (organically covered by the scheduled run for shape + size).
- Unit-only + full scratch restore via the real endpoints (legs 34).
- The prefs checkbox round-trip in the live UI (leg 5) — migration + sync are unit-tested + getter
confirmed live; the checked-render + save-survives round-trip needs the session.
- The synthetic `offbox_enlarge_blocked` delivery event + customer email (leg 6) — hub ingestion is
live; end-to-end delivery needs a session or the demo API key.
- `PlaceOffsiteRestore` against live data; organic enlarge-block firing (demo repo 268 MB / quota 50 GB);
the SQ3 immich offsite-only full circle.
- **Follow-up flagged:** refresh `C4_PASSWORD` in `180:~/.config/credentials` (the demo customer's
current dashboard password) so future in-session live legs aren't blocked.
## 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.