11 KiB
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)
07e3c37— gate housekeeping:backups_split_move_check.pyRETIRED (one-shot v0.124.0 migration gate, served its purpose; this task legitimately rewrites the moved blocks). Doc refs updated.3eacb6c— Part A:templates/app_row.htmlapp_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). funcmapdict+appHref;OffboxAppRow/AppBackupRowgainSlug; old.stack-card/.storage-path-itemrow CSS+markup retired. NEWscripts/app_row_dedup_gate.py.4f06149— Part B:inframeta.godisplay-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. Megnyitás on the dashboard row; the card link comes from Subdomains as before).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.458cf4a— Part D:scripts/mojibake_gate.py(templates + Go sources, strict UTF-8 + zero Ã/Â/Ă/ă/˘/ˇ signature chars, allowlist ZERO, Python per the multibyte rule).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.912e6a6— docs (CHANGELOG A–E, REUSE canonical-row entry + gate list, README, CONTEXT).41c8fc2— v0.126.1 (live-QA finding 1):.form-input/.form-rowhad NO backing CSS rule at all (only the.form-controltwin was styled) — the TRUE root cause of the operator's "unstyled clipped placeholder" screenshot. Rules added (same visual spec as.form-control).21d0f66— v0.126.2 (live-QA finding 2): Cloudflare edge caches/static/style.cssfor 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-itemrow block into backups_remote.html →app_row_dedup_gate: FAIL … forbidden old structure 'storage-path-item' survivesexit 1; reverted → OK. - B (infra link guard):
Linked: trueon cloudflared →TestInfraMetaFor_RegularAppIsNil: cloudflared must exist and must NOT be Linked (no customer UI)FAIL; reverted → PASS. - D (mojibake gate): reintroduced
Tárhelyinto 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)
- 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.) - Demo screenshot spot-check (optional): the demo renders the same build; the drill set is the visual evidence.
Observations
.form-inputvs.form-control: two input-class conventions coexist;.form-inputgot 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-logorules removed with the dashboard conversion;.storage-paths-list/.storage-path-*remain (still used by the storage page);.stack-state-labelkept (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:
🔒(lock) in app_import.html and🛡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.