R-204 item 4 (hub half): the hub answers a rebuilt box's request (hub v0.96.0)
New internal/offsiteheal, the sibling of pbsdrheal: it acts ONLY on the state the box declares, sustained across two distinct reports, re-staging the stored credential before ever minting a new one. A healthy box is a pure no-op; it never blind-timer-reissues and never re-runs a provisioning step. RESTAGE IS POSSIBLE because the stored value survives a consume — established from the schema and ConsumeOneTimeSecret (which stamps consumed_at and nothing else), not inherited from the PBS analogy, and pinned by a test that asserts the SAME value comes back. reportHasOffsite is TIGHTENED to require enabled:true. Its comment asserted that presence == applied-on-the-box, and the declaration deliberately breaks that premise; left alone it would have read a request for help as proof the tier was applied. Provably a no-op for every report shape that existed before, because an attached object has always carried enabled:true. R-192's guard half is CLOSED BY REPLACEMENT: the delivery checker's counting inference read the OLDEST 500 reports after a consume — all predating a rebuild, which is why demo-hp sat stranded for 108 reports under a confident regressed-shape verdict. A declaration outranks both inferred shapes, and the checker stands down with a record so the two mechanisms cannot double-issue. No escrow ceremony is ever run or requested: credential automatic, key customer-present.
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func newRestageStore(t *testing.T) *Store {
|
||||
t.Helper()
|
||||
st, err := New(filepath.Join(t.TempDir(), "test.db"), log.New(io.Discard, "", 0))
|
||||
if err != nil {
|
||||
t.Fatalf("store.New: %v", err)
|
||||
}
|
||||
t.Cleanup(func() { st.Close() })
|
||||
return st
|
||||
}
|
||||
|
||||
// R-204 item 4 / R-193 — THE FACT THE WHOLE DESIGN RESTS ON: the one-time offsite secret's VALUE
|
||||
// survives consumption, so it can be re-armed without any storage-provider call.
|
||||
//
|
||||
// This is asserted rather than inherited from the PBS analogy on purpose. The two secrets are
|
||||
// different objects with different lifecycles, and assuming a shared shape is exactly how two earlier
|
||||
// sessions confused the two credentials. If a future change ever clears `value` on consume — a
|
||||
// perfectly reasonable-looking hardening — restage-before-mint becomes silently impossible and every
|
||||
// rebuild turns into an external mint. This test fails the moment that happens.
|
||||
func TestRestageOneTimeSecret_ReArmsTheSameValue(t *testing.T) {
|
||||
st := newRestageStore(t)
|
||||
const cust, pw = "c1", "the-one-time-password"
|
||||
|
||||
if err := st.SaveOneTimeSecret(cust, pw); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err := st.ConsumeOneTimeSecret(cust)
|
||||
if err != nil || got != pw {
|
||||
t.Fatalf("first consume: got %q err=%v", got, err)
|
||||
}
|
||||
// Single-use holds before the restage.
|
||||
if _, err := st.ConsumeOneTimeSecret(cust); !errors.Is(err, sql.ErrNoRows) {
|
||||
t.Fatalf("a consumed secret was served twice: err=%v", err)
|
||||
}
|
||||
|
||||
restaged, err := st.RestageOneTimeSecret(cust)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !restaged {
|
||||
t.Fatal("an existing secret row reported nothing to re-stage")
|
||||
}
|
||||
|
||||
// THE ASSERTION: the SAME value comes back. Not "a value" — the same one, which is what makes
|
||||
// this a re-arm rather than a silent mint.
|
||||
again, err := st.ConsumeOneTimeSecret(cust)
|
||||
if err != nil {
|
||||
t.Fatalf("consume after restage: %v", err)
|
||||
}
|
||||
if again != pw {
|
||||
t.Fatalf("the re-armed secret changed value: got %q want %q — the value did NOT survive the consume, so restage-before-mint is impossible and the reconciler must mint instead", again, pw)
|
||||
}
|
||||
// And it is single-use again.
|
||||
if _, err := st.ConsumeOneTimeSecret(cust); !errors.Is(err, sql.ErrNoRows) {
|
||||
t.Fatalf("the re-armed secret was not single-use: err=%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// No row → restaged=false, so the caller escalates to a fresh mint instead of silently doing nothing.
|
||||
func TestRestageOneTimeSecret_NoRowReportsFalse(t *testing.T) {
|
||||
st := newRestageStore(t)
|
||||
restaged, err := st.RestageOneTimeSecret("nobody")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if restaged {
|
||||
t.Fatal("re-staging a customer with no stored secret claimed success — the caller would never escalate")
|
||||
}
|
||||
}
|
||||
|
||||
// A restage must not touch the timestamps' meaning beyond the consumed flag: created_at stays put, so
|
||||
// the delivery-state age math is unchanged.
|
||||
func TestRestageOneTimeSecret_ClearsOnlyTheConsumedFlag(t *testing.T) {
|
||||
st := newRestageStore(t)
|
||||
const cust = "c1"
|
||||
if err := st.SaveOneTimeSecret(cust, "pw"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
before, err := st.GetOneTimeSecretInfo(cust)
|
||||
if err != nil || before == nil {
|
||||
t.Fatalf("info before: %v", err)
|
||||
}
|
||||
if _, err := st.ConsumeOneTimeSecret(cust); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := st.RestageOneTimeSecret(cust); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
after, err := st.GetOneTimeSecretInfo(cust)
|
||||
if err != nil || after == nil {
|
||||
t.Fatalf("info after: %v", err)
|
||||
}
|
||||
if !after.ConsumedAt.IsZero() {
|
||||
t.Errorf("consumed_at was not cleared: %v", after.ConsumedAt)
|
||||
}
|
||||
if !after.CreatedAt.Equal(before.CreatedAt) {
|
||||
t.Errorf("created_at moved on a restage: %v → %v (the delivery-state age math would jump)", before.CreatedAt, after.CreatedAt)
|
||||
}
|
||||
}
|
||||
|
||||
// reportHasOffsite is TIGHTENED in v0.96.0 to require enabled:true, because controller v0.199.0 sends
|
||||
// an offsite object with enabled:false to ASK for a credential. This test pins BOTH halves: the
|
||||
// tightening works, AND it is a no-op for every report shape that existed before — the equivalence is
|
||||
// measured here rather than argued in a comment.
|
||||
func TestReportHasOffsite_EnabledOnly(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
report string
|
||||
want bool
|
||||
}{
|
||||
// The PRE-v0.199.0 shapes. These are the equivalence claim: every object the old controller
|
||||
// could ever attach carried enabled:true, so the tightening changes none of them.
|
||||
{"configured box (pre-0.199 shape)", `{"offsite":{"enabled":true,"escrow_state":"escrowed","last_status":"ok"}}`, true},
|
||||
{"configured box, minimal", `{"offsite":{"enabled":true}}`, true},
|
||||
{"no offsite object", `{"controller_version":"0.198.0"}`, false},
|
||||
{"explicit null", `{"offsite":null}`, false},
|
||||
{"unparseable", `{not json`, false},
|
||||
|
||||
// The v0.199.0 DECLARATION — a request for help, not evidence of an applied tier.
|
||||
{"stranded rebuild declaring", `{"offsite":{"enabled":false,"state":"needs_credential"}}`, false},
|
||||
{"disabled object without a state", `{"offsite":{"enabled":false}}`, false},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
if got := reportHasOffsite(tc.report); got != tc.want {
|
||||
t.Errorf("%s: reportHasOffsite = %v, want %v — a declaration read as an applied tier makes DeliveryStateFor say the stranded box is fine", tc.name, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The declaration is readable as such, and an older controller's report yields "" rather than an error.
|
||||
func TestLatestReportOffsiteDeclaration(t *testing.T) {
|
||||
st := newRestageStore(t)
|
||||
const cust = "c1"
|
||||
|
||||
found, _, state, err := st.LatestReportOffsiteDeclaration(cust)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if found {
|
||||
t.Fatal("a customer with no reports was reported as found")
|
||||
}
|
||||
|
||||
if err := st.SaveReport(cust, []byte(`{"controller_version":"0.198.0"}`)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
found, id1, state, err := st.LatestReportOffsiteDeclaration(cust)
|
||||
if err != nil || !found {
|
||||
t.Fatalf("found=%v err=%v", found, err)
|
||||
}
|
||||
if state != "" {
|
||||
t.Errorf("an old controller's report declared %q", state)
|
||||
}
|
||||
|
||||
if err := st.SaveReport(cust, []byte(`{"offsite":{"enabled":false,"state":"needs_credential"}}`)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
found, id2, state, err := st.LatestReportOffsiteDeclaration(cust)
|
||||
if err != nil || !found {
|
||||
t.Fatalf("found=%v err=%v", found, err)
|
||||
}
|
||||
if state != "needs_credential" {
|
||||
t.Errorf("declared state = %q, want needs_credential", state)
|
||||
}
|
||||
if id2 <= id1 {
|
||||
t.Errorf("the report id did not advance (%d → %d) — the debounce counts distinct reports and would never confirm", id1, id2)
|
||||
}
|
||||
}
|
||||
@@ -978,11 +978,27 @@ func (s *Store) SaveReport(customerID string, reportJSON []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// reportOffsitePresence is the minimal parse for "does this controller report carry an offsite
|
||||
// status object" — the R-70 delivery-state signal. The report builder attaches `offsite` only when
|
||||
// the box actually has an offbox target configured, so presence == applied-on-the-box.
|
||||
// reportOffsitePresence is the minimal parse for "does this controller report show an offsite tier
|
||||
// that is actually CONFIGURED on the box" — the R-70 delivery-state signal, i.e. applied-on-the-box.
|
||||
//
|
||||
// ⚠ TIGHTENED 2026-08-05 (hub v0.96.0, R-204 item 4), and the reason is exactly the trap this
|
||||
// project keeps recording. The predicate used to be bare PRESENCE, under a comment asserting *"the
|
||||
// report builder attaches `offsite` only when the box actually has an offbox target configured, so
|
||||
// presence == applied-on-the-box"*. Controller v0.199.0 breaks that premise deliberately: a REBUILT
|
||||
// box now attaches an offsite object carrying `enabled:false` and a declared `state` in order to ASK
|
||||
// for a credential. Left as presence-only, this function would have read that request for help as
|
||||
// proof the tier was applied — turning `DeliveryStateFor` into DeliveryApplied for the exact boxes
|
||||
// that are stranded, and suppressing the stuck event for them.
|
||||
//
|
||||
// The tightening is `enabled == true`, and it is PROVABLY A NO-OP for every report shape that exists
|
||||
// today: `backup.OffboxReportStatus` returned nil unless `t != nil && t.Enabled`, so an attached
|
||||
// object has ALWAYS carried `enabled:true`. Pinned by
|
||||
// TestReportHasOffsite_EnabledOnly — including a case asserting the pre-v0.199.0 shape still reads
|
||||
// true, so the equivalence is measured rather than argued.
|
||||
type reportOffsitePresence struct {
|
||||
Offsite json.RawMessage `json:"offsite"`
|
||||
Offsite *struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
} `json:"offsite"`
|
||||
}
|
||||
|
||||
func reportHasOffsite(reportJSON string) bool {
|
||||
@@ -990,7 +1006,7 @@ func reportHasOffsite(reportJSON string) bool {
|
||||
if err := json.Unmarshal([]byte(reportJSON), &p); err != nil {
|
||||
return false // unparseable report → no offsite evidence
|
||||
}
|
||||
return len(p.Offsite) > 0 && string(p.Offsite) != "null"
|
||||
return p.Offsite != nil && p.Offsite.Enabled
|
||||
}
|
||||
|
||||
// LatestReportOffsitePresence reports whether the customer's most recent controller report exists
|
||||
@@ -1415,6 +1431,67 @@ func (s *Store) ConsumeOneTimeSecret(customerID string) (string, error) {
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// RestageOneTimeSecret re-arms an ALREADY-STORED one-time offsite secret for re-consumption by
|
||||
// clearing its consumed flag — WITHOUT changing the secret value, WITHOUT inserting a row, and
|
||||
// WITHOUT any storage-provider call. It is the off-site sibling of RestageHostPBSSecret (pbsdr.go),
|
||||
// and it exists because a REBUILT box has no credential of its own: its predecessor spent the
|
||||
// one-time password (R-193 / R-204 item 4).
|
||||
//
|
||||
// IT IS POSSIBLE AT ALL ONLY BECAUSE THE VALUE SURVIVES A CONSUME, and that was established from
|
||||
// this file rather than assumed from the PBS analogy (the two secrets are different objects with
|
||||
// different lifecycles, and assuming a shared shape is how two sessions confused the credentials):
|
||||
// ConsumeOneTimeSecret sets `consumed_at` and NOTHING ELSE — the `value` column is never cleared or
|
||||
// overwritten, and the schema declares it `TEXT NOT NULL`. So the row still holds a serviceable
|
||||
// password after consumption, and re-arming it costs no external call and converges in one report
|
||||
// tick. Pinned by TestRestageOneTimeSecret_ReArmsTheSameValue.
|
||||
//
|
||||
// Returns restaged=true when a row existed (its consumed flag is now cleared, so
|
||||
// ConsumeOneTimeSecret will serve it once more); restaged=false when NO secret is stored for the
|
||||
// customer — the caller must then escalate to a fresh mint (ReissueCredentials). The value is never
|
||||
// read or logged here.
|
||||
func (s *Store) RestageOneTimeSecret(customerID string) (bool, error) {
|
||||
res, err := s.db.Exec(`UPDATE one_time_secrets SET consumed_at = NULL WHERE customer_id = ?`, customerID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
n, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return n > 0, nil
|
||||
}
|
||||
|
||||
// LatestReportOffsiteDeclaration returns the id of the customer's newest controller report and the
|
||||
// state DECLARED on its `offsite` object (controller >= v0.199.0; "" on anything older, on a report
|
||||
// with no offsite object, and on an unparseable one).
|
||||
//
|
||||
// The report id is the debounce currency: the reconciler counts DISTINCT reports, not its own ticks,
|
||||
// so a stuck box is confirmed by fresh evidence rather than by the passage of time.
|
||||
//
|
||||
// found=false when the customer has no reports at all — distinguished from "a report that declares
|
||||
// nothing" so the caller never treats an absent report as a healthy one.
|
||||
func (s *Store) LatestReportOffsiteDeclaration(customerID string) (found bool, reportID int64, state string, err error) {
|
||||
var id int64
|
||||
var reportJSON string
|
||||
err = s.db.QueryRow(`SELECT id, report_json FROM reports WHERE customer_id = ? ORDER BY id DESC LIMIT 1`,
|
||||
customerID).Scan(&id, &reportJSON)
|
||||
if err == sql.ErrNoRows {
|
||||
return false, 0, "", nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, 0, "", err
|
||||
}
|
||||
var p struct {
|
||||
Offsite *struct {
|
||||
State string `json:"state"`
|
||||
} `json:"offsite"`
|
||||
}
|
||||
if json.Unmarshal([]byte(reportJSON), &p) == nil && p.Offsite != nil {
|
||||
state = p.Offsite.State
|
||||
}
|
||||
return true, id, state, nil
|
||||
}
|
||||
|
||||
// OneTimeSecretInfo is the delivery-state metadata of a customer's one-time offsite secret —
|
||||
// timestamps ONLY, the value column is deliberately never selected (R-70 detector input; the
|
||||
// customer-scoped sibling of PBSDRHealStates' SecretUnconsumedFor).
|
||||
|
||||
Reference in New Issue
Block a user