Closed R-971, R-954, R-1124, R-1012 (174 -> 175 rows); opened R-1139..R-1143; R-895 READY with the design and hub slice 1; R-972 waits on the vouch. [DESIGN] "The catalog allow-list" in 02; the catalog trust line in 01; FELHOM_THINCHECK and unit-forget in 03. Evidence audits/agent-allowlist-rotation-2026-10-17/. Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
5.0 KiB
R-895 — the hub counts a box's off-site snapshots itself (option B, updated with the measurement) — 2026-10-17
Baselines: felhom.eu 8f97b800 (hub v0.157.0 code), design origin audits/night-burndown-2026-10-06/design-R-822.md
option B. Architecture: 07-backup-architecture.md (the off-site tier; decision 68 — the box-trusted count; decision
69 — the hub writes the pinned key line), 06-offsite-connectivity.md.
1. The question
The clean-up window's drop alarm (EventWindowDrop) compares counts the BOX reports before and after a window. A box
that was broken into, or fakes planted with its add-only key, keep the count level while the real history shrinks.
Option B: the hub counts the files in the box's restic snapshots/ directory itself, over a login of its own.
2. The measurement (read-only, demo-hp's repository folder only)
What login would the hub need? None new. The hub already logs in to every provisioned sub-account once a day with
the sub-account PASSWORD (sealed at rest, R-821) for the authorized_keys audit (offsitekeys.Registrar.Audit,
port 23, host key pinned). That login can list snapshots/. It is NOT read-only — the same password can rm,
mv and rewrite authorized_keys (measured 2026-10-03, audits/offsite-append-only-2026-10-03/live/). The listing
itself only reads (ls -l), and adds no capability the hub does not already hold.
Does the provider show upload times? Yes, by prior measurement on this very sub-account: on 2026-09-01 the
password login on demo-hp's sub-account (u629488-sub3) listed entries with their modification time to the minute
over SFTP (ls -la → Sep 1 12:12), and the port-23 shell offers ls|ll, stat
(audits/evidence-drill-r95-recovery-2026-09-01/phase1-find-the-snapshot/01-probe-tree.txt, 03-shell-capabilities.txt).
A restic snapshot file is written once and never changed, so its mtime is its upload time.
Today's re-measurement from DooPlex: NOT done — and why, with what was tried. Since decision 69 no box holds the
sub-account password; the hub alone holds it, sealed. Tried: (a) CC's secret store (sec list) — no Storage Box
credential; (b) the box's own key — pinned to rclone serve restic --append-only, it reaches only restic's REST API
(names and sizes, no times, and it is the box's key, which is exactly what the hub must not rely on); (c) the hub — it
has no command or endpoint that lists. Reading the sealed password out of the hub DB to measure by hand was not done:
it would put a delete-capable credential of a customer sub-account on DooPlex for a measurement the hub can make
itself. So the measurement ships as code: hub v0.157.0's daily audit lists snapshots/ read-only and LOGS the
count, the newest and the oldest upload time (internal/offsitekeys/snapshots.go). The first daily run after the
deploy IS the measurement; compare its count with the box's own (offsite.snapshot_count in the host report) — a
control from a different channel.
3. The design (the alarm slice, after the measurement)
- Before/after a window, the hub's own count (
OpenWindowFor/CloseWindowFortake the listing, the box's numbers logged beside).EventWindowDropfires on the hub's numbers. Red test first (design-R-822 §5): a fake listing 17 → 3 while the box reports 17 → 15 must fire; today it does not. - Files no box run explains: each box run reports its time (manual runs, catch-ups and crash retries included — they are runs). A snapshot file whose upload time falls outside every reported run (± a slack), or more files than runs, is the injection signature → an operator-only event; the next window is refused while such files exist.
- Snapshots that vanished outside a window: today's list minus yesterday's, when no window was open → an
operator-only event. The hub keeps only names and times (custody unchanged,
07§8a: never contents).
4. Costs
| cost | |
|---|---|
| credential | none new; the existing sealed password (write-capable — a fact today, not a new exposure) |
| per box | one more SSH login per day (the listing), and the stored list (≈ 70 names × 100 bytes) |
| build | slice 1 (this release, ~1 h: list + log); slice 2 (§3.1–3.3, ~1 day with the red tests); a live proof on scratch 9202's sub-account |
| what can go wrong | ls -l output differing on the provider (slice 1 logs it before anything alarms); a run that does not report its time (false alarm — every run path must report) |
A read-only login would be better, and costs a per-box sub-account: a second sub-account with readonly=true
on the same directory (Hetzner API) would let the hub list without holding delete rights for this purpose. It adds
one provisioning step and one sealed secret per box. Not built; named as the alternative.
5. State
R-895 stays OPEN (READY) with this design. Slice 1 (the listing, log-only) is in hub v0.157.0 — deployed only on the operator's yes. Slice 2 (the alarms) needs: the first logged listings on real boxes (the measurement), then about a day.