docs: the public ISO release gate, written BEFORE the first release image

A standard defined in advance cannot be rationalised afterwards, and this is the artifact that most
needs one: once a file is on iso.felhom.eu and someone has downloaded it, it cannot be recalled.

Twelve criteria, each checkable against the UPLOADED FILE rather than the build inputs, and each
carrying the spike measurement that justifies it:

- G1 no answer.toml / auto-installer-mode.toml — deletes the whole Spike 1-2 problem space and
  removes the Automated menu entry by construction rather than by a guard
- G2/G3/G4 no root hash, no SSH key, no customer identity — the shared-credential classes
- G5 credential scan by ENUMERATION against the stock ISO, not a pattern sweep (Spike 1 found
  /answer.toml precisely because the earlier recon grepped the wrong file)
- G6 menu present, interactive default, timeout >= 10 (Spike 2 lost a probe to a 1-second menu),
  underscore timeout_style, and the banned-token safety gate kept unchanged
- G7/G8 the felhom .deb present, and a postinst that cannot fail: no systemctl start/daemon-reload
  (no systemd runs in the installer chroot), no network use (the cable may be out), no 'set -e',
  ends 'exit 0'
- G9 felhom-bootstrap.sh byte-identical to repo HEAD — the one frozen, drift-capable payload
- G10 every build input committed (Spike 1: demo-felhom came from an uncommitted profile)
- G11 published checksum AND a verified download round trip
- G12 bucket Public Access stays Disabled

Committed on its own, before any build.
This commit is contained in:
2026-07-31 16:33:14 +02:00
parent 61e9b55737
commit e787391c0a
+180
View File
@@ -0,0 +1,180 @@
# The public ISO release gate
> **Written before the first release image was built** (2026-07-31), deliberately: a standard defined
> in advance cannot be rationalised afterwards, and this is the artifact that most needs one — once a
> file is on `iso.felhom.eu` and someone has downloaded it, it cannot be recalled.
>
> Design inputs: `documentation/audits/SPIKE-universal-iso-{1,2,3,4}-2026-07-31.md`. Every criterion
> below exists because one of those spikes measured why.
## How to use it
**Run every check against the exact file that will be uploaded** — not against build inputs, not
against a sibling built the same way. A build is reproducible in theory and the uploaded bytes are
what a stranger receives.
**An absent finding is not a finding.** Each check records *what was scanned for*, so "no hits" means
"this pattern was searched and did not appear", not "nothing occurred to me".
**Any FAIL stops the publication.** Stopping is a good outcome.
---
## The criteria
Let `$ISO` be the file about to be uploaded.
### G1 — No `answer.toml`
```bash
osirrox -indev "$ISO" -find / -maxdepth 1 2>/dev/null | grep -cE "'/answer\.toml'|'/auto-installer-mode\.toml'"
```
**PASS = `0`.**
*Why:* the answer file is the only secret-bearing artefact a Felhom ISO has ever carried (Spike 1 §6,
by enumeration against the stock ISO). Removing it deletes the baked root hash, the disk profile, and
the entire Spike 12 problem space in one move, and turns the most dangerous property of the image
into a one-line assertion. Without `auto-installer-mode.toml` the stock `grub.cfg` does not emit the
Automated entry at all (Spike 1 §5), so the unsafe path is absent by construction rather than guarded.
### G2 — No root password or hash
```bash
osirrox -indev "$ISO" -extract /answer.toml - 2>/dev/null # must not exist (G1)
# and, across every file the pipeline adds:
grep -c 'root-password\|root-password-hashed\|\$6\$\|\$y\$\|\$2[aby]\$'
```
**PASS = `0` occurrences, and no `*.rootpw.txt` emitted beside the output.**
*Why:* `build-felhom-iso.sh:270-283` mints a root hash unconditionally in the answer-file modes, and
Spike 1 proved by extraction that it ships inside `/answer.toml` where any downloader can read it. On
a published image that is one credential shared by every box installed from it.
### G3 — No SSH key baked
```bash
grep -c 'root-ssh-keys\|ssh-rsa\|ssh-ed25519\|ssh-dss'
```
**PASS = `0`** across the pipeline-added files.
*Why:* `FELHOM_ROOT_SSH_KEY` is one uncommented profile line away from shipping a shared key to every
downloader, and Spike 1 §4.6 found exactly that had already happened on `demo-felhom` — from an
**uncommitted** profile. Same shared-credential class as G2.
### G4 — No customer identity
```bash
grep -c 'FELHOM_CUSTOMER_ID=[^"]\|FELHOM_RETRIEVAL_PASSPHRASE=[^"]\|claim[_-]code\|api[_-]key\|Bearer '
```
**PASS = `0` with a value.** Empty variable *declarations* inside `felhom-bootstrap.sh` are expected
and are not hits (Spike 1 §6 established this distinction — `:89` initialises them empty).
### G5 — No credential of any kind, by enumeration
Diff the file list against the stock PVE ISO and inspect **every added path**:
```bash
osirrox -indev <stock-pve.iso> -find / | sort > /tmp/stock.txt
osirrox -indev "$ISO" -find / | sort > /tmp/rel.txt
comm -13 /tmp/stock.txt /tmp/rel.txt # everything the pipeline added
```
**PASS =** every added path is accounted for and none carries a secret. Scan the content-bearing ones
for: PEM private-key headers, `ssh-rsa`/`ssh-ed25519` material, `Bearer` tokens, `api_key=`,
`passphrase=`, `password=`, `token=` with ≥12-char values, and crypt hashes (`$6$`, `$y$`, `$2[aby]$`).
*Why enumeration and not a pattern sweep:* a grep only finds what it was told to look for. Spike 1 §6
found `/answer.toml` this way precisely because the earlier recon had grepped the wrong file.
### G6 — The boot menu is present, with both paths and a human timeout
```bash
osirrox -indev "$ISO" -extract /boot/grub/grub.cfg /tmp/g.cfg
grep -c '^[[:space:]]*menuentry ' /tmp/g.cfg # >= 2
grep -E '^set (default|timeout|timeout_style)=' /tmp/g.cfg
```
**PASS =** at least one interactive entry present; `set default=` points at an **interactive** entry;
`set timeout` **≥ 10**; and `timeout_style` uses the **underscore** spelling.
*Why the timeout:* Spike 2 lost an entire probe to a **1-second** menu. A human choosing between two
install paths on unfamiliar hardware needs to read them first. *Why the spelling:* the stock PVE config
writes `timeout-style`, which GRUB does not recognise as a variable name at all (Spike 2 §5.2) —
Felhom's underscore form is the one that works.
**Also assert the safety property still holds** — no live route to a manual disk-picker via a debug or
rescue entry:
```bash
grep -v '^[[:space:]]*#' /tmp/g.cfg | grep -cE 'proxtui|proxdebug|nomodeset|Rescue Boot|memtest|fwsetup'
```
**PASS = `0`.** This is the gate `iso-repack.sh:160-164` already enforces and it is kept unchanged.
### G7 — The Felhom package is present, at a recorded version
```bash
osirrox -indev "$ISO" -find /proxmox/packages 2>/dev/null | grep -c 'felhom-'
osirrox -indev "$ISO" -extract /proxmox/packages/<pkg>.deb /tmp/p.deb
dpkg-deb -I /tmp/p.deb | grep -E '^ (Package|Version):'
```
**PASS =** exactly one `felhom-*.deb`, its version recorded in the manifest, and its sha256 matching
the package built from committed source.
*Why:* Spike 4 measured that this is the only delivery mechanism that survives an interactive install
(`[first-boot]` is never placed on the system — `Config.pm:118`, `Install.pm:746`, `:1360`).
### G8 — The postinst cannot fail, and cannot need what it hasn't got
```bash
dpkg-deb --ctrl-tarfile /tmp/p.deb | tar -xO ./postinst > /tmp/postinst
grep -cE 'systemctl (start|daemon-reload|restart)' /tmp/postinst # 0
grep -cE '\b(curl|wget|apt-get|apt|nc|ping)\b' /tmp/postinst # 0
grep -c 'set -e' /tmp/postinst # 0
tail -1 /tmp/postinst # exit 0
```
**PASS =** all four.
*Why, all four measured in Spike 4 §3:* `pid1` in the installer chroot is `unconfigured.sh` and **no
systemd is running**, so `start`/`daemon-reload` are meaningless; the network was up only because the
installer's DHCP happened to hold, so a box installed with the cable out has none; and a postinst that
exits non-zero surfaces as an error in the middle of a customer's install — far worse than the stub
not running. `set -e` is banned because it converts any unexpected non-zero into exactly that failure.
### G9 — `felhom-bootstrap.sh` matches repo HEAD byte-for-byte
Extract the script from the package and compare:
```bash
dpkg-deb --fsys-tarfile /tmp/p.deb | tar -xO ./usr/local/sbin/felhom-bootstrap.sh | sha256sum
sha256sum scripts/iso/felhom-bootstrap.sh
```
**PASS =** identical.
*Why:* it is the one genuinely frozen, drift-capable payload — everything else the box uses is fetched
at run time from `main`. An ISO in a drawer for six months still runs this exact script.
### G10 — The profile and every build input are committed
```bash
git status --porcelain scripts/iso/ # empty
git rev-parse HEAD; git rev-parse origin/main # equal
```
**PASS =** clean and pushed, and the manifest records the commit.
*Why:* Spike 1 found `demo-felhom` was installed from an **uncommitted** profile, so one of the two
reference boxes cannot be rebuilt from `main`. A published image must never reach that state, and
R-144 records a lab ISO that already has (`nested-probe`'s profile no longer exists).
### G11 — A published checksum, and a verified round trip
**PASS =** the `.sha256` and the manifest are uploaded beside the ISO, **and** the file downloaded from
`https://iso.felhom.eu/<name>` checksums to the same value. The round trip is the claim — a local
checksum proves only that the local file is what it is.
### G12 — The bucket stays private
**PASS =** the bucket's `Public Access` remains **Disabled**; the custom domain is the only public path.
---
## Result recording
Record each criterion as PASS/FAIL **with the observed value and what was scanned for**, in the
release report. A criterion with no recorded observation is a criterion that was not run.