b4edc087fa
§7.2 answer: YES. A real day-0 from the existing v1.25.0 ISO reached a claimable,
app-serving box in ~10 minutes unattended, and an app's data came back from the
drive with the guest's app.yaml gone — proven readable by the application over
its own TCP path, with a discriminator (PRE-BACKUP row = 1, POST-BACKUP row = 0).
Part 0: NO ISO rebuild needed, verified against the ISO on disk rather than from
source. It bakes only felhom-bootstrap.sh, its unit and the secret-free pairing
env (full-base64 match, 1 hit each) and 0 hits for any installer, controller or
golden marker. The installer is fetched at run time; the live URL is byte-identical
to repo HEAD (v1.22.0, six days newer than the ISO) and the fresh box ran it.
Part 1: baked 0.188.0 rather than the brief's 0.187.0 — 0.187.0 lacks D5, which
is the very claim Part 2 step 6 tests. Published (404 pre-gate with a 200 control;
anonymous download, 649310288 bytes, sha match), vouched, and consumed by a real
box. R-120's gate exercised BOTH ways: 0.185.1 refused with no write, 0.188.0
allowed — evaluated, not silently skipped.
Part 3: RUNBOOK-manual-build.md cited a "RECORDED" qemu line that is itself
labelled reconstructed and whose source says it was never saved. The real
invocation is now captured from this bake as §4.0, with the bake/publish/teardown
steps; the old entry is marked SUPERSEDED.
Teardown all three layers, hub disposition stated: VM destroyed, scratch storage
removed with space returned exactly, customer sess-g DELETED via full cascade.
sess-f deliberately left (R-131) with its command recorded.
Filed, none fixed: R-128 (false ISO_VERSION invariant comment), R-129 (demo-hp's
"no baked SSH key" is stale — key auth works), R-130 (HARD_MIN_LVM_GIB warns and
proceeds), R-131 (fourth orphaned scratch customer), R-132 (curl's %{redirect_url}
printed the hub operator password into a transcript — HUB_PW needs rotating).
403 lines
23 KiB
Markdown
403 lines
23 KiB
Markdown
# Tester gate — re-bake the golden, and prove a fresh box is safe to hand to a tester (2026-07-31)
|
|
|
|
**The sentence that matters (§7.2): YES** — a fresh install from the existing v1.25.0 ISO is safe to
|
|
hand to an external tester. Installed clean and unattended, registered, claimed, deployed two apps,
|
|
and restored an app's data with the guest's `app.yaml` gone — with the data proven readable by the
|
|
application over the path the application actually uses. Caveats in §8; none of them blocks a tester.
|
|
|
|
Golden re-baked **0.186.0 → 0.188.0** (D5), published, and vouched. No ISO was rebuilt — Part 0 showed
|
|
none was needed. Nothing already installed changed.
|
|
|
|
---
|
|
|
|
## 1. Baselines — established separately, at the start of this run
|
|
|
|
| What | Value | How |
|
|
|---|---|---|
|
|
| controller `main` | **v0.188.0** (D5) | `HEAD`=`origin/main`=`4115e88`, tree clean |
|
|
| controller on `demo-felhom` (9201) | 0.187.0 | `pct exec 9201 -- cat /etc/felhom-controller-image` |
|
|
| controller on `demo-hp` | 0.187.0 | hub `/configs` row (the box's own report) |
|
|
| **golden's baked controller, before this run** | **0.186.0** (sha `b760ac6a…`) | hub manifest; `drill/bake-0.186.0.log` |
|
|
| agent published + vouched | 0.118.1 (sha `9a74c87d…`) | hub manifest; matches `felhom-agent --version` on felhom-pve |
|
|
| hub | 0.83.0 | `deploy/hub` image tag |
|
|
| ISO | v1.25.0, built 2026-07-23 | `build-felhom-iso.sh:44`; on-disk manifest |
|
|
| `felhom.eu` HEAD | `0a9bd38`, clean | `git rev-parse` |
|
|
| managed floor | v0.156.0 (DB override) | hub `/configuration` |
|
|
|
|
The brief expected golden 0.186.0 and fleet 0.187.0 — both correct. It also assumed `main` was
|
|
0.187.0; it is **0.188.0**. See §3.
|
|
|
|
## 2. Part 0 — does the ISO need rebuilding? **NO**
|
|
|
|
**1. What the ISO contains.** Three Felhom payloads, base64-injected into the first-boot stub
|
|
(`scripts/iso/stub-first-boot.sh:22-38`, filled at `scripts/iso/build-felhom-iso.sh:324-329`):
|
|
`felhom-bootstrap.sh`, `felhom-bootstrap.service`, and the bootstrap env. Plus `answer.toml` (fqdn,
|
|
disk-setup, a per-build throwaway root hash) and the GRUB branding. **No controller image, no golden
|
|
archive, no installer copy.**
|
|
|
|
Verified against the ISO **on disk**, not inferred from source — full-base64 match, one hit each:
|
|
|
|
| Probe | Hits |
|
|
|---|---|
|
|
| base64 of repo-HEAD `felhom-bootstrap.sh` (36 692 chars) | **1** |
|
|
| base64 of repo-HEAD `felhom-bootstrap.service` | **1** |
|
|
| base64 of the reconstructed secret-free pairing env | **1** |
|
|
| `SCRIPT_VERSION="1.2` (host-install marker) | **0** |
|
|
| `felhom-controller` | **0** |
|
|
| `vzdump-lxc-9100` (golden marker) | **0** |
|
|
|
|
**2. What the bootstrap fetches at run time.** `felhom-bootstrap.sh:96` —
|
|
`INSTALL_URL="${FELHOM_INSTALL_URL:-https://felhom.eu/scripts/felhom-host-install.sh}"`, fetched on
|
|
every attempt at `:322-326`. Served from the repo's `/scripts` by git-sync
|
|
(`manifests/webpage.yaml:71-75`). Live check: 200, and **byte-identical to repo HEAD** (both sha256
|
|
`ab8b283e…`), SCRIPT_VERSION 1.22.0 — committed `f6aed82` on 2026-07-29, *six days after the ISO was
|
|
built*. **Proven live in §5: the fresh box ran v1.22.0.**
|
|
|
|
**3. How the golden reaches a fresh box.** The hub-vouched artifact manifest.
|
|
`scripts/felhom-host-install.sh:423-433` `resolve_artifacts()` GETs
|
|
`$HUB_URL/api/v1/artifacts/$CUSTOMER_ID` authed with the retrieval passphrase, reads
|
|
`golden.version` + `golden.sha256`, then fetches from Gitea and verifies the sha. Nothing baked.
|
|
|
|
**4. Version pins inside the ISO.** Four candidates; none pins the box's controller or golden:
|
|
|
|
- `ISO_VERSION="1.25.0"` (`build-felhom-iso.sh:44`) — filename/manifest tag, read by nothing at run time.
|
|
- The baked `felhom-bootstrap.sh` — **this one IS frozen**; a bootstrap change would require a new ISO.
|
|
Currently identical to repo HEAD, so no drift today. This is the single thing that could ever force
|
|
an ISO rebuild, and it is the answer to "what is baked".
|
|
- `answer.toml`'s disk-setup + throwaway root hash — per-build, not versions.
|
|
- The env's two URLs — endpoints, not versions.
|
|
|
|
**Verdict: no ISO work.** Re-baking + vouching the golden is sufficient, and that is what was done.
|
|
|
|
## 3. Part 1 — bake, publish, vouch
|
|
|
|
### 3.1 The version baked, and why not 0.187.0
|
|
|
|
Part 1 said re-baking to **0.187.0** would level R-120's gate. It would — but it would ship a golden
|
|
**without D5**, and Part 2 step 6 *is* the D5 claim, so the proof this task exists for could not have
|
|
passed. The operator page also says "rebuilt on the current controller", and current is **0.188.0**.
|
|
The brief's arithmetic predates D5 landing (2026-07-30). **Baked `felhom-controller:0.188.0`**, which
|
|
also satisfies R-120 (`configs.go:1166` refuses only `compareVersions(golden, newest) < 0`; 0.188.0 vs
|
|
the fleet's 0.187.0 is not `< 0`). Recorded here rather than absorbed silently, per standing rule 4.
|
|
|
|
### 3.2 The bake
|
|
|
|
Venue: the DooPlex golden-bake nested VM, the **accepted exception for bakes**
|
|
(`target-selection.md:59-61`). Reverted to `virgin` first, restored to `virgin` after.
|
|
`build-golden.sh v2.1.0`, template `debian-13-standard_13.6-1_amd64.tar.zst`.
|
|
|
|
Pass markers, as returned: `docker OK (overlay2; data-root /var/lib/docker)`; `including mount point`
|
|
for **rootfs, mp0 and mp1** with **no** `excluding` and **no** `FATAL`; archive 619 MB;
|
|
`upload OK (HTTP 201)`. Infra images baked = **4**, resolved from the controller itself
|
|
(`--print-infra-images`), not the drift-prone fallback list: `traefik:v3.6.7`,
|
|
`cloudflare/cloudflared:2026.6.0`, `gtstef/filebrowser:1.3.3-stable`,
|
|
`gitea.dooplex.hu/admin/felhom-samba:1.1.0`. Literal-token leak grep on the saved log: **0**.
|
|
|
|
GOLDEN_VERSION=0.188.0
|
|
GOLDEN_SHA256=7353d8beb63641f87a848e45f8aa12e465647e1190ad164a65b32ad01fc3d299
|
|
|
|
### 3.3 The three observables, as returned
|
|
|
|
**1 — published.** 404 pre-gate first, with a discriminating control so the check is not vacuous:
|
|
|
|
golden 0.188.0 pre-gate: HTTP 404
|
|
golden 0.186.0 (control): HTTP 200
|
|
|
|
Then an **anonymous** full download — what a fresh box does, no auth header:
|
|
|
|
http=200 bytes=649310288
|
|
7353d8beb63641f87a848e45f8aa12e465647e1190ad164a65b32ad01fc3d299 golden.tar.zst
|
|
|
|
Byte count and sha both identical to what the bake printed.
|
|
|
|
**2 — the manifest vouches it, read back:**
|
|
|
|
<option value="0.188.0" data-sha="7353d8be…" selected
|
|
name="golden_sha256" value="7353d8beb63641f87a848e45f8aa12e465647e1190ad164a65b32ad01fc3d299"
|
|
|
|
**3 — a consumer resolves it.** The exact call `felhom-host-install.sh:425` makes, against the
|
|
**public** hub, passphrase-authed:
|
|
|
|
GET https://hub.felhom.eu/api/v1/artifacts/sess-g → HTTP 200
|
|
{"agent":{"version":"0.118.1","sha256":"9a74c87d…"},
|
|
"golden":{"version":"0.188.0","sha256":"7353d8be…"}}
|
|
|
|
**4 (stronger, from §5) — a real fresh box consumed it:**
|
|
|
|
[INFO] manifest: agent v0.118.1 (sha 9a74c87dc97f259b…), golden v0.188.0
|
|
[INFO] fetching golden v0.188.0 from Gitea → …/vzdump-lxc-9100-2026_07_31-07_40_20.tar.zst
|
|
[OK] verified sha256 7353d8beb63641f8… matches the hub manifest
|
|
|
|
### 3.4 R-120's gate: it evaluated, and it allowed
|
|
|
|
An absent refusal is not evidence (standing rule 3), so the gate was exercised **both ways** against
|
|
the live hub before the real vouch. Vouching golden **0.185.1** (below the fleet's 0.187.0) first:
|
|
|
|
HTTP/1.1 303 → /configuration?flash=golden_behind_fleet
|
|
[WARN] artifact vouch REFUSED: golden 0.185.1 is older than the newest controller the fleet
|
|
reports (0.187.0) — a fresh install would land on stale application code (R-120)
|
|
|
|
and the manifest **still read 0.186.0** afterwards — the refusal wrote nothing. Then the real vouch:
|
|
|
|
[INFO] Artifact manifest set: agent=0.118.1 golden=0.188.0 min_agent="0.113.0" wrapper_sha=true
|
|
|
|
**Evaluated and allowed.** For the record and not for action: the gate lives in the **vouch form**
|
|
(`hub/internal/web/configs.go:1165`) and cannot fire on an image deploy, so it does not cover every
|
|
drift path. Not widened here.
|
|
|
|
## 4. Part 2 — venue
|
|
|
|
**demo-hp (t740), Tier 0, the designated drill+build host.** The brief's venue caution is accurate
|
|
about the *current* `storage.cfg` — `local` carries no `images` content type and `local-lvm` is fenced
|
|
— but `target-selection.md:38-40` names the fix: *"Put VM disks on a dir storage at `/mnt/nvme-1tb`,
|
|
at its root"*, and *"remove scratch storages when done"*. A temporary `cc-scratch` dir storage was
|
|
added at that path and removed at teardown (§7). felhom-pve was the alternative and was not chosen:
|
|
11 GB free RAM against demo-hp's 25, and it is the Tier 0 box that carries the PBS-DR/offsite tier.
|
|
Nested virt confirmed on (`kvm_amd/parameters/nested = 1`). `drill-r50` (VM 300), guest 9201 on both
|
|
boxes, `local-lvm`, and both backup targets were untouched.
|
|
|
|
VM 310: q35/OVMF with `pre-enrolled-keys=0` (the ISO is a `-mkimage` build — **unsigned loader,
|
|
Secure Boot must be off**), 8 GB, 4 cores, 150 G qcow2, `boot order=scsi0;ide2` so the empty disk
|
|
falls through to the ISO and the installed disk wins afterwards. The ISO already present on demo-hp
|
|
was verified byte-identical to the DooPlex build (`99b38420…`).
|
|
|
|
## 5. Part 2 — the proof, step by step
|
|
|
|
| # | Step | Result |
|
|
|---|---|---|
|
|
| 1 | Install from the ISO, real day-0 | **PASS** |
|
|
| 2 | Register and claim | **PASS** |
|
|
| 3 | Controller is the new one, from the box | **PASS** |
|
|
| 4 | Two apps deployed, one with a database | **PASS** |
|
|
| 5 | Discriminating data seeded | **PASS** |
|
|
| 6 | Back up, restore with `app.yaml` moved aside | **PASS** |
|
|
| 7 | Data readable by the app over its real path | **PASS** |
|
|
|
|
**Step 1 — install.** Unattended from boot to provisioned in **~10 minutes**. PVE 9.2.2 installed,
|
|
first-boot stub laid down the retry unit, the box registered itself as an unclaimed appliance
|
|
(pairing code `HTG-N8D`), was bound to customer `sess-g` on the hub, and the delivery landed on the
|
|
next poll. **The installer that actually ran: `felhom-host-install v1.22.0`** — fetched at run time,
|
|
six days newer than the ISO. That is Part 0's claim, proven on a real box.
|
|
|
|
[OK] Day-0 provision SUCCESS — vmid=9201 host_id=sess-g-e687c3 customer=sess-g
|
|
golden=local:backup/vzdump-lxc-9100-2026_07_31-07_40_20.tar.zst
|
|
|
|
Secrets scrubbed on success: `/etc/felhom/` retained only `.bootstrap-done` and the non-secret
|
|
pairing code; `bootstrap.env` and the appliance token were shredded.
|
|
|
|
**Step 2 — register and claim.** Host enrolled, wg peer registered, host-report accepted, DR-recipe
|
|
stored. The claim gate was armed from first boot and the claim was a **real** one — the code exists
|
|
only in the customer's email (`R-119`; the operator relayed it). The positive discriminator flipped:
|
|
|
|
before: {"ok":false,"error":"dashboard not yet claimed"}
|
|
after: {"ok":false,"error":"authentication required"}
|
|
hub: [INFO] [claim] customer sess-g CLAIMED its dashboard (password set by the customer)
|
|
|
|
**Step 3 — the controller, from the box, not from the manifest:**
|
|
|
|
/etc/felhom-controller-image : gitea.dooplex.hu/admin/felhom-controller:0.188.0
|
|
docker ps : gitea.dooplex.hu/admin/felhom-controller:0.188.0 | Up (healthy)
|
|
felhom-agent --version : felhom-agent 0.118.1
|
|
|
|
**Step 4 — two apps, through the real endpoints** (`GET /stacks/{app}/deploy` for the pre-generated
|
|
values, then `POST /api/stacks/{app}/deploy`, the endpoint the UI invokes):
|
|
|
|
- **rallly** — `lukevella/rallly:4.11.1` + `postgres:16-alpine`, the DB app. Chosen because its
|
|
`DB_PASSWORD` is `type: secret`, i.e. exactly D5's travelling class, and its `DATABASE_URL` is a
|
|
TCP URL to a *different container*: `postgresql://rallly@rallly-postgres:5432/rallly`.
|
|
- **homebox** — sqlite, no DB container.
|
|
|
|
Both healthy; rallly served 200 through traefik on its own route.
|
|
|
|
**Step 5 — the discriminator.** `cc_proof` table, one row **before** the backup
|
|
(`PRE-BACKUP-sess-g-2026-07-31`), one **after** (`POST-BACKUP-sess-g-MUST-NOT-RETURN`). Without this
|
|
pair a stale restore is indistinguishable from a good one.
|
|
|
|
**The read path, established and red-proofed before it was trusted.** All DB access ran from
|
|
**rallly's own network namespace** (`docker run --network container:rallly … psql -h rallly-postgres`)
|
|
— the path `DATABASE_URL` names, never `127.0.0.1` inside the postgres container, which postgres
|
|
trusts and which produced D5's §1.2 false pass. Proven to be doing real work:
|
|
|
|
correct password : rallly|t|t (user=rallly, over_tcp=t, PostgreSQL 16)
|
|
wrong password : FATAL: password authentication failed for user "rallly"
|
|
|
|
**Step 6 — backup, then restore with the guest gone.**
|
|
|
|
[backup] Recovery unit captured for rallly → /mnt/sys_drive/felhom-data/backups/primary/rallly
|
|
(images=2, secrets-referenced=2, data_keys=0, portable-carried=2/2, withheld=0)
|
|
[backup] Volume dump: rallly/rallly_rallly_postgres_data → 65.0 MB
|
|
|
|
Unit `compose/app.yaml`: mode **0600**, header naming the carried class, both secrets present.
|
|
Manifest `schema_version: 2`, `controller_version: 0.188.0`. The carried `DB_PASSWORD` was verified
|
|
**by fingerprint, never printed**, against the live one:
|
|
|
|
unit DB_PASSWORD sha256=14c8f5152b205829
|
|
live DB_PASSWORD sha256=14c8f5152b205829
|
|
|
|
and the guest's own copy was `ENC:` at rest. The guest's `app.yaml` was then **moved aside** —
|
|
confirmed absent — and the restore run through `POST /backup/restore`:
|
|
|
|
[backup] Restoring rallly from recovery unit: images=2, secrets recovered=2/2, data_keys=0
|
|
[backup] Restored 1 Docker volume(s) for rallly
|
|
[backup] Restore rallly: replaying DB dump into rallly-postgres (postgres)
|
|
[backup] Restore-from-unit completed: rallly (30.97 s)
|
|
|
|
**2/2 with the guest's app.yaml gone.**
|
|
|
|
**Step 7 — the application reading its own data.** The **live app process** was asked for its own
|
|
credential, and it matches the pre-backup one — so the secret genuinely survived the guest:
|
|
|
|
host=rallly-postgres port=5432 user=rallly pw_sha256=14c8f5152b205829
|
|
|
|
Then the read, over that path:
|
|
|
|
current_user | server_ip | over_tcp
|
|
rallly | 172.19.0.2/32 | t
|
|
|
|
id | marker
|
|
1 | PRE-BACKUP-sess-g-2026-07-31
|
|
|
|
PRE-BACKUP rows = 1
|
|
POST-BACKUP rows = 0
|
|
|
|
**The discriminator held.** 32 `public` tables intact; the guest's `app.yaml` was regenerated from the
|
|
unit with both secrets re-encrypted at rest; rallly's own healthcheck returned **200**, and traefik
|
|
served `poll.sess-g.test` **200**. Checked the data, not the exit code.
|
|
|
|
One difference from D5's adventurelog run worth recording: this unit carried **both** a `.sql` dump
|
|
and the volume tar, and the restore did both (volume restored, then dump replayed). D5's case had
|
|
`db_dumps: None`. The password mattered either way — the replay authenticates with it.
|
|
|
|
## 6. Part 3 — the runbook integrity fix
|
|
|
|
`RUNBOOK-manual-build.md:104-106` told the reader to *"use its RECORDED qemu launch line; do not
|
|
reconstruct from memory"*, citing `RUNBOOK-publish-0.79-0.110-2026-07-10.md:29` — a line labelled
|
|
**"PASS (reconstructed — DEVIATION)"** whose own §Deviations says *"the canonical `qemu-system`
|
|
one-liner was never saved"*. The document forbade improvising and pointed at an improvisation.
|
|
|
|
**Captured verbatim from this bake and recorded as canonical** in `RUNBOOK-manual-build.md` §4.0:
|
|
|
|
```bash
|
|
qemu-img snapshot -a virgin /mnt/5_hdd/felhom.eu/drill/drill.qcow2
|
|
qemu-system-x86_64 -enable-kvm -cpu host -smp 4 -m 8192 \
|
|
-drive file=/mnt/5_hdd/felhom.eu/drill/drill.qcow2,format=qcow2,if=virtio,cache=writeback \
|
|
-netdev user,id=n0,dhcpstart=10.0.2.30,hostfwd=tcp::2222-10.0.2.15:22 \
|
|
-device virtio-net-pci,netdev=n0 -display none -daemonize \
|
|
-pidfile /mnt/5_hdd/felhom.eu/drill/qemu.pid
|
|
```
|
|
|
|
SSH on `:2222` in ~40 s; `pve-manager/9.2.2`. §4 also now carries the bake/publish/teardown steps and
|
|
the template-rot warning, and the old runbook's deviation entry is marked SUPERSEDED with a forward
|
|
pointer so the reconstruction is never used as an authority again.
|
|
|
|
## 7. Teardown — all three layers
|
|
|
|
1. **The machine.** VM 310 stopped and `qm destroy --purge --destroy-unreferenced-disks 1`.
|
|
`qm list` afterwards shows only `drill-r50` (300), untouched; `/mnt/nvme-1tb/images/` is empty.
|
|
2. **The host.** `cc-scratch` removed; `storage.cfg` back to its original four entries.
|
|
`felhom-backup` available **926 492 284 KiB before the run and 926 492 284 KiB after** — the space
|
|
returned exactly.
|
|
3. **The hub. `sess-g` and its host record are DELETED — the full cascade ran.** See §7.1.
|
|
|
|
### 7.1 Layer 3, stated explicitly
|
|
|
|
The delete gate **refused twice before it allowed**, which is the gate working, and both refusals are
|
|
recorded because "I deleted it" without them would hide that this needs a wait:
|
|
|
|
POST /hosts/sess-g-e687c3/delete → 409 Conflict (host ONLINE)
|
|
POST /configs/sess-g/delete (bare confirm) → 400 (all three acknowledgements required)
|
|
|
|
`customer_delete.go:220-227` refuses while `hostStatus(...) == "ok"`, so the record cannot be deleted
|
|
until the destroyed box ages out of ONLINE (~30 min after its last report). It flipped `ok → stale` at
|
|
08:25, and the cascade then ran:
|
|
|
|
POST /configs/sess-g/delete
|
|
ack_hosts=1 ack_reset=1 ack_purge=1 confirm_id=sess-g expect_hosts=1
|
|
→ 303 /configs?flash=deleted
|
|
|
|
[INFO] customer DELETE cascade started for sess-g (journal #8, 1 host(s))
|
|
[INFO] delete sess-g: host sess-g-e687c3 deleted (escrow DEMOTED to retained custody)
|
|
[INFO] tenantsync: deprovision ok for sess-g (ns=sess-g, existed=false)
|
|
[INFO] delete sess-g: residue purged (reports=5 app_telemetry=7 notif_prefs=1
|
|
selfbind_tokens=1 appliance_registrations=1)
|
|
[INFO] customer DELETE cascade COMPLETE for sess-g (journal #8) — full teardown
|
|
|
|
**Verified positively, not by absence** — both pages loaded with real content and returned zero hits,
|
|
and the unclaimed-appliance row is gone by its own UUID:
|
|
|
|
/configs loaded 10160 bytes, occurrences of 'sess-g' = 0
|
|
/hosts loaded 9880 bytes, occurrences of 'sess-g' = 0
|
|
/hosts occurrences of appliance uuid f9f5d7e0-… = 0
|
|
|
|
**A polling trap, recorded because it nearly produced a false result here.** The first delete-gate
|
|
poll loop broke on `grep -q '"status":"ok"' || break` — and an **empty** response satisfies that just
|
|
as well as a status change. It fired on one transient empty reply and reported "GATE OPEN" while the
|
|
host was still ONLINE. Re-run parsing the JSON and treating unparseable as *retry*, the host was
|
|
`ok` for another four minutes. Standing rule 3, in miniature: the absent signal is not the positive one.
|
|
|
|
**`sess-f` is NOT deleted** — it belongs to the 2026-07-30 session, not this run (R-131). Command for
|
|
whoever picks it up, once its host is not ONLINE:
|
|
|
|
POST /configs/sess-f/delete
|
|
ack_hosts=1 ack_reset=1 ack_purge=1 confirm_id=sess-f expect_hosts=<live host count>
|
|
|
|
**Secrets.** Shredded in the guest (`rallly-dbpw`, the moved-aside `app.yaml`, deploy bodies, session
|
|
and CSRF files, claim artefacts) and on the box; helper scripts removed from both. The scratch
|
|
customer's retrieval passphrase and the ISO's throwaway root password were handled file→file and
|
|
never printed; both die with the session scratchpad and the destroyed VM.
|
|
|
|
## 8. Findings — filed, none fixed
|
|
|
|
- **R-128** — `build-felhom-iso.sh:44` claims `ISO_VERSION` "aligns with felhom-host-install
|
|
SCRIPT_VERSION". ISO 1.25.0, host-install 1.22.0. A comment asserting an invariant nothing
|
|
maintains. Cosmetic; it misleads a reader about which installer an ISO implies.
|
|
- **R-129** — the docs say demo-hp has **"no baked SSH key"** and needs the G1 break-glass password
|
|
(`CLAUDE.md`, `target-selection.md:41-42`, `felhom-agent/CLAUDE.md`). `ssh -o BatchMode=yes demo-hp`
|
|
authenticated by **key**, first try. The prohibition is stale in a direction that costs a future
|
|
session a needless vault round-trip.
|
|
- **R-130** — a fresh box's `local-lvm` was ~75 GiB against `HARD_MIN_LVM_GIB=120`, and the installer
|
|
logged `[WARN] local-lvm free ~75 GiB < hard min 120 GiB` and **continued to a fully successful
|
|
install**. Either the hard minimum is not hard, or it is wrong. A "hard min" that warns is the
|
|
R-29 shape.
|
|
- **R-131 (housekeeping)** — hub customer **`sess-f`** ("R-120 golden 0.186.0 proof", DOWN) is a
|
|
scratch customer left behind by the 2026-07-30 session — the fourth instance of the accumulation
|
|
`target-selection.md:86-87` and `PROMPT-TEMPLATE.md` §13 warn about (`drill-r50`, `sess-c`,
|
|
`sess-d`, now `sess-f`). Not deleted here: it is not this run's record. Command in §7.1.
|
|
- **R-132 — `curl -w '%{redirect_url}'` reconstructs the request URL WITH its basic-auth credential.**
|
|
While red-proofing the R-120 gate, a call made correctly with `-u ":$HUB_PW"` (no credential in any
|
|
URL) still printed the hub operator password into this session's transcript, because the *write-out
|
|
format* rebuilds the URL from the credential curl was given. The request was safe; the reporting was
|
|
not. **`HUB_PW` should be rotated.** Rule going forward: read a redirect from `-D -` and
|
|
`grep '^Location:'`, never `%{redirect_url}`, on any authenticated call.
|
|
- **R-119 confirmed live**, not re-filed: the claim code is emailed-only with no operator-side route,
|
|
and it is the single human step in an otherwise unattended day-0. Budget for it before a run.
|
|
|
|
## 9. §7.2 — the sentence that matters
|
|
|
|
**Yes — a fresh install is safe to hand to an external tester.** From ISO boot to a claimable,
|
|
app-serving box was ~10 minutes unattended; the box fetched the current installer and the newly
|
|
vouched golden and verified both by sha; two apps deployed through the real endpoints; and an app's
|
|
data came back from the drive with the guest's `app.yaml` gone, readable by the application over its
|
|
own TCP path, with a discriminator proving it was a restore and not a no-op.
|
|
|
|
**One thing the tester's host must do that the operator must plan for:** the claim code arrives only
|
|
by email (R-119), so the tester needs a working mailbox on the customer record before the box boots.
|
|
Nothing else found here blocks a tester; R-128/R-129/R-130 are documentation and gate-hygiene issues,
|
|
and R-131 is housekeeping.
|
|
|
|
**What "safe" does not mean:** bug-free. Two things this run did **not** cover — the offsite/PBS-DR
|
|
legs (deliberately: the task forbids pointing anything at production PBS or the real restic offsite,
|
|
so `sess-g` was created with DR tier off and offsite disabled), and any bare-metal ISO. Only a
|
|
`nested-vm`-profile v1.25.0 ISO exists on disk; a tester on real hardware needs a `generic`- or
|
|
`hp`-profile build from the same tooling, which is an ISO build and therefore its own task.
|
|
|
|
## 10. What did not happen, and why
|
|
|
|
- **No ISO built** — Part 0 said none was needed, and the brief says to stop and report if one were.
|
|
- **No defect fixed** — R-128/129/130/131 filed only; a fix mid-run would describe a version that
|
|
never existed.
|
|
- **No existing box changed** — no golden deployed to 9201 on either demo host, no floor moved
|
|
(still v0.156.0), no MinAgent change (still 0.113.0).
|
|
- **Campaign 10, the demo-hp repartition, subdomain onboarding** — out of scope, untouched.
|
|
- **`sess-f` not deleted** — see R-131.
|