The escrow wizard sends the typed-back words to the box; the new package reaches the hub only when they match (R-1151 controller half, unreleased; works with every agent)
gates / gates (push) Successful in 47s
gates / gates (push) Successful in 47s
This commit is contained in:
@@ -32,7 +32,8 @@ type escrowAgent interface {
|
||||
EscrowPreflight(ctx context.Context) (agentapi.EscrowPreflightResponse, error)
|
||||
EscrowCeremonyStart(ctx context.Context) (agentapi.EscrowCeremonyStartResponse, int, error)
|
||||
EscrowCeremonyStatus(ctx context.Context) (agentapi.EscrowCeremonyStatusResponse, error)
|
||||
EscrowCeremonyClaim(ctx context.Context) (string, int, error)
|
||||
EscrowCeremonyClaim(ctx context.Context) (agentapi.EscrowClaim, int, error)
|
||||
EscrowCeremonyConfirm(ctx context.Context, words []string) (int, error)
|
||||
AgentVersion() string
|
||||
}
|
||||
|
||||
@@ -102,6 +103,8 @@ func (s *Server) ServeEscrowAPI(w http.ResponseWriter, r *http.Request) {
|
||||
s.escrowStatusAPIHandler(w, r)
|
||||
case r.URL.Path == "/api/escrow/claim" && r.Method == http.MethodPost:
|
||||
s.escrowClaimAPIHandler(w, r)
|
||||
case r.URL.Path == "/api/escrow/confirm" && r.Method == http.MethodPost:
|
||||
s.escrowConfirmAPIHandler(w, r)
|
||||
default:
|
||||
escrowJSON(w, http.StatusNotFound, nil, "ismeretlen végpont")
|
||||
}
|
||||
@@ -275,7 +278,8 @@ func (s *Server) escrowClaimAPIHandler(w http.ResponseWriter, r *http.Request) {
|
||||
escrowJSON(w, http.StatusBadGateway, nil, "Az ügynök nem elérhető.")
|
||||
return
|
||||
}
|
||||
code, status, err := agent.EscrowCeremonyClaim(r.Context())
|
||||
claim, status, err := agent.EscrowCeremonyClaim(r.Context())
|
||||
code := claim.Code
|
||||
if err != nil {
|
||||
switch status {
|
||||
case http.StatusGone: // 410 — the code was minted but never shown; permanently void.
|
||||
@@ -293,7 +297,63 @@ func (s *Server) escrowClaimAPIHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
return
|
||||
}
|
||||
if claim.ConfirmRequired {
|
||||
// R-1151: the agent keeps the new package until the household types the two words back (/api/escrow/confirm);
|
||||
// the ceremony is not complete yet, so nothing is stamped and no report is pushed here.
|
||||
s.logger.Printf("[INFO] [web] escrow recovery code claimed (one-shot; not logged) — its package waits for the typed-back words")
|
||||
escrowJSON(w, http.StatusOK, map[string]any{"recovery_code": code, "confirm_required": true, "verify_positions": claim.Positions}, "")
|
||||
code, claim = "", agentapi.EscrowClaim{}
|
||||
_, _ = code, claim
|
||||
return
|
||||
}
|
||||
s.logger.Printf("[INFO] [web] escrow recovery code claimed (one-shot; not logged)")
|
||||
s.escrowCompleted()
|
||||
escrowJSON(w, http.StatusOK, map[string]any{"recovery_code": code}, "")
|
||||
code = "" // drop the reference promptly (GC caveat: best-effort)
|
||||
_ = code
|
||||
}
|
||||
|
||||
// escrowConfirmAPIHandler (R-1151) takes the two words the household typed back and hands them to the agent, which
|
||||
// stores the new package on the hub only when they match. Body: JSON {"words": [w1, w2]}. The words are part of the
|
||||
// code: never logged. On success the ceremony is complete (escrowCompleted).
|
||||
func (s *Server) escrowConfirmAPIHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
var req struct {
|
||||
Words []string `json:"words"`
|
||||
}
|
||||
if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 4096)).Decode(&req); err != nil || len(req.Words) != 2 {
|
||||
escrowJSON(w, http.StatusBadRequest, nil, s.msg(r, "api.escrow.confirm_words_mismatch"))
|
||||
return
|
||||
}
|
||||
agent, err := s.escrowAgentConn()
|
||||
if err != nil {
|
||||
escrowJSON(w, http.StatusBadGateway, nil, "Az ügynök nem elérhető.")
|
||||
return
|
||||
}
|
||||
status, err := agent.EscrowCeremonyConfirm(r.Context(), req.Words)
|
||||
req.Words = nil
|
||||
if err != nil {
|
||||
switch status {
|
||||
case http.StatusUnprocessableEntity, http.StatusBadRequest: // the words do not match — the wait goes on
|
||||
escrowJSON(w, http.StatusUnprocessableEntity, nil, s.msg(r, "api.escrow.confirm_words_mismatch"))
|
||||
case http.StatusGone:
|
||||
escrowJSON(w, http.StatusGone, map[string]any{"restart": true}, s.msg(r, "api.escrow.confirm_expired"))
|
||||
case http.StatusNotFound:
|
||||
escrowJSON(w, http.StatusGone, map[string]any{"restart": true}, s.msg(r, "api.escrow.confirm_lost"))
|
||||
default:
|
||||
s.logger.Printf("[WARN] [web] escrow confirm failed (status %d)", status)
|
||||
escrowJSON(w, http.StatusBadGateway, nil, s.msg(r, "api.escrow.confirm_store_failed"))
|
||||
}
|
||||
return
|
||||
}
|
||||
s.logger.Printf("[INFO] [web] escrow recovery code typed back — the new package is on the hub (R-1151)")
|
||||
s.escrowCompleted()
|
||||
escrowJSON(w, http.StatusOK, map[string]any{"uploaded": true}, "")
|
||||
}
|
||||
|
||||
// escrowCompleted runs when the new package is on the hub: at the claim for an agent older than 0.160.0, at the
|
||||
// typed-back confirm since (R-1151).
|
||||
func (s *Server) escrowCompleted() {
|
||||
// v0.138.0: stamp the ceremony-completed time so /backups/remote shows the "awaiting hub
|
||||
// confirmation" card during the report-cycle gap before the auto-confirmer flips to escrowed
|
||||
// (Phase-0 verdict A: the yellow "szükséges" card during that ~15-min wait was the real gap).
|
||||
@@ -310,9 +370,6 @@ func (s *Server) escrowClaimAPIHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// hub's ACK hash-match flip pending→escrowed in seconds (EscrowAutoConfirmer, unchanged)
|
||||
// instead of after the next ~15-min cycle. Non-blocking; a failed push degrades to the cycle.
|
||||
s.reportTriggerNow()
|
||||
escrowJSON(w, http.StatusOK, map[string]any{"recovery_code": code}, "")
|
||||
code = "" // drop the reference promptly (GC caveat: best-effort)
|
||||
_ = code
|
||||
}
|
||||
|
||||
// clearAuthFailures clears the login counter after a right re-auth password — the counter reserveLoginAttempt counts
|
||||
|
||||
Reference in New Issue
Block a user