R-196 / R-204 item 2: a re-issue no longer marks a healthy escrow stale (hub v0.95.0)
ReissueCredentials marked the escrow stale on every re-issue, on precautionary grounds — the box's re-apply MIGHT mint a fresh repository password. It usually does not. A stale flag withholds restic_pw_sha256 from the ACK, which stops the controller's auto-confirm, which leaves EscrowState pending, which makes OffboxRunnable false: every off-site backup refused on a box whose key was never in doubt — and the customer told to re-run the one ceremony that would have superseded the key just recovered. The case it guessed at is measured elsewhere: the controller's Scenario-F re-check compares the sealed hash against the live repo password on every ACK (and the mark was BLINDING it by emptying that hash), and R-197's offsite_repo_key_changed fires on a proven difference across a supersession. offsite_reissued is unchanged. MarkEscrowStale is kept without a caller so a future EVIDENTIAL writer has the mechanism, with a test pinning it live. TestReissue_InvalidatesEscrow is replaced by its exact inverse.
This commit is contained in:
+32
-18
@@ -445,14 +445,14 @@ func (s *Store) migrate() error {
|
||||
// instead of trusting blob-presence. NULL/'' = a legacy or password-less blob (never auto-confirms).
|
||||
s.db.Exec(`ALTER TABLE host_escrow ADD COLUMN restic_pw_sha256 TEXT`)
|
||||
|
||||
// v0.57.0 (2.3, escrow honesty on offsite re-issue) — stale_at is set when the offsite PROVIDER
|
||||
// credentials are re-issued. ⚠ CORRECTED 2026-08-04 (R-196): it used to say "when the offsite repo
|
||||
// password is re-issued", which nothing in the hub does — the repository password is generated on
|
||||
// the box and never leaves it except sealed under R. The flag is PRECAUTIONARY (the box's re-apply
|
||||
// MAY mint a fresh repository password), not evidence that it changed; the evidential signal is
|
||||
// R-197's offsite_repo_key_changed. While set, the hub stops advertising "ceremony done" and
|
||||
// withholds the possibly-non-matching restic_pw_sha256 from the auto-confirm ACK. NULL = current;
|
||||
// a fresh ceremony (SaveHostEscrow) clears it.
|
||||
// v0.57.0 (2.3, escrow honesty on offsite re-issue) — stale_at marks an escrow blob that may no
|
||||
// longer cover the box's live repository password. While set, the hub stops advertising "ceremony
|
||||
// done" and withholds restic_pw_sha256 from the auto-confirm ACK. NULL = current; a fresh ceremony
|
||||
// (SaveHostEscrow) clears it.
|
||||
// ⚠ NOTHING SETS IT as of v0.95.0 (R-196 / R-204 item 2). The only writer was the PRECAUTIONARY
|
||||
// mark on offsite re-issue, which guessed rather than measured and so blocked off-site backups on
|
||||
// boxes whose key had not changed. The column and its readers stay; see MarkEscrowStale for what a
|
||||
// legitimate future writer would have to prove first.
|
||||
s.db.Exec(`ALTER TABLE host_escrow ADD COLUMN stale_at DATETIME`)
|
||||
|
||||
// dr_recipe (SPIKE-dr-recipe-2026-06-16): the secret-free DR reconstruction recipe, stored
|
||||
@@ -2699,12 +2699,24 @@ func (s *Store) CountCurrentEscrowWithIdentity() (total, withIdentity int, err e
|
||||
return total, withIdentity, err
|
||||
}
|
||||
|
||||
// MarkEscrowStale flags a host's escrow blob as stale (v0.57.0, 2.3) — called when the offsite
|
||||
// PROVIDER credentials are re-issued. ⚠ CORRECTED 2026-08-04 (R-196): it used to say "when the offsite
|
||||
// repo password is re-issued", which no hub path does; see the reasoning at offsite.ReissueCredentials.
|
||||
// The flag is precautionary — the box's re-apply may mint a fresh repository password — not a
|
||||
// measurement that one did. No-op when no escrow row exists; idempotent (only stamps the first
|
||||
// re-issue since the last ceremony; a fresh ceremony clears stale_at via SaveHostEscrow's ON CONFLICT).
|
||||
// MarkEscrowStale flags a host's escrow blob as stale (v0.57.0, 2.3).
|
||||
//
|
||||
// ⚠ IT HAS NO CALLER as of hub v0.95.0 (R-196 / R-204 item 2), and that is deliberate, not an
|
||||
// oversight. Its ONE caller was `offsite.ReissueCredentials`, which called it on every re-issue on
|
||||
// the PRECAUTIONARY grounds that the box's re-apply might mint a fresh repository password. It
|
||||
// usually does not, so the call marked healthy escrows stale — and because a stale flag WITHHOLDS
|
||||
// restic_pw_sha256 from the ACK (GetEscrowStatusForCustomer, below), it blocked every off-site backup
|
||||
// on those boxes and asked the customer for a ceremony that would supersede a perfectly good key. The
|
||||
// full reasoning, and the two measured signals that cover the real case, are at
|
||||
// offsite.ReissueCredentials.
|
||||
//
|
||||
// KEPT, not deleted, because the FLAG is still live and correct — `stale_at` is read by the ACK, the
|
||||
// operator card and the PBS-DR view, and a future EVIDENTIAL caller (one that has measured a key
|
||||
// change rather than guessed at one) is the right way to set it. Pinned by
|
||||
// TestReissue_DoesNotMarkAHealthyEscrowStale: if a caller reappears without that evidence, it fails.
|
||||
//
|
||||
// No-op when no escrow row exists; idempotent (only stamps the first mark since the last ceremony; a
|
||||
// fresh ceremony clears stale_at via SaveHostEscrow's ON CONFLICT).
|
||||
func (s *Store) MarkEscrowStale(hostID string) error {
|
||||
_, err := s.db.Exec(`UPDATE host_escrow SET stale_at = datetime('now') WHERE host_id = ? AND stale_at IS NULL`, hostID)
|
||||
return err
|
||||
@@ -2733,11 +2745,13 @@ type EscrowStatus struct {
|
||||
IdentityBlobPresent bool `json:"identity_blob_present"`
|
||||
ResticPwSHA256 string `json:"restic_pw_sha256,omitempty"`
|
||||
CreatedAt string `json:"created_at,omitempty"`
|
||||
// Stale (v0.57.0, 2.3) — true when the offsite PROVIDER credentials were re-issued after the blob
|
||||
// was sealed. ⚠ CORRECTED 2026-08-04 (R-196): it used to say "the offsite password was re-issued",
|
||||
// which reads as the repository password and is not what happens; see MarkEscrowStale.
|
||||
// Stale (v0.57.0, 2.3) — true when `stale_at` is stamped on the host's escrow row. ⚠ SINCE hub
|
||||
// v0.95.0 (R-196 / R-204 item 2) NOTHING STAMPS IT: the one caller was the precautionary
|
||||
// re-issue mark, and it was removed for marking healthy escrows stale. See MarkEscrowStale.
|
||||
// When stale the ResticPwSHA256 is WITHHELD (emptied) so the controller cannot auto-confirm against
|
||||
// a hash that may no longer match the live repo password — the ceremony must run again.
|
||||
// a hash that may no longer match the live repo password — the ceremony must run again. That
|
||||
// withholding is exactly why the precautionary caller had to go: it BLINDED the controller's own
|
||||
// hash comparison, which is the measurement that actually detects a changed repository password.
|
||||
Stale bool `json:"escrow_stale,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user