hub: customer-claim password arc parts 1+2 — code engine, emails, ACK, configgen bake, UI (v0.50.0)

Closes DRILL-day0-vm F-4 hub-side: per-customer claim state (customer_claims,
bcrypt-only custody), the claim engine (issue at real config retrieve = Day-0
bake; first-report issue for live boxes; resend rotates generation; reset
rate-limited 3/day), three Hungarian emails via the dispatcher, report-ACK
claim object {code_hash, generation, issued_at} + set-only claimed ingest,
web.claim_code_* baked into generated controller.yaml, Setup-tab status chip
+ resend button, POST /api/v1/claim/reset-request (self-scoped), claim_lockout
event allowlisted. 13 new tests; full repo green.

Claude-Session: https://claude.ai/code/session_01NptTCFtu7dz2Ru89qHRagN
This commit is contained in:
2026-07-12 18:12:48 +02:00
parent b904477ed9
commit 6b40eb8619
14 changed files with 1103 additions and 7 deletions
+9
View File
@@ -15,6 +15,7 @@ import (
"gitea.dooplex.hu/admin/felhom-hub/internal/api"
"gitea.dooplex.hu/admin/felhom-hub/internal/assets"
"gitea.dooplex.hu/admin/felhom-hub/internal/claim"
"gitea.dooplex.hu/admin/felhom-hub/internal/gitea"
"gitea.dooplex.hu/admin/felhom-hub/internal/hetznerapi"
"gitea.dooplex.hu/admin/felhom-hub/internal/mailrelay"
@@ -245,6 +246,13 @@ func main() {
)
apiHandler.SetDispatcher(dispatcher)
// Customer-claim password arc (v0.50.0, F-4): the code engine — the dispatcher delivers the
// Hungarian emails, the store holds bcrypt(code) only. Wired into the API (Day-0 issue at
// config retrieve, live-box issue + claimed ingest + ACK on report, reset endpoint) and the
// web UI (status chip + resend).
claimEngine := &claim.Engine{Store: dataStore, Mailer: dispatcher, Logger: logger}
apiHandler.SetClaimEngine(claimEngine)
// App-email passthrough (POST /api/v1/mail): a customer box's shim forwards a raw message
// here and the hub re-emits it to Resend over SMTP, UNCHANGED (raw passthrough — NOT the
// dispatcher's structured HTTP-API path, which drops inline CID images). The Resend key stays
@@ -260,6 +268,7 @@ func main() {
webServer := web.New(dataStore, cfg.Auth.PasswordHash, cfg.API.ReportAPIKey, Version, staleThreshold, logger)
webServer.SetTemplateFetcher(templateFetcher)
webServer.SetAssetManager(assetsMgr)
webServer.SetClaimEngine(claimEngine) // v0.50.0 — Setup-tab claim chip + resend button
// Day-0 artifact version dropdowns: let the operator pick a version and have the hub derive the
// sha256 from Gitea (no hand-copied checksums). Reuses the registry creds; degrades to manual text
// entry when they're absent.