docs: v0.93.0 publish train executed — built, published, vouched, deployed fleet-wide

This commit is contained in:
2026-07-22 09:03:01 +02:00
parent eba040d0be
commit c230258542
3 changed files with 69 additions and 60 deletions
+4
View File
@@ -1,5 +1,9 @@
## v0.93.0 — a recovery code can no longer contain a hyphenated word (2026-07-21)
> **SHIPPED 2026-07-22:** built + published (sha256 `a68b2ff73200622e…`), Day-0-manifest-vouched,
> deployed to both fleet boxes (felhom-pve + demo-hp), clean-restart verified. Record:
> `felhom.eu/documentation/pilot/RUNBOOK-publish-agent-0.93-2026-07-22.md`.
**Generation-only change. Every recovery code already issued remains valid** — R is consumed as a
whole passphrase by the PBS scrypt KDF (`Wrap`/`Unwrap`) and is never re-split, so nothing about
verification moves. Nothing in the KDF/consume path, the word count, or the joiner changed.
+8
View File
@@ -5,6 +5,14 @@
## Current
- **2026-07-22 — v0.93.0 is the FLEET AGENT.** Built, published (sha `a68b2ff73200622e…`),
Day-0-manifest-vouched (MinAgent also 0.93.0, operator-ruled) and deployed to BOTH boxes
(`demo-felhom-8363b5` + `demo-hp-bb76ea`, the latter over G1 break-glass — still no key baked);
clean-restart 5/5 on both, `.bak-0.92.1` retained. Discharges the onboarding runbook §A5
ceremony gate. Record: `felhom.eu/documentation/pilot/RUNBOOK-publish-agent-0.93-2026-07-22.md`.
**The bullet below ("agent is DOWN … deployed 0.90.0") is SUPERSEDED history** — vmbr0 was made
static .162 on 2026-07-20 (F1 mitigation) and the agent has been up since; kept for the record.
- **2026-07-20 — REMOTE SITE until ~2026-08-02; the agent is DOWN there and cannot self-recover.**
felhom-pve moved off the home LAN; `ssh felhom-pve` = tailnet `100.70.170.35` (direct, ~37 ms). The
host is on DHCP and holds `192.168.0.147`, so `localapi`'s literal `192.168.0.162` bind fails with
+57 -60
View File
@@ -1,79 +1,76 @@
# REPORT — escrow recovery-code wordlist fix (TASK-E Part 1), 2026-07-21
# REPORT — v0.93.0 publish train executed: built, published, vouched, deployed fleet-wide (2026-07-22)
**Version: v0.93.0. Baseline `935904f` (v0.92.1), clean tree. NOT built or deployed** — no operator
action needed today; this rides the next agent publish train.
**The previous report's "NOT built or deployed" line dies here.** v0.93.0 (hyphen-free escrow
wordlist, `a452dc33`, red-proofed 2026-07-21) is now the fleet agent. No code changed this train
this was the build/publish/deploy leg. Full operational record:
`felhom.eu/documentation/pilot/RUNBOOK-publish-agent-0.93-2026-07-22.md`.
## What was wrong
## Confirmed baselines (re-verified at live source before acting)
The EFF large wordlist contains exactly four entries that themselves contain the hyphen the words are
joined with: `drop-down` (L2009), `felt-tip` (L2528), `t-shirt` (L6640), `yo-yo` (L7748). Drawing one
produced a recovery code that reads as **11 words instead of 10** — ambiguous to transcribe in the one
situation R exists for, a customer reading a code back during a disaster.
- `main` = `eba040d0` = origin/main, clean tree; v0.93.0 delta vs 0.92.1 = escrow code + tests +
docs only, **no `configs/` change** → binary-only deploy (sudoers/wrapper untouched).
- Both boxes 0.92.1 + active (hub `hosts` table AND direct `--version`); Day-0 manifest agent
0.92.1/`7424bc1c…`; floor 0.156.0; hub 0.70.0; package `felhom-agent/0.93.0` = 404 pre-publish.
**The "known flake" was this defect, not a flaky test.** Per code the hit rate is
`1 (7772/7776)^10 ≈ 0.51%`; over the test's 50 draws that is ≈ **23%**, which is exactly the "fails
~1/5" that had been documented in the `felhom-testing` skill and re-run past for weeks.
`felhom.eu/REPORT.md` §6 had it at 3/8 in one session.
## Artifact
## The fix
```
AGENT_VERSION=0.93.0
AGENT_SHA256=a68b2ff73200622ea9e23aed71a0a8ff885808626cab9e9d554a5a5df5ced28f (13 817 690 bytes)
```
`joinSafe` filters those four out of the effective wordlist at init. Generation-only.
| | before | after |
|---|---|---|
| draw space | 7776 | **7772** |
| 10-word code entropy | 129.248 bits | **129.241 bits** |
| margin over the 128-bit floor | 1.248 | **1.241** |
The cost is **0.007 bits**. Both numbers are asserted in `TestEntropyFloorSurvivesFiltering`, so a
future wordlist swap cannot quietly move the floor.
**Every recovery code already issued remains valid**, and this was verified rather than assumed: R is
consumed as a whole passphrase by the PBS scrypt KDF (`Wrap`/`Unwrap`), and `grep` confirms nothing in
the consume path ever splits it. The joiner, the word count and the KDF path are untouched.
Built on DooPlex from `eba040d0` (`CGO_ENABLED=0`, ldflags version); published via
`scripts/publish-agent.sh` (pre-delete 404 → PUT 201 → round-trip sha match); **sha-chain identical
at three independent points** incl. an anonymous GET of the package URL.
## Tests
`TestGenerateRecoveryCode_EntropyAndFormat` now counts words by **generation count**, not by splitting
the joined string — conflating those two is what made it flake. It asserts the segmentation property
separately, because that is the property `joinSafe` actually buys.
Green gate on the exact built HEAD: `go build ./... && go vet ./... && go test ./...` — green.
**The formerly-flaky escrow package: `-count=10` fresh → 10/10 pass (135.2 s).** The ~1/5 flake WAS
the hyphenated-word defect (see the 2026-07-21 report), so a clean 10× run is direct evidence the
fix is in the built artifact.
New in `wordlist_test.go`: the four filtered words are pinned by name (a wordlist swap that changes the
set fails loudly); the entropy numbers above; and a production-wiring test that drives the exported
`GenerateRecoveryCode` 500×, so the fix is proven at the entry point and not only in the helper.
## Manifest vouch (rule 1 — before any box moved)
### Red-proof — RUN, both deterministic and probabilistic halves
Viktor saved Day-0 artifacts (password-gated UI, the train's one STOP); CC verified in the live hub
DB before deploying: agent **0.93.0 / a68b2ff7…**. Golden, floor and wrapper sha **byte-identical
before/after** (0.153.0 / 0.156.0 / `104db0a4…`). **Deviation, ruled:** MinAgent moved
0.92.1→0.93.0 at the save (spec said do-not-touch); flagged before deploy, ruled leave-as-is —
transient held-floor window only, both boxes already ran controller ≥ floor.
Pre-fix shape restored (`var wordlist = parseWordlist(wordlistRaw)`), suite re-run:
## Deploys — felhom-pve first, then demo-hp
```
--- FAIL: TestWordlistLoaded
effective wordlist should be 7772 words (7776 EFF - 4 hyphenated), got 7776
--- FAIL: TestGenerateRecoveryCode_EntropyAndFormat
joined code must segment into 10 words, got 11 (a drawn word contained "-")
--- FAIL: TestEntropyFloorSurvivesFiltering
filtered entropy moved: 129.248, expected 129.241
--- FAIL: TestGenerateRecoveryCode_NeverContainsAmbiguousWord
code 193 segmented into 11 parts, want 10
```
Channel per the 0.90 train: scp one hop → on-box sha256 verify (= `a68b2ff7…` both) →
`.bak-0.92.1``install -m0755` → restart. demo-hp reached over the hub-vaulted G1 break-glass
(no key baked; secret handled file→file via `SSH_ASKPASS`, shredded after, never printed).
Fix restored → green; `git diff` clean.
Per-box clean-restart verification — **5/5 green on both** (journal times host-local CEST):
`TestGeneratedCodeSegments_FilteredVsUnfiltered` is the **deterministic** companion: it drives the
generator against a fixture list where every word is hyphenated, so the pre-fix defect reproduces with
probability 1 rather than ~1/5, and shows the same list through `joinSafe` refuses to generate at all.
| Check | felhom-pve (restart 06:56:49Z) | demo-hp (restart 06:57:48Z) |
|---|---|---|
| active / no restart loop | ✅ active, NRestarts=0, clean startup `version=0.93.0` | ✅ same |
| capabilities | ✅ 68/68 ok, degraded=0, inactive=0 | ✅ 68/68 ok, degraded=0, inactive=0 |
| ReassertGuestBinds | ✅ only enrolled `uuid:47a3361a…` (`/mnt/hdd_1`→9201) | ✅ only enrolled `uuid:91d2dc2d…` (`/mnt/nvme-1tb`→9201) |
| guestnet sweep | ✅ watchdog up; sweeps quiet, 0 ERROR/WARN since restart | ✅ same |
| hub `agent_version` | ✅ 0.93.0 (report at startup, 06:56:52Z) | ✅ 0.93.0 (06:57:52Z) |
## Gates
Escape hatch not needed; `.bak-0.92.1` retained on both boxes. `peti-felhom` deliberately not
deployed to (down; gets 0.93.0 via Friday's reinstall + the vouched manifest — by design).
`go build ./... && go vet ./... && go test ./...`**green**. The ex-flaky package was additionally
run 10× consecutively: 10/10 pass (it previously failed ~1 in 5).
## Explicit statements (per the train spec)
## Observations
- Controller floor and MinAgent handling: floor **byte-identical** before/after (0.156.0); golden
entry byte-identical; MinAgent's 0.93.0 value is the one ruled deviation above.
- No escrow ceremony was run as "verification" — the proof is the generation-path tests + version
reporting, per the spec.
- This train discharges `RUNBOOK-onboarding-draft-v4.md` §A item 5 (agent ≥ 0.93.0 box-side before
the first real tester's escrow ceremony) — annotated there.
- The `felhom-testing` skill's "Known flake … re-run before diagnosing; it is not a regression" line
is now removed. It had been actively harmful: it told every future session to dismiss a true
positive. Replaced with the generalised lesson — "known flake, just re-run it" is a diagnosis and
needs evidence like any other.
- `WordlistSize()` now reports the **effective** (filtered) draw space, 7772. `WordlistFilteredOut()`
is new, for audit. Any external consumer expecting a literal 7776 would need updating; there is none
outside the tests.
## Observations (recorded, not acted on)
- The spec's "ROADMAP wordlist row" does not exist in `felhom.eu/documentation/backlog/ROADMAP.md`;
the gate discharge was annotated at its real home (onboarding runbook §A5) instead.
- demo-hp's 1TB NVMe is now **enrolled** (reasserted into 9201) — TASK-H's "unenrolled/NTFS, do not
touch" note is outdated.
- demo-hp remains key-less; break-glass deploy worked cleanly. Tailnet key-expiry toggle still open.
- CONTEXT.md's stale "agent DOWN at remote site / deployed 0.90.0" bullet superseded this train.