hub v0.39.0: offsite hardening — F4 credential re-issue, F2 scan retry, F5 save UX

F4: ReissueCredentials — explicit operator recovery for consumed-password
dead-ends; resets the labelled resource's password (exactly-1 guard,
red-proofed), stores a fresh one-time secret, bumps ConfigVersion.
New hetznerapi.ResetBoxPassword for the dedicated path.
F2: host-key scan retry-with-backoff (~60s ladder, red-proofed) — first
save survives fresh-subaccount DNS lag.
F5: config form disables submits + shows an in-flight notice (the re-click
bait that caused live F1).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
2026-07-09 22:39:08 +02:00
parent ecf9185605
commit 17cc67f7cd
9 changed files with 321 additions and 3 deletions
+23
View File
@@ -1,5 +1,28 @@
# Felhom Hub — Changelog
## v0.39.0 — offsite hardening: F4 credential re-issue + F2 scan retry + F5 save UX (2026-07-09)
Part of the offsite-provisioning hardening bundle (pairs with controller v0.107.0 + agent v0.78.0); the
sharp edges from the live e2e (`documentation/audits/VALIDATION-offsite-provisioning-e2e-2026-07-09.md`).
- **F4 (pilot-gating) — "Re-issue offsite credentials":** `Provisioner.ReissueCredentials` — the EXPLICIT
operator recovery for a consumed-password dead-end (fresh-guest DR; consumed-but-failed install). Resets
the customer's sub-account password (`ResetSubaccountPassword`) or dedicated-box password (new
`ResetBoxPassword` in `hetznerapi`, client+interface+fake) → stores a FRESH one-time secret → the handler
re-saves the config unchanged so `ConfigVersion` bumps and the stuck guest's next refresh re-runs the
bridge. **Hard-scoped:** targets ONLY the resource labelled `felhom-customer=<id>`; refuses unless the
label lookup finds exactly 1 (ambiguity = refuse, no reset, no secret) **+ companion red-proof** (dropped
the exactly-1 guard → ambiguous lookup proceeded → test FAILED). NOT implicit rotation — `ProvisionOffsite`
never calls it. UI: a confirm-gated button on the config form (shown only when provisioned), route
`POST /configs/{id}/offsite-reissue` (CSRF rides the parent form). The password value is never logged.
- **F2 — host-key scan retry-with-backoff:** a fresh sub-account's DNS lags creation, so the FIRST save
502'd (`no such host`, live). `scanWithRetry` retries on failure (default ladder 2/4/8/16/30s ≈ 60s total,
inside applyOffsite's 3-min detached ctx; ctx-abortable; fail-closed past the budget) **+ companion
red-proof** (disabled the retry loop → DNS-lag save failed → test FAILED). `Provisioner.ScanBackoff`
injectable for tests.
- **F5 — save UX:** the config form disables its submit buttons and shows an in-flight notice on submit
(the ~2560s spinner-less save was the re-click bait that caused F1 live).
## v0.38.1 — offsite provisioning: detach from the client's request context (live finding F1) (2026-07-09)
Found in the first supervised live run: the offsite save takes ~25s (create + wait + host-key scan) with no