hub v0.104.0: the guest network gets a reader (R-319), and the hub half of the naming (R-295)
gates / gates (push) Successful in 14s
gates / gates (push) Successful in 14s
Four paper debts and one fact given a reader. Hub-only — nothing to bake. A4 — the entry about "the tester's machine" named a risk correctly and labelled it in a way that invited deleting it. Established from the hub's own store: `peti-felhom` is a REAL machine (482 reports, 2026-02-27 → 2026-07-15, a named person's own box) and the 3.6 GB with no key and no backup is real. `david` → `tester-1` is a DIFFERENT record with no host, no escrow and no report, ever — deleted 07:55:49 and re-created 07:56:47 this morning. The prompt's premise conflated the two; the register now says which is which. A1 — R-312/R-313/R-303 recorded as DECIDED with their re-open triggers, and moved out of STATUS's "Waiting on you", which is now empty. A3 — day0-install §C.1 said pushing the installer publishes it. It has not since R-110. Corrected, with the two manifest pins named and an outside-verification command; the one copy that repeated it (a dated audit, true when written) carries a superseded note. A5 — standing rule 5: evidence comes off the machine at the end of the phase that produced it, before any revert. Earned twice in three days on the same box at the same point (R-320). Four homes, plus what to do when it is already gone. R-295 hub half — „Beállító kód" everywhere; „Visszaállító kód" retired. New `reenroll` mail kind so the mail names the page a REBUILT box actually shows („A szerver beállítása"), not the „Elfelejtett jelszó" page it has no login screen to reach. Naming only; the acceptance pin proves the secret is untouched. R-319 — the hub models `guest_net` after 23 days of receiving and discarding it. The signal is `heals_last_hour`, not `state`: a guest the watchdog keeps repairing reads healthy between repairs. `heal_succeeded` decoded too (R-260's lesson). Unknown is never drawn as healthy — three absences, three sentences. No alarm, deliberately. Three red-proofs, mutations asserted applied. Wire-gate checked tags 182 → 190. B1 — the operator's 2026-08-12 dispositions were NOT in the register; they are now. Third allowlist kind for the five ruled "no reader wanted"; `reporting_disabled` reclassified redundant. 8 read · 5 deliberately unread · 1 redundant · 6 still owed. Also filed: R-321 (a deliberately-silent box still alarms stale/down — the checker is age-only, and decoding the flag would not have fixed it), R-322 (the claim guard has never scanned the hub; a hand scan returns zero, so it is a scope gap, not a defect).
This commit is contained in:
@@ -1,3 +1,110 @@
|
||||
## v0.104.0 — the hub can see whether a guest's networking works, and one name per secret (2026-08-13, R-319 + R-295 hub half)
|
||||
|
||||
Two things, both hub-only. **No wire change, no agent change, no controller change, nothing to bake.**
|
||||
|
||||
### R-319 — the guest-network watchdog finally has a reader
|
||||
|
||||
The agent has reported `guest_net` on every heartbeat since **v0.92.0** (R-54, 2026-07-21). The string
|
||||
`guest_net` occurred **nowhere** in this repository: the stanza was stored as raw text inside
|
||||
`report_json` and read by nothing — no check, no alarm, no screen. This is the first of R-264's
|
||||
twenty-one unconsumed facts to be given a reader, and it was chosen because it has a live incident
|
||||
behind it: a killed `dhclient` in a guest took a tunnel down for 1 h 15 m with nobody told
|
||||
(`audits/INCIDENT-guest-dhclient-killed-2026-07-20.md`).
|
||||
|
||||
**Established before anything was written.** The facts arrive and are persisted:
|
||||
`demo-felhom-8363b5`'s newest row carries `guest_net.checked_at` plus per-guest
|
||||
`vmid/state/mode/ip/has_route/dhclient_alive/checked_at/message`, and the agent's wire type
|
||||
(`felhom-agent/internal/hub/report.go:139-160`) additionally carries `healed`, `heal_succeeded`,
|
||||
`last_heal_at`, `heals_last_hour`, `damped` — absent from the live rows only because they are
|
||||
`omitempty` on a box that has never needed a repair.
|
||||
|
||||
**THE SIGNAL IS THE REPAIR COUNT, NOT THE STATE.** A guest whose network the watchdog keeps repairing
|
||||
is healthy at every instant anyone looks and is nevertheless failing. Rendering `state` alone would
|
||||
give that machine a green tick — the exact shape of the defect that drew a failed disk as a healthy
|
||||
empty disk. `heals_last_hour` is therefore surfaced *beside* the state, not behind it, and drives the
|
||||
card's summary badge.
|
||||
|
||||
`heal_succeeded` is decoded too, deliberately. **R-260 is this project's standing warning that a
|
||||
decoder three fields short of the agent dropped the one that decided the question the checker
|
||||
existed to answer.** Six failed repairs is a guest that is down; six successful ones is a nuisance.
|
||||
|
||||
**An unknown is never drawn as healthy.** Three absences are kept apart, each with its own sentence,
|
||||
following the companion-flag convention (`hostNetworkView` is the sibling):
|
||||
|
||||
- the agent predates v0.92.0 — absence is expected, and still tells us nothing;
|
||||
- a capable agent sent no stanza — the watchdog is default-on, so this means it was switched off;
|
||||
- a guest whose own `state` is empty or unrecognised — the state switch is an ALLOW-LIST, so adding a
|
||||
state to the agent can never silently paint it green here.
|
||||
|
||||
A malformed stanza degrades to unknown and **must not 500 the page** — one box's bad field cannot
|
||||
break the host page for the rest of the fleet.
|
||||
|
||||
**No alarm was added, and that is a judgement rather than an omission.** The incident behind this was
|
||||
about nobody being able to *see* the condition, not about nobody being paged; and a new alarm on a
|
||||
fleet of two demo machines is untested noise on a dispatcher whose severity contract is exact-match
|
||||
lowercase. Revisit when a third machine exists, or when a repair count is observed climbing on real
|
||||
hardware — the visible count is what will supply that evidence.
|
||||
|
||||
**Red-proofs, each mutation asserted applied by grep before its run and reverted after:**
|
||||
|
||||
1. the unknown branches replaced by the healthy badge → **both** scenario-C sub-cases go red, a silent
|
||||
machine seen rendering as healthy. *(the one that matters)*
|
||||
2. `RepairCount: 0` in the decoder → B goes red on *"a guest repaired 6 times in an hour is reported
|
||||
as fine"*.
|
||||
3. `Degraded()` forced true → A goes red on *"a machine that is fine is being alarmed on"* — the guard
|
||||
is reachable in both directions, not just the alarming one.
|
||||
|
||||
**Positive control that the wiring is real and not merely written:** `wire_contract_gate.py`'s
|
||||
checked-tag count rose **182 → 190** and its skipped count fell **88 → 80**, because the eight
|
||||
`guest_net` allowlist entries were REMOVED. An allowlisted tag is *skipped*, so leaving them would
|
||||
have meant the new reader's own fields were never checked for reachability at all.
|
||||
|
||||
### R-295 hub half — one name per secret, and the mail names the page the machine shows
|
||||
|
||||
The box side shipped 2026-08-10; the hub side was dropped twice and was the last place the retired
|
||||
name survived — in the **mails**, the one surface a customer reads *before* they see any screen.
|
||||
|
||||
- the three-word dashboard code is **„Beállító kód"** everywhere: the mail bodies, the mail subjects,
|
||||
the operator button, and the lockout notice;
|
||||
- the ten-word escrow code stays **„Helyreállítási kód"**, and no dashboard-code mail names it —
|
||||
naming both secrets in one message is how a customer comes to believe they are the same thing;
|
||||
- **„Visszaállító kód" is retired.**
|
||||
|
||||
**New email kind `reenroll`, because the mail must name the page the machine is actually showing.**
|
||||
`ReissueForReenroll` sent the *reset* mail, which directs the customer to an „Elfelejtett jelszó"
|
||||
page — but a rebuilt box has no password, so the controller renders „A szerver beállítása"
|
||||
(`web/claim.go:279`, `reset := s.authEnabled()`) and serves **no login page at all**. The route named
|
||||
was not on their screen. Only the hub can tell the two situations apart, because it is the hub that
|
||||
chose which call site fired. Same secret, same name, different sentence — which is exactly what the
|
||||
ruling asks for. The re-enrol mail deliberately says **nothing** about apps or backups: a clean-slate
|
||||
reinstall is precisely where such a reassurance could be false.
|
||||
|
||||
**This is naming, not function.** No acceptance logic moved.
|
||||
`TestReenrollSplit_ChangesTheMailNotTheSecret` pins it: the generation still advances, the stored
|
||||
bcrypt hash still verifies the emailed code, no plaintext is persisted. On the box side the companion
|
||||
pin is the controller's `TestResetCode_StillAcceptedOnTheSetupPage`.
|
||||
|
||||
**Two red-proofs:** routing re-enrolment back to `EmailReset` reddens both the new pin and the
|
||||
pre-existing `TestReissueForReenroll`; putting „Visszaállító kód" back in the reset body reddens
|
||||
`TestFormatClaimEmail_OneNamePerSecret`.
|
||||
|
||||
**The claim guard has never scanned the hub.** `retrieval_promise_gate.py` lives in
|
||||
`felhom-controller/controller/scripts/` and its declared surfaces are that repo's templates plus one
|
||||
Go handler file. A scan of `felhom.eu/hub/` with the gate's own four stems returns **zero** — so
|
||||
nothing was hiding, and the new strings carry no stem and need no registration. That the gate's scope
|
||||
excludes a customer-facing surface is recorded as R-322 rather than fixed here.
|
||||
|
||||
### Also
|
||||
|
||||
- `wire_contract_gate.py` grows a **third** entry kind — `not consumed, deliberately` — carrying the
|
||||
operator's 2026-08-12 rulings with their date. Five facts move to it; `reporting_disabled` moves to
|
||||
`redundant`. **Those rulings were not in the register before today**, which is why R-264 now records
|
||||
them. Where the twenty stand: **8 read · 5 deliberately unread · 1 redundant · 6 still owed**.
|
||||
- **R-321 filed, not fixed:** the staleness checker is age-only, so a box on which reporting is
|
||||
deliberately switched off still alarms `node_stale` then `node_down`. Decoding `reporting_disabled`
|
||||
would have felt like progress and left the alarm firing; the fix belongs in the checker, which
|
||||
already holds the `health.status = "disabled"` it needs.
|
||||
|
||||
## v0.103.0 — a host can read the packages we kept for it (2026-08-12, R-311)
|
||||
|
||||
**`ListSupersededEscrow` had zero production callers for nineteen days.** It is the only reader of a
|
||||
|
||||
Reference in New Issue
Block a user