R-195: a customer with no machine ever bound does not alarm (hub v0.92.0) + R-193/R-192 spike
gates / gates (push) Successful in 7s
gates / gates (push) Successful in 7s
Part 4 (ships): `david` — a prospective customer with hosts=0, host_deletions=0, reports=0 — e-mailed an expected_dbdump_missed ERROR at 03:00 UTC three mornings running. The existing down-skip could never cover it: it reads the staleness checker's state, which is seeded from a query over the `reports` table, so a customer that never reported has no state at all and GetState() returns "" rather than "down". store.HasEverBoundHost (hosts row OR host_deletions tombstone) is consulted once per customer at the top of the deadline loop. The discriminator is "was a host EVER bound", never "has a report arrived" — a box installed and never heard from is a real fault and keeps alarming. Fail-OPEN on a read error. Red-proof observed: removing the guard fails with `got [expected_dbdump_missed]`, verbatim the event david sent. Parts 0-3 (spike, NO production code for R-193/R-192): audits/SPIKE-offsite-credential-recovery-2026-08-04.md establishes that the one-shot provider password is the RECOVERABLE secret and the restic repository password is the irreplaceable one — and that a guest rebuild mints a fresh one, orphaning the previous off-site history. Measured without touching a box, by comparing host_escrow.restic_pw_sha256 against host_escrow_superseded: BOTH demo boxes changed (demo-hp 15 snapshots / 40.9 MB, demo-felhom 36 snapshots / 1.14 GB). demo-felhom's "lucky" 76-second recovery restored delivery and not the repository, silently, for 13h. ReissueCredentials does NOT rotate the restic password (R-39's record and two hub comments are wrong -> R-196); candidate (b) is not implementable against a zero-knowledge escrow; candidate (a) already exists as F3 and is wired to the wrong event. Ends in ranked options and an unanswered question for the operator. R-195 SHIPPED; R-196 + R-197 filed; R-192 + R-193 updated, neither closed.
This commit is contained in:
@@ -2262,6 +2262,28 @@ func (s *Store) GetHostByCustomer(customerID string) (*Host, error) {
|
||||
return h, err
|
||||
}
|
||||
|
||||
// HasEverBoundHost reports whether a machine was EVER bound to this customer — a live row in
|
||||
// `hosts` OR a tombstone in `host_deletions`. It answers "was anything ever expected of this
|
||||
// customer", which is the question the deadline verdicts actually need (R-195).
|
||||
//
|
||||
// It is deliberately NOT "has a report arrived", and the distinction is the whole point: a box
|
||||
// that was installed, bound, and then went silent IS bound, and its silence is a real fault that
|
||||
// must keep alarming. Only a customer that never had a machine at all is UNKNOWN.
|
||||
//
|
||||
// `host_deletions` is included because a customer whose host was removed HAD one — the deadline
|
||||
// caller reaches its down-skip for that shape, and this predicate must not quietly take over a
|
||||
// judgement the staleness checker owns.
|
||||
func (s *Store) HasEverBoundHost(customerID string) (bool, error) {
|
||||
var n int
|
||||
if err := s.db.QueryRow(
|
||||
`SELECT EXISTS(SELECT 1 FROM hosts WHERE customer_id = ?)
|
||||
OR EXISTS(SELECT 1 FROM host_deletions WHERE customer_id = ?)`,
|
||||
customerID, customerID).Scan(&n); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return n != 0, nil
|
||||
}
|
||||
|
||||
// ListHostsByCustomer returns the customer's hosts ordered by host_id (v0.47.0 — the
|
||||
// customer page's Host tab is a LIST by design: 1 host today, N for a later HA cluster).
|
||||
// Uses the idx_hosts_customer index.
|
||||
|
||||
Reference in New Issue
Block a user