Files
felhom-controller/REPORT.md
T

90 lines
11 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 — v0.126.0.3: UI uniformity bundle + the claimed-box wizard CSRF fix
## v0.126.3 (2026-07-13, same supervised session) — storage wizard CSRF on claimed boxes
First live hit during the agent-0.87.0 drill wizard leg (operator session): the wizard OFFERED
the hot-added disk (the 0.87.0 fix works in the UI), but POST /api/storage/init died with
"CSRF token missing or invalid" (controller log: `token mismatch`). Root cause:
`storageWizardPageHandler` rendered /storage/init + /storage/attach via raw `render()` — the
ONLY raw-render page handler left — so the csrf-meta token was EMPTY, and the wizard's fetch()
posts exactly that token. LATENT until the claim arc: an unclaimed box skips CsrfProtect
entirely, so the wizard had never run against a password-gated box. Fix: `executeTemplate`
(CSRF auto-injection). Regression test `TestStorageWizardPages_CarrySessionCSRFToken` renders
both wizard pages with a REAL session and asserts the meta carries the session token;
red-proven (render() swap → both cases fail on the empty meta). Deployed drill + demo
(0.126.3 healthy both); the restart clears in-memory sessions → operator re-login required.
---
# v0.126.0/.1/.2: UI uniformity bundle (shared app-list rows, infra identity, restore-form polish, mojibake + dedup gates, honest stale line)
**Date:** 2026-07-13 · **Version:** controller v0.126.2 LIVE (from v0.125.0; .1/.2 are same-session live-QA fixes) · **MinAgent:** 0.81.0 (UNCHANGED) · **Floor:** untouched (0.122; the recorded rule stands — never halt above 0.124.0 without the strand fix) · Controller-only, presentation-layer — NO backup/toggle/engine behavior change (render tests pin the action markup).
**Baseline confirmed:** `main` @ `e0618af` = v0.125.0 (drill + demo both 0.125.0 healthy before deploy).
## Commits (per part, pushed to main)
1. `07e3c37` — gate housekeeping: `backups_split_move_check.py` RETIRED (one-shot v0.124.0 migration gate, served its purpose; this task legitimately rewrites the moved blocks). Doc refs updated.
2. `3eacb6c`**Part A**: `templates/app_row.html` `app_list_row`/`app_list_row_end` (the layout_start/_end idiom) — icon+name(+optional secondary) left, caller action block right, compact 44px row. Applied to: Távoli mentés toggles, Visszaállítás restore-to-verify + .fab lists, dashboard Telepített alkalmazások (state edge + data-href preserved); Alkalmazások collapsed headers ALIGNED (icon left, status dot moved right before chevron; expander untouched — the ONE allowlisted aligned copy). funcmap `dict`+`appHref`; `OffboxAppRow`/`AppBackupRow` gain `Slug`; old `.stack-card`/`.storage-path-item` row CSS+markup retired. NEW `scripts/app_row_dedup_gate.py`.
3. `4f06149`**Part B**: `inframeta.go` display-only map — cloudflared → „Cloudflare Tunnel", traefik → „Traefik", filebrowser → „FileBrowser" + the spec'd Hungarian descriptions; embedded generic `/static/infra-logo.svg` (Lucide-style server) via the FallbackIcon slot + the stacks-card onerror chain; filebrowser = the ONLY Linked stack (files.<domain> Megnyitás on the dashboard row; the card link comes from Subdomains as before).
4. `67494f9`**Part C**: .fab password field placeholder „Opcionális jelszó" + helper under the field („Üresen hagyva a csomag titkosítás nélkül készül."); import-page bundle-password input got `.form-input`.
5. `458cf4a`**Part D**: `scripts/mojibake_gate.py` (templates + Go sources, strict UTF-8 + zero Ã/Â/Ă/ă/˘/ˇ signature chars, allowlist ZERO, Python per the multibyte rule).
6. `02c84e1`**Part E**: `offboxWarningDisplay(lastWarning, toggledCount)` pure display pick — stale „nincs mentésre jelölt alkalmazás" run-warning → „A kijelölés módosult az utolsó futás óta — a következő távoli mentés már tartalmazza." once ≥1 app toggled (rendered NEUTRAL — reassurance, not deviation); 0 toggled keeps the v0.123.0 line + warn color verbatim; quota/partial warnings pass through.
7. `912e6a6` — docs (CHANGELOG AE, REUSE canonical-row entry + gate list, README, CONTEXT).
8. `41c8fc2`**v0.126.1 (live-QA finding 1)**: `.form-input`/`.form-row` had NO backing CSS rule at all (only the `.form-control` twin was styled) — the TRUE root cause of the operator's "unstyled clipped placeholder" screenshot. Rules added (same visual spec as `.form-control`).
9. `21d0f66`**v0.126.2 (live-QA finding 2)**: Cloudflare edge caches `/static/style.css` for 4h (`Cf-Cache-Status: HIT`) → every release served the PREVIOUS release's CSS until TTL. Stylesheet link now `?v={{.Version}}` (auto-busts per release; hub v0.47.0 gotcha class, controller instance extinct).
## Part D finding — where the mojibake actually is
Byte-level sweep (python, raw-bytes + decoded-text passes over every template/Go/CSS/JS/MD file): the committed source contains **ZERO** double-encoded literals — nothing to fix in code. The „Tárhely"-class text on the live import page is the **felhom-usb DRIVE-LABEL DATA** on the demo box: `settings.json` `storage_paths[0].label` = `'Tárhely (felhom-usb)'` (double-encoded á; the default-label pattern is `settings.go` `Tárhely (%s)`, mangled by the bash-era sweep). Repair is the label-edit UI step (see Open items — needs an authenticated demo session). The gate now makes the code-side class impossible.
## Red-proofs (all four run → fail → revert; verbatim)
- **A (dedup gate):** pasted a pre-0.126.0 `storage-path-item` row block into backups_remote.html → `app_row_dedup_gate: FAIL … forbidden old structure 'storage-path-item' survives` exit 1; reverted → OK.
- **B (infra link guard):** `Linked: true` on cloudflared → `TestInfraMetaFor_RegularAppIsNil: cloudflared must exist and must NOT be Linked (no customer UI)` FAIL; reverted → PASS.
- **D (mojibake gate):** reintroduced `Tárhely` into app_import.html → `mojibake_gate: FAIL (allowlist is zero — fix the text)` naming file:line, exit 1; reverted → OK (292 files clean).
- **E (display pick):** pick removed (unconditional pass-through) → BOTH the unit case (`stale warning + 1 toggled: got "Sikeres — nincs mentésre jelölt…"`) AND the render case (`selection-changed note missing with 1 app toggled`) FAIL; reverted → PASS.
## Tests + gates
`go test ./...` — all 23 packages ok (full suite, twice). New tests: `app_row_test.go` (4 per-surface render tests incl. filter-preservation on the restore lists), `inframeta_test.go` (identity on both surfaces + exactly-one-https guard + Linked unit), `offbox_warning_display_test.go` (pick unit + both render branches). Final gate outputs:
```
integrity gate OK — every JS element-ID reference resolves within its own template
emoji gate OK — no emoji in web/setup templates
native-confirm gate OK — no native confirm()/prompt() in templates
offbox rename gate OK — Tier-3 is 'Távoli mentés' everywhere customer-facing
docker -v gate OK — every volume mount is named-volume or proven host-visible
app_row_dedup_gate: OK (row markup single-sourced in app_row.html; 28 templates scanned)
mojibake_gate: OK (292 files clean — no double-encoding signatures)
```
(`backups_split_move_check.py` retired in commit 1.)
## §13 live validation (drill box, 0.126.2, claude-in-chrome; demo deployed + healthy)
Deploys: 0.126.0 → drill (nested qm300 guest via root@192.168.0.152) then demo 9201; live QA found the two CSS defects → 0.126.1, 0.126.2 built + deployed the same way; **both boxes now `0.126.2 Up (healthy)`**.
**Demo dashboard access:** the demo box is CLAIMED (customer-owned password) — CC does not enter credentials, so the browser click-through ran on the **drill scratch box** via a REVERSIBLE gate-lift: `settings.json` backed up, `password_hash` blanked over SSH (stop `.path`+service first — the path unit instantly restarts the container and the running instance rewrites settings.json, first attempt proved it), visual QA done, then the ORIGINAL hash restored byte-identical (`$2a$10$pDC…` before == after), backup removed, login gate verified back ON (screenshot). Drill is geo-HU-gated (enkisfelhom.hu WAF); window ≈15 min.
Evidence (screenshots in-session):
- **Dashboard**: all four rows through the shared partial — compact uniform height, ActualBudget (icon+name+desc, Fut/Megnyitás/restart/stop/Napló unchanged), Cloudflare Tunnel + Traefik with the curated descriptions + generic server icon + Védett and **NO Megnyitás**, FileBrowser with its logo + Megnyitás → files.enkisfelhom.hu.
- **App cards** (/stacks): infra cards show curated name+description; subdomain-link ONLY on FileBrowser; Védett rendszerkomponens chips.
- **All four backup pages** at default width; narrow-width render (≈900px window) shows no clipping (the CDP screenshotter re-emulates 1568px on most captures — one genuine narrow capture obtained; noted as tool limitation).
- **Part E live, both branches**: page loaded with the drill's REAL stale zero-toggle LastWarning + ActualBudget toggled → the neutral „A kijelölés módosult…" note; toggle OFF → the ORIGINAL amber „Sikeres — nincs mentésre jelölt alkalmazás" + v0.123.0 hint verbatim (state-2 status card „Aktív — nincs kijelölt alkalmazás" also correct); toggle back ON → note returns. Box left as found (toggle restored).
- **Click-through, behavior unchanged**: offbox toggle round-trip (flash „A távoli mentés beállítása frissítve."); .fab download → estimate → inline felhomConfirm „A csomag becsült mérete: 3.6 KB…" Igen/Mégse (no native dialog) → Mégse cancels cleanly; backups-apps expander opens/closes with the aligned header (icon left, dot+chevron right).
- **Part C**: after 0.126.2 the .fab password field renders themed (label above, dark input, „Opcionális jelszó", helper below).
## Open items / human steps (demo box — needs Viktor's dashboard login)
1. **felhom-usb label repair (the standing data item):** log into felhom.demo-felhom.eu → Tárhely → Meghajtók → rename `Tárhely (felhom-usb)``Tárhely (felhom-usb)` (before/after screenshot). CC documented the broken value read-only; everything else about Part D is closed. (Or log in and let CC drive it in a follow-up session.)
2. **Demo screenshot spot-check** (optional): the demo renders the same build; the drill set is the visual evidence.
## Observations
- **`.form-input` vs `.form-control`:** two input-class conventions coexist; `.form-input` got styled in 0.126.1 as a visual twin. Consolidation to ONE class is a candidate cleanup (not done — no "while I'm here").
- **style.css orphan check:** `.stack-name`/`.stack-desc`/`.stack-info`/`.stack-card`/`.stack-actions`/`.stack-logo` rules removed with the dashboard conversion; `.storage-paths-list`/`.storage-path-*` remain (still used by the storage page); `.stack-state-label` kept (usage not in this task's surfaces — possible orphan, not touched).
- **CF edge-cache**: any future controller static asset needs the `?v=` discipline (only style.css is versioned now; chart.min.js/fonts are effectively immutable).
- **Emoji-as-entities**: `&#128274;` (lock) in app_import.html and `&#128737;` in backups_shared.html render as emoji but pass emoji_gate (it scans codepoints, entities are ASCII). Pre-existing; flagged for a future copy pass.
- **ref-based clicks** (claude-in-chrome) only FOCUS buttons on this UI; coordinate clicks submit — relevant for future browser validation.