From 2a2514255b4c276326ede918a56dfb5bca82df68 Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Tue, 16 Jun 2026 11:16:23 +0200 Subject: [PATCH] docs: REPORT.md for controller v0.69.0 infra-backup stub removal Co-Authored-By: Claude Opus 4.8 (1M context) --- REPORT.md | 125 +++++++++++++++++------------------------------------- 1 file changed, 38 insertions(+), 87 deletions(-) diff --git a/REPORT.md b/REPORT.md index 97e76f3..da55c26 100644 --- a/REPORT.md +++ b/REPORT.md @@ -1,96 +1,47 @@ -# REPORT — two UI cleanups + M3 live re-verification (2026-06-15) +# REPORT — controller v0.69.0: remove dead infra-backup stubs + unused restic-password report field (2026-06-16) -**Deployed:** controller **v0.68.3** on guest 9201 / felhom-pve (bootstrap-managed; healthy). -**Commits:** `37ed757` (v0.68.2 CSS) · `a821a9d` (v0.68.3 refresh fix). Trunk-based, pushed to `main`. +**Deployed:** controller **v0.69.0** on guest 9201 / felhom-pve (bootstrap-managed; healthy, hub-reporting OK). +**Scope:** controller half of Phase-1 Infra Backup retirement (hub **v0.12.0**). See +`felhom.eu/documentation/audits/SPIKE-infra-backup-2026-06-15.md`. Commit `9f59bc2`. ---- +## What changed (pure dead-code removal — no behaviour change) -## Part 1 — stack-card state-badge clipping fix (v0.68.2, CSS-only) +- **Removed `report.Pusher.PushInfraBackup`** (`internal/report/pusher.go`) — pushed the infra-backup + payload to the now-removed hub endpoint `POST /api/v1/infra-backup`. Dead since slice 8C; no callers. +- **Removed `notify.Notifier.NotifyBackupCompleted`** (`internal/notify/notifier.go`) and the + `backup_completed` event — caller-less since whole-guest backup moved to the agent in slice 8C. The + hub's backup-deadline check now reads the agent host-report's PBS snapshots instead. `NotifyBackupFailed` + and the DB-dump notifiers are untouched and still used. +- **Removed `report.BackupReport.ResticPassword`** (`internal/report/types.go`, `json:"restic_password"`). -**Symptom:** on the /stacks page, an **unhealthy** app's state badge clipped — "Nem egészséges" rendered -as "Ner…". Healthy / not-deployed cards were fine. +## STEP-2 gate — confirmed before removing ResticPassword -**Root cause (confirmed):** `.stack-detail-header` is `flex` / `space-between` holding `.stack-title-row` -(logo + title + subdomain link + the `route-unpublished` warning) and `.stack-state-badge`. Only on an -unhealthy app does the long "⚠ URL nem elérhető – útvonal nincs publikálva" warning render, inflating the -title-row. The title-row had no `min-width:0` (so it wouldn't shrink below its content) and the -`white-space:nowrap` badge had no `flex-shrink:0` (so the flexbox compressed the *badge* instead → clip). +The task required proving the live builder leaves `ResticPassword` empty (else it would be an ongoing +restic-password leak to the hub). Confirmed two ways: +- **Source:** `buildBackupReport` (`internal/report/builder.go`) constructs `BackupReport{Enabled:…}` + + `LastDBDump` only; the comment at the disk-tier seam states restic/snapshot fields are left zero. No + code anywhere assigns `BackupReport.ResticPassword`. +- **Live data (hub DB, read-only):** the latest controller reports (v0.68.3) carry **no** + `restic_password` field at all. So no ongoing leak — safe to remove. -**Fix** (`internal/web/templates/style.css`): -```css -.stack-title-row { flex: 1; min-width: 0; } /* shrinks + wraps its own content */ -.stack-state-badge { flex-shrink: 0; } /* never compressed */ -``` +> **Flagged (separate, worse historical finding — not in this task's scope):** the hub's legacy +> `reports` table holds **thousands of historical rows with a plaintext `restic_password` value** from +> *old* controller versions that DID populate it. The current controller does not, and this removal +> makes the field structurally unsendable, but the historical rows persist on the hub — a distinct +> purge/rotation decision for the operator. -**Browser-verified on /stacks (demo, guest 9201):** -- BEFORE (v0.68.1): komga badge clipped to "Ner…" at the card's right edge (screenshot captured live). -- AFTER (v0.68.2): komga badge reads the full **"Nem egészséges"**; the "⚠ URL nem elérhető…" warning - **wraps within the title column** (screenshot captured live). -- Regression: Mealie ("Fut", healthy) and all "Nincs telepítve" cards visually unchanged. +## Verification -**Defensive note (not applied):** the spec offered adding `flex-shrink:0` to -`.badge-missing-storage`/`.badge-orphaned` *if* a card showing BOTH unhealthy AND a missing-storage/ -orphaned badge still clipped. No such card exists live right now, and the primary fix's mechanism -(title-row `min-width:0` absorbing all shrink) already shields sibling badges, so it was left out — a -one-liner if that combo ever appears. - ---- - -## Part 2 — M3 live re-verification (no code change — the migrate path is correct) - -**Goal:** prove the migrate merge-walk re-asserts the `2775`-setgid/gid-1000 convention on a -**pre-existing STALE dir** (the B3 calibre case), which the acceptance audit had only covered by unit -test + static convention. - -**Method:** two full **migrate-all** runs driven through the dashboard UI (the real -`/api/storage/migrate` endpoint, all 8 flash apps: audiobookshelf, calibre-web, immich, jellyfin, komga, -paperless-ngx, radarr, romm): **flash→usb**, then **usb→flash** (restoring apps to the default drive). -Before each, the migrate **target** was pre-seeded host-side with deliberately-stale `0755`, -**non-setgid** userdata dirs; `stat` recorded. - -**Result — PASS (clean, non-hollow):** -- Pre-seeded `userdata/documents` at `755 gid1000` — a dir **no app mounts** — became - **`2775 gid1000`** after migrate (the `EnsureUserdataDir` re-assert in `walkMerge`, migrate.go:854-859). - Same for the seeded `userdata` root and `userdata/import`. Whole target userdata tree = `2775`. -- Integrity: static seeded files (`b3-document.txt`, `b3-movie.txt`, `b3-photo.txt`, `demo.jpg`) had - **identical sha256** before/after. -- All 8 apps redeployed **healthy** on the target (komga "unhealthy" = its pre-existing - `/api/v1/actuator/health` 401 — serving, unrelated to migration). - -**Secondary finding — the `import/calibre` 755 is an APP clobber, not a migrate bug.** In both runs -`userdata/import/calibre` ended at `755`. Isolated live: **calibre-web (CWA)** bind-mounts -`${USERDATA_PATH}/import/calibre` as `/cwa-book-ingest` and **chmods it to 755 (strips setgid) on every -startup** — proven by setting it to `2775` and restarting calibre-web alone (reverted to `755`, no -migration). The merge-walk *does* re-assert `2775` during copy; calibre-web re-clobbers it during the -flip/redeploy phase (this is also why the source flash calibre was already `755` pre-migration). -**migrate.go was therefore NOT modified** — the M3 path works. If the convention must hold for that -single-app ingest dir, the fix belongs in the catalog/app layer (an `umask`/entrypoint wrapper for CWA), -not in migrate. - ---- - -## Part 3 — Beállítások endless-refresh loop fix (v0.68.3) — found during Part 2 - -**Symptom (user-reported):** after any migration finished, the settings page reloaded itself every -~1.5 s, forever. - -**Root cause:** `MigrationStatus` keeps returning the last `done` job indefinitely (never cleared). The -settings page's resume-view IIFE called `migWatch()` for *any* returned job, and `migWatch`'s `done` -branch does `setTimeout(()=>location.reload(),1500)`. So every load saw the persisted `done` job → watched -it → reloaded → saw it again → endless loop. - -**Fix** (`internal/web/templates/settings.html`): the resume-view now starts the watcher **only for an -in-progress job** (`phase!=='done' && phase!=='aborted'`). The one-time post-completion reload still fires -from the *active* watcher started by `storageMigrateAll`, so a real migration refreshes drive state once -when it finishes — but a stale terminal job no longer triggers the loop. - -**Browser-verified on v0.68.3:** with a persisted `done` job present in the journal, the settings page -stayed put for 25 s (`navType:navigate`, planted marker survived, migration panel idle) — no reload. - ---- - -## Resting state -- Apps healthy on **felhom-flash** (the default drive); **felhom-usb** cleaned/empty. -- Controller **v0.68.3** deployed live on guest 9201. -- Build/deploy: built+pushed on 192.168.0.180, deployed via the bootstrap mechanism +- `go build ./... && go test ./...` — green; `go vet` clean. No residual references to + `PushInfraBackup` / `NotifyBackupCompleted` / `BackupReport.ResticPassword` (the `config.go` + `ResticPasswordFile` is the separate restic-password-file *path* config, intentionally untouched). +- Built `felhom-controller:0.69.0`, deployed to guest 9201 via the bootstrap mechanism (`docker pull` → `/etc/felhom-controller-image` → restart `felhom-controller-bootstrap.service`). +- Live: container `Up … (healthy)` on 0.69.0; startup clean (no errors/panics); hub connectivity + `HTTP 200`; `Hub report pushed successfully (8309 bytes)` — confirming the caller-less removal causes + no behaviour change and reporting still works. + +## Out of scope + +Credential rotation (operator); the historical `reports`-table restic_password rows (flagged above); +the Komga healthcheck.