v0.201.0 — a correct recovery code is never called wrong again (CAMPAIGN-11) — MinAgent 0.125.0
gates / gates (push) Successful in 9s
gates / gates (push) Successful in 9s
R-216: the offsite key recovery is a coupled feature and now says so. featureProbes +
featureMinAgent 0.125.0 + a Supports gate at the unlock entry point, FAILING CLOSED — an
agent that cannot answer is named as such instead of the customer's code being blamed.
Measured live: a 404 from agent 0.120.0 came back as "we did not accept your recovery
code, check that all ten words", in 0.134 s, against a perfect code.
R-218: delete the repo-password short-circuit in needsOffsiteCredential. The declaration
stops when the TIER WORKS, not when a key exists — installing a key is the recovery
screen's whole job, so succeeding at recovery was switching off the mechanism that would
have delivered the coordinates to use it.
R-219: the unlock finishes the job — place the key, bring the tier up, then list. Without
it the promised listing could never render on the shape the screen exists for.
R-217: an unreadable store no longer claims to have opened with unattributable content
(the OffsiteInventory{} zero value). Opened / empty / unreadable are three states.
R-222: a code that is right about a RETAINED earlier package is named, not blamed. States
what the hub knows and promises nothing — no read path exists.
R-215: GET /recovery is gated on the same predicate as the interception.
Five red-proofs, each demonstrated failing and restored.
This commit is contained in:
@@ -20,6 +20,11 @@ type EscrowStatus struct {
|
||||
IdentityBlobPresent bool `json:"identity_blob_present"`
|
||||
ResticPwSHA256 string `json:"restic_pw_sha256"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
// SupersededPresent / SupersededAt (v0.201.0, R-222) — the hub is ALSO keeping an earlier sealed
|
||||
// package, and when it was set aside. Absent on a pre-0.97.0 hub, which reads as "no earlier
|
||||
// package" and simply keeps today's message: an older hub cannot make the screen say anything new.
|
||||
SupersededPresent bool `json:"superseded_present"`
|
||||
SupersededAt string `json:"superseded_at"`
|
||||
}
|
||||
|
||||
// EscrowAutoConfirmer runs the auto-confirm check on each report ACK. Long-lived (one per process) so
|
||||
@@ -54,7 +59,13 @@ type EscrowAutoConfirmer struct {
|
||||
// immediately, so the one fact that distinguishes a REBUILT box from a box that never had
|
||||
// off-site backups was thrown away on every cycle. nil → not recorded (older wiring, tests).
|
||||
RecordPresence func(present bool) error
|
||||
Logger *log.Logger
|
||||
// RecordSuperseded persists the ACK's `superseded_present` / `superseded_at` (v0.201.0, R-222) —
|
||||
// whether the hub is ALSO keeping an EARLIER sealed package for this box. Wired here for the same
|
||||
// reason as RecordPresence: this is the one place the ACK's escrow object already arrives, and a
|
||||
// second wiring point in main.go is how this project accumulated six features that were built and
|
||||
// never wired. nil → not recorded (older wiring, tests).
|
||||
RecordSuperseded func(present bool, at string) error
|
||||
Logger *log.Logger
|
||||
|
||||
mu sync.Mutex
|
||||
warnedHash string // last mismatched hub hash we warned about (dedupe; shared by both branches)
|
||||
@@ -111,6 +122,14 @@ func (c *EscrowAutoConfirmer) Reconcile(es *EscrowStatus) {
|
||||
c.logf("[WARN] [escrow-confirm] could not record the hub's identity-blob presence (present=%v): %v", es.IdentityBlobPresent, err)
|
||||
}
|
||||
}
|
||||
// Same discipline, same place, same reason (R-222): recorded unconditionally, because the box that
|
||||
// needs it most is the unconfigured rebuilt one every gate below skips. Failure is logged, never
|
||||
// swallowed, and never blocks the auto-confirm.
|
||||
if c.RecordSuperseded != nil {
|
||||
if err := c.RecordSuperseded(es.SupersededPresent, es.SupersededAt); err != nil {
|
||||
c.logf("[WARN] [escrow-confirm] could not record the hub's superseded-package state (present=%v): %v", es.SupersededPresent, err)
|
||||
}
|
||||
}
|
||||
c.mu.Lock()
|
||||
c.sealedAt = es.CreatedAt // in-memory only; a timestamp, never a secret
|
||||
c.mu.Unlock()
|
||||
|
||||
Reference in New Issue
Block a user