hub v0.163.0: a rebuilt box gets the household's sealed package back (R-1176); one run per daily job on clock-change days (R-1183); a scoped abandonment purge (R-1182); the new-link page says the old box is still linked (R-1203)
gates / gates (push) Successful in 13m23s

This commit is contained in:
2026-10-21 13:36:39 +02:00
parent e07b077ac2
commit 9688e76efd
16 changed files with 1018 additions and 29 deletions
+18 -1
View File
@@ -622,9 +622,12 @@ func (h *Handler) handleReport(w http.ResponseWriter, r *http.Request) {
AbandonPurgeRequested bool `json:"abandon_purge_requested"`
} `json:"offsite"`
}
abandonDeclared := false
if err := json.Unmarshal(body, &abandonPayload); err == nil &&
abandonPayload.Offsite != nil && abandonPayload.Offsite.AbandonPurgeRequested {
n, perr := h.store.PurgeSupersededEscrowForCustomer(payload.CustomerID)
abandonDeclared = true
// R-1182: only the packages that open a set-aside copy the hub DELETED — never every retained package.
n, perr := h.store.PurgeAbandonedEscrowForCustomer(payload.CustomerID)
switch {
case perr != nil:
// Never swallowed: the box keeps declaring, so this retries next cycle — but an operator
@@ -649,7 +652,16 @@ func (h *Handler) handleReport(w http.ResponseWriter, r *http.Request) {
// EscrowState pending→escrowed ONLY when sha256(its local repo password) matches restic_pw_sha256
// (blob-presence alone must never confirm — a stale blob may not cover the current key). The hash is
// non-reversible (256-bit random secret) — safe to serve; omitted entirely when no escrow row exists.
// R-1176: a rebuilt box's FIRST ACK must already carry the household's package, or the box mints a fresh key.
h.carryEscrow(payload.CustomerID)
if es, err := h.store.GetEscrowStatusForCustomer(payload.CustomerID); err == nil && es != nil {
// R-1182: other retained packages are now rightly KEPT after an abandonment, so `superseded_present` can stay
// true for good; this field tells the box its own declaration is finished. Only on a declaring report.
if abandonDeclared {
if left, lerr := h.store.AbandonedEscrowLeft(payload.CustomerID); lerr == nil && left == 0 {
es.AbandonPurgeDone = true
}
}
resp["escrow"] = es
}
@@ -969,6 +981,11 @@ func (h *Handler) handleHostReport(w http.ResponseWriter, r *http.Request) {
return
}
// R-1176: the agent's report is the first a new box sends — carry the household's package before its
// controller exists, so the controller's first ACK finds it.
if custID != "" {
h.carryEscrow(custID)
}
h.checkTunnel(hostID, custID, rep.Cloudflared.Status, rep.Cloudflared.Detail)
h.checkCrash(hostID, custID, body)
h.checkDeliberateOff(hostID, custID, &rep)