burn-down night: hub v0.138.0 source (R-879 seal + roll-back, R-136, R-283, R-349/R-276 hub halves), gates R-315/R-422 closed, R-325/R-426 partial, decisions 132-133, 05 §16 (195 -> 193)
gates / gates (push) Successful in 2m6s
gates / gates (push) Successful in 2m6s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -419,7 +419,8 @@ Hungarian „öt szó" is correct and unchanged.
|
||||
|
||||
### 16.1 Form protection (CSRF) on both login paths (R-135)
|
||||
|
||||
The operator logs in two ways: a browser session (`hub_session` cookie + a per-session token on every form) and HTTP
|
||||
The operator logs in two ways: a browser session (`__Host-hub_session` cookie since v0.138.0, R-136 — always Secure, `Path=/`, no Domain, so a sibling
|
||||
subdomain cannot plant one; + a per-session token on every form) and HTTP
|
||||
Basic for scripts. Until v0.135.0 a state-changing request with NO cookie skipped the token check — on the reasoning
|
||||
that it must be a script. It need not be: a browser caches Basic credentials per origin and resends them on a
|
||||
cross-site form POST (SameSite does not govern the Authorization header). Now a request without a session passes
|
||||
@@ -439,9 +440,18 @@ Legacy rows are sealed in place at start-up (`SealLegacyRecoverySecrets`, measur
|
||||
refused; a wrong key → a reveal is a 500 with nothing in the body or the log. Both retrieval paths (the operator page and
|
||||
the global-key API) open through `GetHostRecoveryCredential`, so the break-glass route still works with the UI down.
|
||||
|
||||
**What a copy of `hub.db` still holds readable** (R-879): each box's hub API key, each household's owner passphrase
|
||||
(`customer_configs.retrieval_password`) and API key, and the PBS-DR token values (`host_pbs_secrets.value`, kept after
|
||||
use). **And the key is the other half:** a backup of the database restores a hub that can open the sealed columns only
|
||||
**Since v0.138.0 (R-879) four more columns are sealed the same way:** each box's hub API key (`hosts.api_key`), each
|
||||
household's controller API key and owner passphrase (`customer_configs.api_key`, `retrieval_password`) and the PBS-DR
|
||||
token values (`host_pbs_secrets.value`). Hashing or deleting was not possible — every one of these values is served again
|
||||
(re-enroll returns the key, the config ships the controller key, the operator page shows the passphrase, a PBS token can
|
||||
be re-staged). The two API keys also carry an UNKEYED SHA-256 twin (`api_key_hash`, backfilled at every start), and a box
|
||||
is looked up by that hash — **so a box authenticates even when the sealing key is missing or wrong** (`TestR879_BoxAuthSurvivesFailedSealing`).
|
||||
Legacy rows are sealed at start-up (`SealLegacyBoxSecrets`, idempotent, non-fatal). A value that does not open marks the
|
||||
record unreadable: its serve paths answer 500, a save refuses it (never blanks it), a PBS token is not consumed. **Roll-back
|
||||
to a hub before v0.138.0** needs the columns opened first: `felhom-hub -unseal-box-secrets` (all or nothing; exit 1 =
|
||||
nothing changed) — run in the running pod, then put the old image back. `guests.api_key` is an unused, always-empty
|
||||
column and is not sealed. *Decided by CC unattended — operator may reverse (`09` §3 decision 132).*
|
||||
**And the key is the other half:** a backup of the database restores a hub that can open the sealed columns only
|
||||
with the same `OFFSITE_SECRET_KEY`; today that key exists only on DooPlex (the k8s Secret, and the GPG secrets export
|
||||
on the same machine). The off-site plan for the database and its key: `runbooks/RUNBOOK-hub-db-offsite-backup.md`
|
||||
(R-173 — option A decided 2026-10-05, `09` decision 125; §16.3).
|
||||
|
||||
@@ -913,6 +913,19 @@ its length, and both fixes cost something the household would notice — operato
|
||||
(a 75 GiB drill box) and changes a behaviour operators rely on, with no measured floor behind 120. **Chosen (a)**;
|
||||
reversible by renaming back. Whether a REAL floor exists is unmeasured. Installer 1.32.0 (`installer-v1.32.0`, not cut).
|
||||
|
||||
132. **A hub with no sealing key, asked to write a NEW box secret (R-879).** Options: (a) refuse — enrolment, customer
|
||||
creation, passphrase regeneration and a PBS mint fail until the key is back, existing boxes unaffected; (b) write it
|
||||
in plaintext — silently re-creates the readable secret the row removes. **Chosen (a)**: `05` §16.2 already says
|
||||
„no key → a save is refused" for every sealed column, and the production hub runs with the key. The box lookup is
|
||||
an UNKEYED hash so that authentication never depends on the key. Hub v0.138.0.
|
||||
133. **What removing a household's own off-site target does to its repository password (R-729, R-545).** Options: (a)
|
||||
shred it always — a hub-held package then hits the R-241 mint refusal, and history on the NAS loses its on-box key;
|
||||
(b) refuse the whole press while the hub holds a package — refused on practically every box, R-729 unsolved; (c)
|
||||
clear the target, SSH key and known-host line always; keep the password whenever anything could depend on it (a
|
||||
hub package, escrowed, a successful run, snapshots), delete it otherwise. **Chosen (c)**: R-241's rule (never drop
|
||||
a key a package protects) and „never the repository"; keeping a key is reversible, deleting it is not. Cost: on
|
||||
most boxes the 0600 password file stays with no target. `07`. Controller (next release).
|
||||
|
||||
### 2026-10-05 (~21:00) — three rulings, the reviewer's picks given to the operator (recorded before the work; the burn-down night)
|
||||
|
||||
128. **R-126 — a `.fab` export onto a network drive.** **Refuse an export WITHOUT a password to any network drive; with
|
||||
|
||||
@@ -32,3 +32,15 @@ cherry-picks onto `main`, writes CHANGELOG, closes rows, pushes and watches CI.
|
||||
| R-179 | fixed on main (NAS units; data-drive units left, GL6-F2) | 25 | `c11d4fbf` |
|
||||
| R-310 | fixed on main + runbook line | 15 | `b43705ac` |
|
||||
| R-274 | fixed on main (disclosure + pinning tests; check shipped with R-297) | 15 | `f790734e` |
|
||||
| R-879 | fixed (hub v0.138.0) + roll-back command; decision 132; close after deploy | 75 | `5f060e3d`, `91e4ace9` |
|
||||
| R-136 | fixed (hub v0.138.0); close after deploy | 20 | `e221476f` |
|
||||
| R-283 | fixed (hub v0.138.0): box's own claim state shown; un-claim NOT taken (operator's call) | 30 | `daeed175` |
|
||||
| R-540 | moved to D (multi-box design + money) | 10 | — |
|
||||
| R-435 | moved to D (per-app counts, two repos, unmeasured threshold) | 10 | — |
|
||||
| R-315 | fixed and closed (gate tooling is live on push) | 25 | `900e6d86` |
|
||||
| R-325 | felhom.eu half fixed; controller half → helper ctrl-c | 15 | `7ee00d59` |
|
||||
| R-422 | fixed and closed | 25 | `bf179847` |
|
||||
| R-426 | partial (20 → 16 exemptions); row stays open | 15 | `68df84bc` |
|
||||
| R-502 | moved to A (a Docker-running gate on DooPlex needs an operator word) | 10 | — |
|
||||
| R-349 | hub half fixed (hub v0.138.0) | 20 | `a5b4d29a` |
|
||||
| R-276 | hub half fixed (hub v0.138.0) | 15 | `0a460bcd` |
|
||||
|
||||
@@ -26,6 +26,15 @@
|
||||
|
||||
---
|
||||
|
||||
## 2026-10-05 (night) — the burn-down night: gate fixes
|
||||
|
||||
The full text of every row below: `git show 86def579:documentation/backlog/OPEN-ITEMS.md`.
|
||||
|
||||
| Row | What | Closed | Evidence |
|
||||
|---|---|---|---|
|
||||
| **R-315** | **The wire-contract gate's positive control FAILS on the new wire: it checks name-presence, not decodability.** (P4) | CLOSED 2026-10-05 — FIXED (the burn-down night): mirrored roots are checked field by field | felhom.eu `900e6d86`: `scripts/wire_contract_gate.py` checks the receiver's mirror type for the hub→agent escrow/retained and hub→controller escrow-ACK roots and prints each root's check; decoys `wire-mirror/renamed` (convicts — the measured `superseded_at` rename) and `wire-mirror/genuine` (passes) in `scripts/test_gate_decoys.py`; red-proof: deleting the MIRRORS entry lets the renamed decoy pass. The two report wires stay name-reachability (printed as such). |
|
||||
| **R-422** | **`reuse_refs_check.py` only checks citations whose extension is one of `go py html css yml yaml sh`.** (P4) | CLOSED 2026-10-05 — FIXED (the burn-down night): .md citations are checked | felhom.eu `bf179847`: `scripts/reuse_refs_check.py` checks `.md` citations; audits/ and documentation/tests are indexed for .md only; walk across all four repos' REUSE.md: 0 failures after one fix; tests `scripts/test_reuse_refs_check.py` (3) + the KNOWN-HOLE decoy now expects a conviction; red-proofs recorded in the night log. |
|
||||
|
||||
## 2026-10-05 (night) — the burn-down night: stale rows
|
||||
|
||||
The full text of every row below: `git show c8da8e04:documentation/backlog/OPEN-ITEMS.md`.
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user