feat(escrow): v0.138.0 — "awaiting hub confirmation" waiting state

After a completed escrow ceremony the Távoli mentés page showed the yellow
"Helyreállítási kód szükséges" card for ~15 min until the next hub-report ACK
flipped pending→escrowed. Phase-0 diagnosis (read-only) = verdict A (report-cycle
lag), already resolved on the demo box (escrow_state:"escrowed"); hub Hypothesis B
verified false (SaveHostEscrow ON CONFLICT already clears stale_at on upload) → no
hub change.

- settings.OffboxTarget.CeremonyCompletedAt: stamped on the recovery-code claim,
  zeroed on the auto-confirmer Flip + the deprecated manual confirm; persisted.
- web/handlers.go: offboxCeremonyWaitState + escrowCeremonyGraceWindow (35m).
- backups_remote.html: info "megerősítésre vár, legfeljebb 15 perc" card → warn
  "a megerősítés nem érkezett meg" past the window. Existing branches untouched.
- backups_escrow.html: "Mi történik ezután?" note on the wizard's final step.
- Test web/escrow_wait_state_test.go (truth table + red-proof recorded in REPORT).

No scheduler/agent/hub/endpoint changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qDiBqKKQ5vPB5fXBqu7Kp
This commit is contained in:
2026-07-16 18:44:13 +02:00
parent e99c675fe4
commit 120332103a
13 changed files with 283 additions and 61 deletions
+30
View File
@@ -713,6 +713,31 @@ func (s *Server) backupsHandler(w http.ResponseWriter, r *http.Request) {
s.executeTemplate(w, r, "backups", data)
}
// escrowCeremonyGraceWindow (v0.138.0) bounds how long the "awaiting hub confirmation" card is
// shown after a completed escrow ceremony before it degrades to the "confirmation did not arrive"
// warning. Two report cycles (2×15m) + slack — long enough for the normal report-ACK confirm
// (Phase-0 verdict A: the demo confirmed on the next ACK ~14m out), short enough that a genuinely
// stuck ceremony never renders as an indefinite wait.
const escrowCeremonyGraceWindow = 35 * time.Minute
// offboxCeremonyWaitState classifies the post-ceremony wait for the remote page's escrow card:
// awaiting (stamped, within the grace window, still pending) vs timedOut (stamped, past the window,
// still pending). Both false when escrowed, unstamped, or the timestamp is unparseable — fail to the
// plain pending CTA rather than render a phantom wait.
func offboxCeremonyWaitState(t *settings.OffboxTarget) (awaiting, timedOut bool) {
if t == nil || t.EscrowState == "escrowed" || t.CeremonyCompletedAt == "" {
return false, false
}
ts, err := time.Parse(time.RFC3339, t.CeremonyCompletedAt)
if err != nil {
return false, false
}
if time.Since(ts) >= escrowCeremonyGraceWindow {
return false, true
}
return true, false
}
// backupsRemoteHandler renders the Távoli mentés page: the Felhom-offsite status card, the
// participation toggles and the manual-target form.
func (s *Server) backupsRemoteHandler(w http.ResponseWriter, r *http.Request) {
@@ -725,6 +750,11 @@ func (s *Server) backupsRemoteHandler(w http.ResponseWriter, r *http.Request) {
agentVer = agent.AgentVersion()
}
data["EscrowAgentOK"] = escrowAgentSupported(agentVer)
// v0.138.0: the post-ceremony "awaiting hub confirmation" card (and its timeout degrade) —
// bridges the report-cycle gap between the ceremony and the auto-confirmer's pending→escrowed flip.
awaiting, timedOut := offboxCeremonyWaitState(s.settings.GetOffboxTarget())
data["OffboxCeremonyAwaiting"] = awaiting
data["OffboxCeremonyTimedOut"] = timedOut
s.executeTemplate(w, r, "backups_remote", data)
}