hub 0.167.0 source: R-1230 — a box that holds for the household's recovery code is not reported stuck and gets no off-site re-issue (measured on the test night); red-proved

This commit is contained in:
2026-10-22 23:56:20 +02:00
parent 1805f8bbf0
commit 14388e218f
3 changed files with 44 additions and 0 deletions
+6
View File
@@ -12,6 +12,12 @@
`TestD30_TheAlarmClearsWhenTheNewKeyShakesHands` (red-proved). The stale text says an agent with D30 re-keys by itself
15 minutes after it sees the sign, plus its next desired-state fetch; the `wg_tunnel_ok` log line is INFO; the source comment no longer says the ok event is
mailed (it is stored, not mailed — review 2026-10-22 night).
- **R-1230 — a held box is not „stuck".** Measured on the test night (Tester4, reinstalled): a box that consumed its
one-time off-site password and now waits for the household's recovery code reports `offsite.state=
awaiting_recovery_key` and no offbox target; after one hour the delivery checker read it as „burned" — a false
`offsite_delivery_stuck` mail to the operator and a re-issue of the off-site login (a config bump that restarts the
box's controller). The checker now stands down for that declared state (the wait is the household's, it can take
days); an undeclared consumed shape alarms as before. `TestR1230_AHeldBoxIsNotStuck`, red-proved.
- `scripts/wire_contract_gate.py`: `wireguard.last_handshake_age_s` left the unread list (the hub reads it since 0.166.0).
## v0.166.0 (unreleased — built 2026-10-22 night, NOT deployed; the deploy is the operator's yes) — the clock-ahead tunnel alarm (R-1204)
+11
View File
@@ -102,6 +102,12 @@ func (c *OffsiteDeliveryChecker) Check() {
declared := false
if _, _, state, derr := c.store.LatestReportOffsiteDeclaration(cfg.CustomerID); derr != nil {
c.logger.Printf("[WARN] offsite-delivery: %s: read declaration: %v (treating as undeclared)", cfg.CustomerID, derr)
} else if state == declaredAwaitingRecoveryKey {
// R-1230 (test night 2026-10-23, measured): a reinstalled box that HOLDS — its credential consumed, the
// household's recovery code not entered yet — reports no offbox target, so it read as „burned": a false
// operator mail and a needless re-issue of the off-site login (which restarts the box's controller). The
// box says what it waits for; that wait is the household's, and can take days. No event, no heal.
continue
} else {
declared = state == declaredNeedsCredential
}
@@ -137,6 +143,11 @@ const (
// are pinned together by TestDeclaredStateStringMatchesTheReconciler.
const declaredNeedsCredential = "needs_credential"
// declaredAwaitingRecoveryKey mirrors the controller's backup.OffsiteStateAwaitingRecoveryKey (the box holds: the hub
// keeps a sealed package and only the household's recovery code opens it). Pinned by
// TestR1230_AHeldBoxIsNotStuck.
const declaredAwaitingRecoveryKey = "awaiting_recovery_key"
func shapeOf(status offsite.DeliveryStatus, declared bool) deliveryShape {
if declared {
return shapeDeclared
@@ -300,3 +300,30 @@ func TestDeliveryChecker_QuietShapes(t *testing.T) {
t.Fatalf("disabled offsite must be skipped, got events=%v calls=%d", *h.events, h.reissuer.count())
}
}
// R-1230 (test night 2026-10-23, measured live on Tester4): a reinstalled box that waits for the household's recovery
// code reports `offsite.state=awaiting_recovery_key` and no offbox target. It must raise no „stuck" event and no
// re-issue — the wait is the household's. The control: the same consumed shape WITHOUT that declaration still alarms.
// RED-PROOF: with the awaiting_recovery_key skip removed, this fails (a stuck event and a re-issue) — seen failing
// 2026-10-23 night.
func TestR1230_AHeldBoxIsNotStuck(t *testing.T) {
h := newDTHarness(t, true)
h.burnedFixture(t, 5)
if err := h.st.SaveReport("c1", []byte(`{"health":{"status":"ok"},"offsite":{"enabled":false,"state":"awaiting_recovery_key","escrow_state":"pending"}}`)); err != nil {
t.Fatal(err)
}
h.checker.Check()
if n := len(h.savedEvents(t, "offsite_delivery_stuck")); n != 0 {
t.Fatalf("a box waiting for the household's recovery code is not stuck: %d stuck event(s)", n)
}
if h.reissuer.count() != 0 {
t.Fatalf("no re-issue for a held box, got %d", h.reissuer.count())
}
if err := h.st.SaveReport("c1", []byte(dtReportNoOffsite)); err != nil { // control: no declaration → the old verdict
t.Fatal(err)
}
h.checker.Check()
if n := len(h.savedEvents(t, "offsite_delivery_stuck")); n != 1 {
t.Fatalf("control: an undeclared consumed shape must still alarm, got %d", n)
}
}