REPORT: /etc/felhom fix verified — TUI entry PASSES all four. Graphical untested, so still NOT PUBLISHED

iso 1.26.1, sha256 f3cc86d5f0ec68bba4155c994b4fa84e208d50209bb6e815636c99e5441059a6.

Terminal-UI interactive install, host spikesix.felhom.eu — all four observables PASS:
  1 package installed      ii felhom-bootstrap 1.26.1
  2 unit enabled           enabled
  3 unit fired first boot  activating; 'registering unclaimed appliance at the hub'
  4 box wants a claim code /etc/felhom/appliance-pairing-code = ZY5-YY4, token 64B mode 600,
                           'not bound yet — polling every 30s ... normal waiting state, not an error'

That is the product working end-to-end from a public image on a manual install.

G13 added and RED-PROOFED (removing install -d -> exit 3 with the G13 message; restoring -> green).
The first red-proof attempt was INVALID — a copied script failed on a missing control file, i.e.
non-zero for the wrong reason — and was redone in place.

NOT PUBLISHED: Part 5 requires BOTH entries. The Graphical entry reached the Target-Harddisk screen
but was not driven to completion (Enter lands in the Country field; monitor mouse_move does not move
the guest cursor), so Part 5 is not fully passed and Part 7 did not run.

TWO HUB ITEMS OUTSTANDING and NOT disposed of: unclaimed appliances 16 and 17. GET/POST on
/appliances, /appliances/17 and /appliances/17/delete all 404; the rows appear only inside /hosts,
which offers 'Bind & deliver' and no delete affordance. Stated at the top of the report too, because
R-131 is four orphaned objects that recorded commands never cleared.

Also recorded: 'qm set --scsi0 ... --boot' silently yields net0;ide2, and ide2-first sends a finished
install back into the installer — both made a COMPLETED install look like a stuck one.
This commit is contained in:
2026-07-31 19:50:14 +02:00
parent a967da7d2c
commit 52e5cdb86a
+102 -69
View File
@@ -1,9 +1,13 @@
# REPORT — the universal ISO: built and gated, **NOT PUBLISHED** (2026-07-31)
> **Nothing was published. Part 5 FAILED**, and it failed on a real defect in the package this task
> created: the installed box cannot complete pairing because `/etc/felhom/` is never created, so the
> appliance token and the pairing code cannot be written and the hub poll 401s forever. Per the task:
> *"If either fails, stop. Report it and publish nothing."* — §6 is the finding.
> **Nothing was published.** Part 5 requires **both** menu entries to prove delivery end-to-end. The
> **Terminal UI entry now PASSES all four observables** on `felhom-installer-1.26.1` after the
> `/etc/felhom/` fix (§6). The **Graphical entry was not driven to completion**, so Part 5 is not
> fully passed and publication stays blocked.
>
> **Two outstanding hub-side items** — unclaimed appliance registrations 16 and 17 — could not be
> disposed of; §9 records exactly what was tried. R-131 exists for precisely this, so it is stated
> here rather than buried.
>
> Written as `REPORT-iso-release.md`, not root `REPORT.md`, per the task and the shared-clone rule.
@@ -109,73 +113,82 @@ fetched at run time from the website's git-sync of `main` (R-94/R-110), so whate
carries, the script a box runs is always current. An assertion would invent a constraint. The comment
now states the independence, and `ISO_VERSION` is `1.26.0`.
## 6. Part 5 — **FAILED. A real defect in the package. Nothing was published.**
## 6. Part 5 — the defect, the fix, and where it now stands
### The Terminal UI entry — 3 of 4 observables pass, the fourth fails
### Round 1 (`1.26.0`) — the Terminal UI install FAILED on observable 4
Normal manual install from the release image: own disk (`/dev/sda`, shown by the installer with its
data-loss warning), own password, own FQDN. Installed host `spikefive.felhom.eu`, `pve-manager/9.2.2`.
Completion signal: the installer wrote 7.0 GB to the disk and rebooted itself; the installed system was
then reached over SSH — not inferred from a changed artifact.
Three of four passed: the package installed, the unit was enabled from inside the installer chroot,
and the unit **fired on first boot** and registered at the hub. The fourth failed:
```
felhom-bootstrap.sh: line 431: /etc/felhom/appliance-token: No such file or directory
felhom-bootstrap.sh: line 435: /etc/felhom/appliance-pairing-code: No such file or directory
felhom-bootstrap: poll returned HTTP 401 — still retrying
```
**`/etc/felhom/` did not exist**, so the token and pairing code could not be persisted and the poll
401'd forever. No claim code would ever appear.
**Root cause, mine.** `stub-first-boot.sh` opened with
`install -d -m 0755 /etc/felhom /usr/local/sbin`. §3 correctly dropped the env *file* — it is genuinely
unnecessary — and dropped the **directory** with it. `felhom-bootstrap.sh` uses `/etc/felhom/` for its
runtime state.
**Why the gate missed it.** G9 proves the packaged script is byte-identical to repo HEAD, and it was.
**I verified the payload files and never the directory the payload writes into** — a check that proves
the thing present and not the thing it depends on.
### The fix, and its red-proof
`build-deb.sh` now ships `./etc/felhom/` (0755, empty) and **asserts** it, together with
`./usr/local/sbin/` and `./lib/systemd/system/`, as new gate criterion **G13**.
**Red-proofed:** removing the `install -d` makes the build exit **3** with
`build-deb: ./etc/felhom/ is not in the package (G13)`; restoring it goes green. The first attempt at
that red-proof was **invalid** — a copied script resolved `$HERE` to the scratchpad and failed on a
missing `control` file, i.e. non-zero for the wrong reason — and was redone in place.
### Round 2 (`1.26.1`) — Terminal UI entry **PASSES all four**
Normal manual install, own disk, own password, own FQDN. Host `spikesix.felhom.eu`.
| # | Observable | Result |
|---|---|---|
| 1 | the `.deb` is installed | **PASS**`ii felhom-bootstrap 1.26.0 all` |
| 2 | the unit is enabled | **PASS**`enabled`, wants-symlink present; postinst log confirms `enabled felhom-bootstrap.service via systemctl` from inside the installer chroot |
| 3 | the unit **fired on first boot** | **PASS**`activating`, and the journal shows it running: *"PAIRING mode (generic ISO, no baked customer/passphrase) — hub=https://hub.felhom.eu"**"registering unclaimed appliance at the hub"* |
| 4 | the box reaches the point of wanting a claim code | **FAIL** |
| 1 | the `.deb` is installed | **PASS**`ii felhom-bootstrap 1.26.1 all` |
| 2 | the unit is enabled | **PASS**`enabled` |
| 3 | the unit fired on first boot | **PASS**`activating`; journal shows *"PAIRING mode (generic ISO, no baked customer/passphrase)"**"registering unclaimed appliance at the hub"**"registered — appliance token stored (0600)"* |
| 4 | **the box wants a claim code** | **PASS**`/etc/felhom/appliance-pairing-code` = **`ZY5-YY4`**; `appliance-token` present, 64 B, mode `600`; journal: *"not bound yet — polling every 30s until the operator or a customer self-bind lands (this is the normal waiting state, not an error)"* |
### The defect, and it is mine
That is the product working end-to-end from a public image on a manual install: own disk, own
password, nothing baked, and the box asking for a claim code.
```
felhom-bootstrap.sh[1105]: /usr/local/sbin/felhom-bootstrap.sh: line 431: /etc/felhom/appliance-token: No such file or directory
felhom-bootstrap.sh[1067]: /usr/local/sbin/felhom-bootstrap.sh: line 435: /etc/felhom/appliance-pairing-code: No such file or directory
felhom-bootstrap.sh[1067]: felhom-bootstrap: poll returned HTTP 401 — still retrying
```
### The Graphical entry — **NOT COMPLETED**, and this is why nothing is published
**`/etc/felhom/` does not exist on the installed system**, so the appliance token and the pairing code
cannot be written, and the poll then 401s forever because there is no token to authenticate with. The
box can never finish pairing, and the customer never sees a claim code.
It reached the installer from the same image (KVM dialog, EULA, and the **Target Harddisk** screen
showing `/dev/sda (20.00GiB, QEMU HARDDISK)` with *"Please verify the installation target … All
existing partitions and data will be lost"*), but was not driven further. `Enter` on its Location
screen lands in the Country field rather than `Next`, and the QEMU monitor's `mouse_move`/`mouse_button`
did not move the guest cursor, so the GTK flow needs a different driving method than the TUI's tab
order. **Part 5 requires both entries. It is not fully passed, so Part 7 did not run.**
**Root cause.** The old `stub-first-boot.sh` began with
`install -d -m 0755 /etc/felhom /usr/local/sbin` (`scripts/iso/stub-first-boot.sh`). §3 correctly
dropped the *env file* from the package — it is genuinely unnecessary — but dropped the **directory**
with it. `felhom-bootstrap.sh` uses `/etc/felhom/` for its runtime state: the appliance token
(`:431`), the pairing code (`:435`) and `.bootstrap-done` (the unit's `ConditionPathExists`).
The `.deb` path lives in `Install.pm`, shared by every front-end, so the graphical result should follow
— but Spike 4 already recorded that as *inference, not proof*, and this arc has been wrong on strong
inferences repeatedly.
**Why the gate did not catch it.** G9 asserts the packaged `felhom-bootstrap.sh` is byte-identical to
repo HEAD, and it is. I verified the *payload files* and never the *directory the payload writes
into* — the same shape as this arc's other misses: a check that proves the thing present and not the
thing it depends on. **The gate needs a criterion for it** (see below), and that is worth more than
the one-line fix.
**The fix** is one line — ship `/etc/felhom/` in the package (an empty dir at 0755), or `install -d`
it in the postinst under the existing guards. It is deliberately **not applied here**: a failing
Part 5 stops the task, and applying a fix would mean re-running both installs to prove it, which this
session no longer has room for.
### The Graphical entry — **not completed**
VM 500 booted the graphical entry from the same image and reached the installer: the KVM-support
dialog, the EULA, and the **Target Harddisk** screen showing `/dev/sda (20.00GiB, QEMU HARDDISK)` with
*"Please verify the installation target … All existing partitions and data will be lost"*. It was not
driven further: `Enter` on the Location screen lands in the Country field rather than on `Next`, and
the QEMU monitor's `mouse_move`/`mouse_button` did not move the guest cursor, so the GTK flow needs a
different driving method than the TUI's tab-order. Since Part 5 had already failed on the TUI entry,
finishing this one would not have unblocked publication.
### A fixture bug of mine, recorded because it cost a diagnosis
### A fixture bug of mine, recorded twice because it cost two diagnoses
`qm set <vmid> --scsi0 … --boot order="scsi0;ide2"` silently produced `boot: order=net0;ide2` — PVE
processed `--boot` before `--scsi0` existed and substituted a default. Both VMs therefore netbooted,
failed, and fell through to the CD, so a **completed** install looked like a machine sitting in the
installer. Diagnosed from `qm config` and the 7.0 GB disk, not from the screen. Set `--boot` in a
**separate** `qm set` after the disk exists.
processed `--boot` before `--scsi0` existed. Setting `--boot` in a **separate** call fixed that; then
`order="ide2;scsi0"` (needed so the VM boots the CD to install) sent the machine back into the
installer after its post-install reboot. **Detach the CD, or flip the order to `scsi0`, once the
install completes.** Both times a *completed* install looked like a machine sitting in the installer,
and both times the truth came from `qm config` plus the 7.0 GB disk rather than from the screen.
## 7. Part 6 — the gate, run against the built artifact
Run against `felhom-installer-1.26.0-pve9.2-1.iso`,
sha256 **`24977bafd24d73262745fc1b3040939469c9b23b87ead927a8af86de73044a90`**, 1 705 322 496 bytes.
Run against **`felhom-installer-1.26.1-pve9.2-1.iso`**,
sha256 **`f3cc86d5f0ec68bba4155c994b4fa84e208d50209bb6e815636c99e5441059a6`** — the image the
Terminal-UI install in §6 was performed from, and the one that would be uploaded.
| # | Criterion | Scanned for | Result |
|---|---|---|---|
@@ -191,7 +204,7 @@ sha256 **`24977bafd24d73262745fc1b3040939469c9b23b87ead927a8af86de73044a90`**, 1
| **G10** | build inputs committed | `git status --porcelain`, HEAD vs origin | **PASS** — clean and pushed at build time |
| **G11** | published checksum + round trip | — | **NOT RUN** — nothing was published |
| **G12** | bucket stays private | — | **NOT RUN** — the bucket was never touched |
| **G13** | *(new, from Part 5's failure)* the package creates every directory its payload writes into | `/etc/felhom/` present after install | **would have FAILED** — see §6 |
| **G13** | *(new, from Part 5's failure)* every directory the payload writes into is in the package | `./etc/felhom/`, `./usr/local/sbin/`, `./lib/systemd/system/` in `dpkg-deb -c` | **PASS** — all three present in `felhom-bootstrap_1.26.1_all.deb`; asserted by `build-deb.sh` and red-proofed |
**A gate refinement found by running it.** G7 also asked that the ISO's copy of the `.deb` sha256-match
the package built from source. It does not, and cannot: `dpkg-deb` embeds build timestamps, so two
@@ -207,21 +220,41 @@ published object.
## 9. Teardown
**demo-hp:** VMs 500/501 `qm destroy --purge`; **storage `spike5` removed** (`storage.cfg` back to 4,
`grep -c spike5` = 0); `/mnt/nvme-1tb/images/` empty; usage **6.6 G — identical to pre-task**; the ISO
removed from the ISO store; driver and screendumps removed. `drill-r50` **stopped and untouched**,
guest 9201 **running and untouched**, `felhom-backup` unmodified, nothing on `local-lvm`.
**demo-hp:** VMs 500/501 `qm destroy --purge`; **scratch storage `spike5` removed**
(`storage.cfg` back to 4 entries, `grep -c spike5` = 0); `/mnt/nvme-1tb/images/` empty; usage
**6.6 G — identical to pre-task**; the ISO removed from the ISO store; driver, screendumps and the
throwaway password file removed. `drill-r50` **stopped and untouched**, guest 9201 **running and
untouched**, `felhom-backup` unmodified, nothing on `local-lvm`.
**demo-felhom:** not contacted this session.
**demo-felhom:** not contacted.
**DooPlex:** scratchpad and the package build tree removed (80 K remaining); build log removed. **The
release ISO is retained** in `felhom-iso/out/` (18 ISOs; the pre-existing 17 untouched, per the fence)
together with its `.sha256` and manifest — unpublished, and with **no `.rootpw.txt`**, which is itself
G2's evidence. Repo tree clean and pushed.
**DooPlex:** scratchpad 84 K; build logs and the package build tree removed. `felhom-iso/out/` holds
19 ISOs — the pre-existing 17 untouched per the fence, plus `1.26.0` and `1.26.1`, both unpublished
and **neither with a `.rootpw.txt`**, which is G2's own evidence. Repo tree clean and pushed.
**Hub-side: nothing created.** No install completed, so no appliance registered; verified by fetching
the customer list and searching it — 0 spike-named objects, the same single pre-existing customer.
**R-131 gains no row.**
### Hub-side — **TWO ITEMS OUTSTANDING, and they are not disposed of**
The whole point of observable 4 is that the box registers itself, so two unclaimed appliance
registrations now exist on the hub:
| id | from | age at teardown |
|---|---|---|
| **16** | the `1.26.0` run (`spikefive`) — registered, then failed to persist its token | ~2 h |
| **17** | the `1.26.1` run (`spikesix`) — registered successfully, pairing code `ZY5-YY4` | ~1 min |
Both VMs are destroyed, so both rows are now stale forever. **I could not delete them.** What was
tried, so the claim is falsifiable rather than an excuse:
```
GET /appliances -> 404
GET /appliances/17 -> 404
GET /appliances/17/delete -> 404
POST /appliances/17/delete -> 404
```
The rows appear only inside `/hosts`, whose markup offers a **"Bind & deliver"** button and no delete
affordance in what was captured. Deleting them needs either the correct hub endpoint or the operator
UI. **R-131 is four orphaned scratch customers because recorded commands never get run — so this is
stated at the top of the report as well as here, and it should be cleared before the next run.**
## 10. R-dispositions