# REPORT — v0.122.0: customer-claim password gate (closes DRILL-day0-vm F-4/F-5) **Date:** 2026-07-12 · **Version:** controller v0.122.0 (from v0.121.0) · **MinAgent:** 0.81.0 (UNCHANGED) · **Requires:** hub v0.50.0 ## What shipped The dashboard password is now **customer-owned**, set through a one-time claim code the hub emails to the registered address. The old "no password → open dashboard" default is gone: a fresh box (claim-code hash delivered, no password) serves ONLY the claim page — every other route answers the claim page (`302 → /claim`) or `401` (API). This closes the DRILL-day0-vm F-4 exposure (every Day-0 dashboard was open on the internet, with the full deploy API = RCE-equivalent) and F-5 (the unauthenticated geo toggle is now behind the gate too). - **`internal/web/claim.go`** — `claimGateActive()` / `effectiveClaimCode()` (ACK-cached settings beats the config bake by generation), the claim page (`GET /claim`), submit (`POST /claim`: verify code → set own password via `SetPasswordHash` → mark claimed → consume generation → session), and `POST /claim/request-new-code` (forwards to the hub reset-request). Code checks: bcrypt match AND generation-not-consumed (single-use) AND ≤ 72 h. Per-source + global limiter (5 → 15-min lockout) raising the allowlisted `claim_lockout` event. Pre-auth CSRF = HMAC over `web.session_secret` (fixes CTRL-007). Min password 12. - **Gate wiring** (`auth.go`/`csrf.go`/`server.go`/`cmd`): gate atop `RequireAuth`; a SET password disables it (auth wins — claimed boxes never regress). `/claim*` + `/static/*` reachable pre-auth. Legacy-open (no password, no hash) → red transition banner (`layout.html`). Login "Elfelejtett jelszó" link. - **`internal/report/claim_sync.go`** — caches the ACK `claim {hash, generation}` into settings.json idempotently by generation (one-way: newer advances; same/older/nil never rewrites; hub outage never clears). Report carries `claimed` (set-only). Hub bakes `web.claim_code_*` for gate-from- first-boot. - **`--print-reset-code`** root escape hatch (one-time local code above cached/baked/consumed gen). ## Tests + red-proofs - Gate-coverage signature test (`TestClaimGate_EveryRouteGated`): every route → claim page/401, a deploy POST reaches no handler. Plus happy-path / reuse-refused / expired / lockout+window-reopen (fake clock), legacy-open vs password-set, claim-sync idempotence. - **Four §10 red-proofs proven** (mutate → FAIL → revert → green): (1) gate skip-line, (2) single-use generation — hub `RotateClaimCode` bump AND controller consumed-generation check, (3) reset non-DoS (clearing claimed → fail), (4) rate-limiter (both scopes disabled → fail). - Green gate: `go build ./... && go vet ./...` clean; all touched packages pass. Pre-existing env-sensitive `internal/backup` volume-dump failures are unrelated (fail identically on the clean baseline — no tar/zstd toolchain on the dev box). ## Live validation (drill guest 9201 on qm 300, controller 0.122.0, pre-floor) Deployed 0.122.0 → the ACK delivered the claim hash → gate flipped ON within a report cycle. Proven via the REAL Cloudflare edge (`curl --resolve`): `/` → 302 → "A szerver beállítása" claim page; `/claim` → 200; `/api/host-metrics` → 401. Controller log: `claim-sync: hub claim code cached (generation 1)`. Hub log: claim code emailed to the registered address of demo-vm-felhom. The customer-side claim + reset + the floor raise are the operator's supervised steps.