docs(v1.24.0): R-59/R-60/R-61 SHIPPED — CHANGELOG, README, ROADMAP (+R-62), runbook, capability map, drill evidence, REPORT

Virgin-ISO nested drill closed the train: dead-NIC install baked the
fallback (incl. the dead default gateway), the R-59 screen painted
(capture committed beside the spike doc), the cable move healed +
registered at the hub in 23s unaided, and the build's rootpw file
matched the installed box's shadow hash. R-59 SHIPPED with the recorded
deviation (first-boot gate; installer-initrd abort out of scope by
operator ack). R-60 SHIPPED (spike + drill cited; F-P9 route-flush fix
included). R-61 slice 1 SHIPPED. New R-62 row (hub delete-dialog
cosmetics, XS). Capability map: new PROVEN-LIVE row (nested != metal,
said so). Cleanup verified: felhom-pve interfaces byte-identical,
bridge/VMs/ISO removed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UuFPHmHNrCJj1VhY6QdDMU
This commit is contained in:
2026-07-22 11:45:55 +02:00
parent 699325bd8a
commit a12c6f9730
8 changed files with 218 additions and 87 deletions
+52
View File
@@ -1,5 +1,57 @@
# Felhom scripts — Changelog
## ISO train v1.24.0 — the box that can never call home learns to say so, heal itself, and let you in (R-59 + R-60 + R-61) (2026-07-22)
All three findings are from the HP t740 install (2026-07-21). Proven end-to-end on a nested
wrong-NIC install (spike + virgin-ISO drill: `documentation/audits/SPIKE-firstboot-nic-sweep-2026-07-22.md`).
- **R-59 — the silent unreachable box gets a legible screen.** A no-DHCP install bakes the
installer's **192.168.100.2 fallback as a static `vmbr0` config** (with a dead
`gateway 192.168.100.1`) and completes; the box then waited silently forever. The first-boot
bootstrap now runs a **network gate before mode dispatch**
(`scripts/iso/felhom-bootstrap.sh:251` `network_gate`, called at `:510`): hub unreachable →
physical-NIC diagnosis (name/MAC/carrier/speed; NICs are raised first — unused ports sit
admin-DOWN with unreadable carrier), the fallback signature named when present, and a calm
Hungarian console screen (`paint_network_screen`, `:230` — the `print_pairing_banner` pattern)
with the NIC table and the one-line remedy; then retry every 60 s with the unit held in
`activating` (waiting is not failing — the v1.21.0 lesson, unit `TimeoutStartSec=infinity`).
The trigger is always **hub-unreachable, never the fallback signature**: the drill proved a
second wrong-NIC variant (the installer picks the NIC that LEASES and bakes the lease as
static — a later cable move leaves a plausible-looking dead config with no signature).
*Recorded deviation:* R-59 ships as this first-boot refuse-loudly gate, not an install-time
abort — the fallback is baked inside the Proxmox auto-installer itself, unreachable without an
installer-initrd hook (out of scope by operator ack, noted on the ROADMAP row).
- **R-60 — first-boot NIC sweep self-heal.** While the hub is unreachable AND no install attempt
has begun (`state.json` gate; the unit's `ConditionPathExists=!done-flag` confines the whole
script to pre-install anyway), the gate sweeps (`sweep_nics`,
`scripts/iso/felhom-bootstrap.sh:178`): per candidate NIC, re-point `vmbr0`'s `bridge-ports`
(atomic tmp+mv, always rendered from the pristine copy) → `ifreload` → **flush vmbr0's
addresses/routes** → bounded `dhclient` (20 s) → hub probe (any HTTP status counts — the hub
answers `/` with 302). The flush is load-bearing: the baked fallback default route survives a
successful lease otherwise (dhclient-script never replaces an existing default route) and the
probe rides the dead gateway — found live in the drill. First success persists
`bridge-ports <winner>` + `iface vmbr0 inet dhcp` (original kept as
`interfaces.felhom-bak`) and proceeds; total failure restores the config **byte-identically**.
Drill: cable moved → healed + registered at the hub in well under a minute, unaided.
- **R-61 slice 1 — the baked root password becomes knowable.** `build-felhom-iso.sh` writes the
minted throwaway plaintext to a **0600 sibling `<iso>.rootpw.txt`** (password + ISO name +
build date; `scripts/iso/build-felhom-iso.sh:254`) — the single record of truth. Never
stdout/logs; the manifest carries only a pointer (`:383`). A fixed well-known password stays
REJECTED (operator ruling 2026-07-21). Drill: the emitted password's hash matches the
installed box's `/etc/shadow` — the console lockout is closed.
- **Harness** (`scripts/iso/test/bootstrap-modes.sh`): caught up with the v1.21.0 in-script wait
(the old pairing scenarios would hang), plus four gate scenarios — hub-reachable = ZERO new
behavior (no ip/ifreload/dhclient/sleep call, fixture untouched), sweep-success (persist +
bak + proceed), sweep-fail (screen + byte-identical config), state-file-present (sweep NEVER
invoked). New `test/rootpw-emission.sh` for R-61 (emission, 0600, plaintext↔answer-hash
cross-check, manifest guard). **Four red-proofs run and restored:** manifest-plaintext leak,
restore-on-failure dropped, state gate removed, hub-reachable early-return removed — each
failed exactly its guarding assertion.
- Assistant image gains `python3` (the bootstrap's JSON parsing needs it; PVE ships it on real
boxes — the harness runs the bootstrap in the image). `build-felhom-iso.sh` gains the
`FELHOM_ISO_KEEP_WORK=1` debug/test escape and resolves `OUT_ISO` before the mint (the rootpw
sibling is named after the ISO).
## build-hub.sh v1.23.0 — the hub build script enters the repo (2026-07-21)
It never was in one. `/mnt/5_hdd/felhom.eu/build/felhom-hub/build.sh` lived only in a DooPlex build
+21 -5
View File
@@ -49,7 +49,13 @@ scripts/iso/build-felhom-iso.sh \
> `/mnt/5_hdd/felhom.eu/felhom-iso/out`** (override with `$FELHOM_ISO_OUT` on other hosts), so on 180 you
> can omit `--out` entirely and output still lands on the HDD, never the SSD.
Output: `felhom-pve-<pvever>-v<isover>-<profile>.iso` + `.sha256` + `.manifest.txt`.
Output: `felhom-pve-<pvever>-v<isover>-<profile>.iso` + `.sha256` + `.manifest.txt` +
**`.rootpw.txt` (0600, R-61 slice 1)** — the minted throwaway root password in plaintext (one line:
password + ISO name + build date), the operator's console credential for every box installed from
that build. It is the SINGLE record of truth: never logged, never in the manifest (which carries
only a pointer — manifests get pasted into committed REPORTs), **never committed or pasted
anywhere**. A fixed well-known password stays rejected (operator ruling 2026-07-21): a pre-pairing
box sits on a stranger's LAN.
The build **gates the answer on validate-answer's OUTPUT TEXT, never `$?`**`validate-answer`
returns exit 0 even on a broken file (spike S1 trap; also `prepare-iso` was observed to exit 0 on a
@@ -170,10 +176,20 @@ wired NICs and the install picks wrong:
| `wlo1` | `iwlwifi` | wifi | unused |
Plug the cable into the **onboard** port. If the install already happened on the wrong port, the
symptom is nasty: the installer does not abort, it bakes its **192.168.100.2 fallback as a STATIC
`vmbr0` address** and completes, so the box looks installed and can never reach the hub (R-59).
Repair on the console: point `bridge-ports` at `enp2s0f0` in `/etc/network/interfaces`, set the
correct address (or `dhcp`), `ifreload -a`. R-60 is the self-heal that would make this unnecessary.
symptom used to be nasty: the installer does not abort, it bakes its **192.168.100.2 fallback as a
STATIC `vmbr0` config** (address AND a dead `gateway 192.168.100.1`) and completes, so the box
looks installed and can never reach the hub (R-59). **Since v1.24.0 the trap has a gate and a
self-heal (R-59+R-60):** on first boot, hub-unreachable → the bootstrap sweeps every physical NIC
(re-point `bridge-ports` → bounded DHCP → hub probe) and keeps the first that reaches the hub
(persisted as `iface vmbr0 inet dhcp`, original in `interfaces.felhom-bak`); if nothing works it
paints a legible Hungarian console screen (NIC table: név/MAC/kábel/sebesség + remedy) and retries
every minute — so *moving the cable to any port is enough, no console repair needed*. The sweep
runs only before the install attempt begins (`state.json` gate); a box already past that point
gets the screen but its interfaces are never touched. Manual repair (pre-v1.24.0 boxes): point
`bridge-ports` at `enp2s0f0` in `/etc/network/interfaces`, set the correct address (or `dhcp`),
`ifreload -a` — and mind the baked stale `gateway` line. Proven end-to-end on the nested drill
(`documentation/audits/SPIKE-firstboot-nic-sweep-2026-07-22.md`); metal proof rides the next real
install.
**Confirm the serial is the system disk and not a data drive.** On this board the SanDisk X600 128GB
(`sda`) is the system disk; the 1TB NVMe is the future data drive and must stay OUTSIDE the filter —