SPIKE 4: a .deb in the ISO DOES deliver on an interactive install

Findings only — no script, profile or build file changed; no release ISO built, nothing published.
documentation/audits/SPIKE-universal-iso-4-2026-07-31.md

MEASURED, with a control, and the negative control is in the SAME box. One ISO (15 GRUB entries),
a trivial probe .deb injected into /proxmox/packages/, two qm-created VMs on demo-hp (400
interactive / 401 automated control) on a scratch dir storage at the /mnt/nvme-1tb mount ROOT.

Interactive (Terminal UI) install:
- package installed (ii felhom-spike4-probe 0.0.1)
- postinst RAN (marker + content intact)
- it enabled a systemd unit, and that unit FIRED ON FIRST BOOT (uptime 7.98s, pid1=systemd)
- while on the same machine proxmox-first-boot is NOT installed and /var/lib/proxmox-first-boot
  does not exist — Spike 3's negative reproduced, not assumed.

Postinst environment (identical both paths): pid1=unconfigured.sh, NO running systemd, but
'systemctl enable' SUCCEEDS; /proc+/sys mounted; network+DNS happened to be up (inherited from the
installer's DHCP — must NOT be relied on). Constraints: never systemctl start/daemon-reload, never
require network, never fail, do the real work in the unit at first boot.

Repack preserves it, but a naive 'xorriso -boot_image any replay' fails with 'Overlapping MBR
partition entries' — iso-repack.sh:270-292 already documents that exact failure and its fix.

R-153 RETRACTED into R-94 leg (b): OPEN-ITEMS.md:15 carries it verbatim at READY (XS), and R-29
says explicitly 'do not mint a new ID for a new instance'. Spike 3's further claim that the drift
leaves the generator 'three minor versions stale' was FALSE and is corrected — R-94 retracts that
exact reading; the served script is always main, so 1.22.0 is what every install already gets.

No new R-rows opened.
This commit is contained in:
2026-07-31 15:35:07 +02:00
parent bb29186d62
commit 61e9b55737
3 changed files with 411 additions and 61 deletions
+70 -61
View File
@@ -1,88 +1,97 @@
# REPORT — SPIKE 3: on an interactive install, does the ISO deliver anything at all? (2026-07-31)
# REPORT — SPIKE 4: can a `.deb` in the ISO deliver the stub on an interactive install? (2026-07-31)
> Written as `REPORT-universal-iso-spike.md`, not `REPORT.md`: the shared file belongs to today's hub
> v0.85.0 session and the second session in a shared clone never touches it. Supersedes this file's
> Spike 1 and Spike 2 contents.
> Spike 13 contents.
**Class: Spike.** Findings only — no production file changed, no release ISO built, nothing published.
Evidence: `documentation/audits/SPIKE-universal-iso-3-2026-07-31.md`.
Evidence: `documentation/audits/SPIKE-universal-iso-4-2026-07-31.md`.
## The answer: no, and more completely than expected
## The answer is yes, and it was measured with the negative control in the same box
**`[first-boot]` does not fire on an interactive install.** Measured, with a control from the *same*
image (one ISO, 15 GRUB entries):
One ISO, 15 GRUB entries, a trivial probe `.deb` injected into `/proxmox/packages/`. Two VMs on
demo-hp built with `qm` so the run was visible in the web console: **400 interactive**, **401
automated control**.
- **Automated entry** → hook fires: `ttyS0` marker, marker file,
`/var/lib/proxmox-first-boot/proxmox-first-boot` (0700), activation symlink, unit `active`.
- **Terminal UI entry, normal manual install** → every one of those **absent**, and the
`proxmox-first-boot` **package is not installed at all**. A whole-filesystem grep for the marker
returns nothing.
On the **interactive** install (`spikefour.felhom.eu`):
It is not that the hook fails to run — nothing that could run it is ever placed on the system. The
mechanism, cited: `Config.pm:118` defaults `first_boot.enabled => 0` and `set_first_boot_opt` is never
called anywhere in the Perl tree; `Install.pm:746` returns early without the flag; `Install.pm:1360`
skips the package. And `proxinstall`, the graphical installer, contains **zero** occurrences of
`first-boot`. **This is a property of the PVE installer, not a Felhom defect** — but nothing in our
tree records it, which is R-154.
- the package is installed — `ii felhom-spike4-probe 0.0.1`
- its **postinst ran** — marker file present, content intact
- it **enabled a systemd unit**, and **that unit fired on first boot** (uptime 7.98 s, `pid1: systemd`)
- and on **that same machine**, `proxmox-first-boot` is not installed and `/var/lib/proxmox-first-boot`
does not exist — Spike 3's negative reproduced, not assumed
## The commercially relevant part
So the two delivery mechanisms are independent, and the one that survives the path we are actually
shipping is the `.deb`. **The product — insert the stick, install Proxmox normally choosing your own
disk and password, box sets itself up and waits for a claim code — is now measured rather than hoped
for.** With one honest caveat: what was measured is a trivial probe package, not Felhom's real stub.
Packaging the real stub and confirming pairing end-to-end is the last step before a spec (~60 min).
**A delivery mechanism does exist.** `Install.pm:1343-1372` unpacks **every** `.deb` in the ISO's
`/proxmox/packages/` into the target on *every* install path — a fixed skip-list of known package
names, everything else installed — then `dpkg --configure -a` runs postinsts. That is exactly how PVE
ships `proxmox-first-boot` itself.
## What a postinst may and may not do
**It is read from source, not measured**, and I have not proven either that our repack preserves an
added `.deb` or that a postinst running in the installer chroot can do the job (it runs *inside the
installer*, not on a booted system). Proving it is the top follow-up, ~45 min. Until then the honest
position is: the *insert-stick-install-type-claim-code* experience is **plausible but unvalidated**,
and the documented one-command manual path is what works today.
Identical on both paths: `pid1 = unconfigured.sh`, **no running systemd**, `/proc` and `/sys` mounted,
and **`systemctl enable` succeeds** (it wrote the symlink). Network and DNS *happened* to be up —
inherited from the installer's own DHCP.
Incidentally confirmed: the interactive installer shows the target disk and a summary listing
`Bootdisk(s) /dev/sda` before erasing. Your ruling needs no Felhom work for that warning.
Four constraints for the real postinst, so they get written against rather than discovered:
## The manual path is already most of the way there
1. Never `systemctl start` or `daemon-reload` — there is no systemd running. `enable` is the only verb.
2. **Never require the network**, despite it being present here. A box installed with the cable out
gives a postinst no route, and a failing postinst breaks the customer's install.
3. Never fail — guard everything, `exit 0`.
4. Do the real work in the unit at first boot, where systemd, network and a booted kernel exist.
Two of the three things I was asked to look for already exist:
## Two smaller results
- **The hub already renders the command with the customer-id filled in**
(`customer_unified.html:563-564`, plus a JS generator that adjusts the flags per role).
- **The URL is already short, public and always current** —
`felhom.eu/scripts/felhom-host-install.sh`, git-synced from `main` on a 30-second period.
- **Console display does not exist.** Nothing writes `/etc/issue`; PVE's own banner is what appears
after install. That file is the obvious carrier. Reported, not built.
**The repack preserves the `.deb`, but not naively.** `xorriso … -boot_image any replay` fails with
*"Overlapping MBR partition entries"* — and `iso-repack.sh:270-292` already documents that exact
failure and its fix. Mirroring it produced a working image (19 El Torito entries; the `.deb` extracted
back out is byte-identical). So this is an insertion into an extract→modify→re-master cycle our repack
already performs, not a new build stage.
## Two things I'd flag
**Q3:** `iso-repack.sh:100-106` refuses an ISO without `auto-installer-mode.toml`. It is a guard, not
a structural requirement, and its reasoning is sound for the shape it was written for — already R-155,
cited exactly here. With no mode file the stock grub.cfg does not emit the Automated entry at all; with
a mode file but no answer, that entry aborts safely and loudly.
**A working gate is red and nobody runs it.** `configs.go:28` has `hostInstallVersion = "1.19.0"`
against `SCRIPT_VERSION = "1.22.0"`, and `scripts/hostinstall_gates.py` detects it and exits 1. The
comment saying drift "is now gated" is true of the gate's existence and false of its enforcement, so
the customer page's install-command generator targets a flag surface three minor versions stale.
**R-153.**
## A correction I owe you from last session
**The cleanest public-image posture is currently blocked by our own tooling.** §6 of the findings
argues the public image should carry **no `answer.toml` at all** — which removes the baked root hash,
the disk profile, and every question Spikes 12 wrestled with, and makes "no `answer.toml`" a
one-line release gate. But `iso-repack.sh:100-106` refuses any ISO lacking `auto-installer-mode.toml`.
That guard is sound for its original case and wrong for this one. **R-155.**
**R-153 is retracted.** The register grep this task mandated shows R-94 already carries it verbatim at
`OPEN-ITEMS.md:15`, status `READY (XS)`, with leg (b) being precisely "the gate fails today and is
invoked by nothing" — and R-29, the class, says in terms *"do not mint a new ID for a new instance."*
Spike 3 filed a duplicate.
## Not reached
**And the substantive half of that Spike 3 claim was wrong.** I wrote that the drift left the customer
page's install-command generator "targeting a flag surface three minor versions stale." R-94 explicitly
retracts exactly that reading: the constant selects no script — it renders as a text label, and the
command beneath it fetches the script the website git-syncs from `main`, so **1.22.0 is what every
install already gets**. It is a wrong number on your screen and nothing more. I overstated it.
**Q3 — the real stub at `before-network`** was not run; driving the interactive install consumed the
session. It is recorded as not reached, not inferred. Spike 2's structural case stands unrefuted, and
§2 narrows its scope: with no first-boot hook on the interactive path, that question now governs
**operator-built images only**. ~40 min when wanted.
## Still unknown
**The real stub has not been packaged** — that is the one thing between here and a build spec.
Also unproven: `dpkg --configure -a` ordering for a package with dependencies; an ISO that never went
through `prepare-iso` (blocked by R-155, which this spike was fenced from changing); and the Graphical
installer, where the result should hold *a fortiori* since the `.deb` path is in `Install.pm`, shared
by all front-ends — but that is inference, not measurement.
**Spike 3's Q3** — the real stub at `before-network`**this session did not touch it.** Note it is
now partly superseded: on the `.deb` route the unit's ordering comes from the unit file, not from
`[first-boot].ordering`.
## R-rows
**R-153** (a working gate is red and unrun) · **R-154** (`[first-boot]` is automated-only and nothing
in our tree says so) · **R-155** (`iso-repack.sh` blocks the no-`answer.toml` posture).
**None opened.** Each candidate was grepped against the register first: the delivery result is a
positive finding, the postinst constraints belong in the build spec, and the repack guard is already
R-155. **R-153 retracted** into R-94 leg (b) / R-29.
## Teardown
All three layers verified positively. demo-hp back to its exact pre-spike state — `spike3/` removed,
0 QEMU processes, 0 loop devices, **disk usage 6.6 G, identical to pre-spike**, 4 storages unchanged,
nothing on `local-lvm`, `drill-r50` stopped and 9201 running. demo-felhom: nothing placed, external
drive observed only. DooPlex: scratchpad 4.4 GB → 80 K, both throwaway passwords destroyed,
`felhom-iso/out/` untouched at 17 ISOs, no production file modified. **Hub-side: nothing created**
verified by fetching and searching the customer list, not asserted. Nothing published.
All three layers plus the scratch storage, verified positively. demo-hp: VMs 400/401 purged, **storage
`spike4` removed** (`storage.cfg` back to 4, `grep -c spike4` = 0), `/mnt/nvme-1tb/images/` empty,
**disk usage 6.6 G — identical to pre-spike**, probe ISO and driver removed, 0 loop devices,
`drill-r50` stopped and untouched, 9201 running, `felhom-backup` unmodified, nothing on `local-lvm`.
DooPlex: workspace scratch **4.8 GB removed**, scratchpad **3.3 GB → 88 K**, both throwaway passwords
destroyed, 17 ISOs in `out/` untouched, no production file modified. **Hub-side: nothing created**
the VMs took LAN DHCP leases but never ran `felhom-host-install.sh` or contacted the hub; verified by
fetching and searching the customer list. Nothing published.