# RUNBOOK — publish train 0.90/0.143 + golden + universal N100 ISO + DMARC quarantine (2026-07-18)

**Class:** Runbook-style operational run (NO code changed anywhere). **Executor:** CC, with two
password-gated **STOP → Viktor** points (hub UI + Cloudflare — CC cannot perform).
**Status:** **ALL PHASES COMPLETE.** CC phases 0/1/2/5 green; operator phases 3 (manifest+floor) and
4 (DMARC) done by Viktor 2026-07-18 and **verified by CC** (hub DB read + `dig` gate).

---

## Results at a glance

| Phase | What | Outcome |
|---|---|---|
| 0 | pre-flight (baselines, 404 pre-gates, serials) | ✅ all green |
| 1 | publish felhom-agent 0.90.0 | ✅ published, sha-chain identical 5 places |
| 2 | build + publish golden 0.143.0 | ✅ published, round-trip + teardown grep=0 |
| 3 | **STOP → Viktor:** manifest vouch + floor | ✅ done + CC-verified in hub DB |
| 4 | DMARC `p=none` → `p=quarantine` | ✅ done (Viktor, CF) + `dig` gate green |
| 5 | build v1.20.0 universal pairing ISOs | ✅ safety + real built, verified |

**The four operator values for the hub Day-0 manifest (Phase 3):**
```
AGENT_VERSION=0.90.0
AGENT_SHA256=18c48272228940373ba567b79e63832c292fb802abb83bf4f7b277ebed15a93c
GOLDEN_VERSION=0.143.0
GOLDEN_SHA256=ded3cfc210f6ee76e6dd125879eb508467af91d83d32a0e79d53473d5e102a2e
MIN_AGENT=0.90.0     (declared by controller v0.143.0)
FLOOR=v0.143.0       (save LAST)
```

---

## 1. Baselines used (verified 2026-07-18 against live Gitea — unchanged vs the spec)

| Repo | `main` @ | Version |
|---|---|---|
| felhom-controller | `a4a7de3d` | v0.143.0 (MinAgent 0.90.0) — image already in registry (built+deployed 07-17) |
| felhom-agent | `8208a80c` | v0.90.0 — deployed both demo agents |
| felhom.eu | `cfdcb50b` | hub v0.66.0, scripts v1.20.0 |

## Phase 0 — pre-flight ✅

- **Package pre-gates:** `felhom-agent/0.90.0` = **404**, `felhom-golden/0.143.0` = **404** (nothing to
  overwrite; immutability respected).
- **Controller image:** `gitea.dooplex.hu/admin/felhom-controller:0.143.0` running on guest 9201,
  `Up (healthy)` — pullable from registry (not rebuilt).
- **N100 disk serials (harvested pre-wipe):**
  - Internal SSD `/dev/sda` — **AirDisk 512GB SSD**, `ID_SERIAL_SHORT=QDF922W009654S30EX` — backs the
    `pve` VG (`/dev/sda3` → `pve-root`, mounted `/`) = **install target**.
  - External HDD `/dev/sdb` — **TOSHIBA MQ04ABF100**, `ID_SERIAL_SHORT=65NOP3HDT` — the secondary-backup
    drive; the install filter must **not** match it.
  - (SD card `/dev/sdd` present, irrelevant.) Serials match the prior v1.16.0 run exactly (box unchanged).

## Phase 1 — publish felhom-agent 0.90.0 ✅

Published the **live-deployed** binary (not a rebuild). **SHA_A = `18c48272228940373ba567b79e63832c292fb802abb83bf4f7b277ebed15a93c`**, byte-identical across **five** points:
felhom-pve `/usr/local/bin/felhom-agent` → local `/tmp` → build-server `/tmp` → published package →
independent **anonymous** GET. `publish-agent.sh 0.90.0`: pre-delete **404**, PUT **201**, round-trip GET
sha match. Package: `https://gitea.dooplex.hu/api/packages/admin/generic/felhom-agent/0.90.0/felhom-agent`
(anon GET **200**, 13 801 829 bytes). Registry token sourced on the build server, never echoed.

## Phase 2 — build + publish golden 0.143.0 ✅

Drill VM on 180 reverted to `virgin`, booted with the recorded qemu line (drill-vm-golden-bake memory —
`-cpu host -smp 4 -m 8192`, `if=virtio,cache=writeback`, slirp `hostfwd :2222`). Template resolved live
(rots): **`debian-13-standard_13.6-1_amd64.tar.zst`** (checksum OK). `build-golden.sh` v2.0.0 as a
transient unit baking `felhom-controller:0.143.0`:

- overlay2; **all 3 mounts included** (rootfs + mp0 `/var/lib/docker` + mp1 `/mnt/sys_drive`), no FATAL/exclusion.
- archive **612 397 450 bytes**; pre-delete **404**, PUT **201**.
- **GOLDEN_SHA256 = `ded3cfc210f6ee76e6dd125879eb508467af91d83d32a0e79d53473d5e102a2e`**.
- **Gate:** independent anon GET of `felhom-golden/0.143.0/golden.tar.zst` → **200**, 612 397 450 bytes,
  sha **matches**.
- **Teardown (GL-1):** build guest 9100 `--purge`d; in-VM token/script/log `shred`ded; VM powered off;
  qemu exited; qcow2 reverted to `virgin` (snapshot intact, exactly-as-found); staged token on 180
  shredded. **Token-leak grep of the saved log (literal value) = 0.** Evidence: `180:/mnt/5_hdd/felhom.eu/drill/bake-0.143.0.log`.

## Phase 3 — manifest vouch + floor ✅ DONE (Viktor 2026-07-18), CC-verified

Viktor saved both (Day-0 artifacts, then floor LAST). **CC verified against the live hub SQLite
`hub_settings`** (streamed read-only copy, then shredded): `artifact_agent_version=0.90.0` /
`artifact_agent_sha256=18c48272…`, `artifact_golden_version=0.143.0` /
`artifact_golden_sha256=ded3cfc2…`, `artifact_min_agent=0.90.0`, `min_controller_version=0.143.0` —
all exactly the published values. No ordering issue (both persisted).

**Fleet effect (verified from `hosts`/`reports`, not assumed):**
- `demo-felhom` — agent **0.90.0**, controller **0.143.0**, reporting 07-18 → floor **no-op**, MinAgent satisfied.
- `demo-vm-felhom` (nested) — agent **0.90.0**, controller **0.143.0** → no-op.
- **`peti-felhom` — CORRECTION to the spec's assumption:** no `hosts` row; last controller report
  **2026-07-15** (v0.115.0). He is **not currently enrolled/reporting**, so there is **no live MinAgent
  hold to observe**. The gate is correctly armed (his 0.115 < floor 0.143; a future 0.81 agent <
  MinAgent 0.90.0 would trigger the hold) but stays **latent** until his box re-enrolls in the R-1
  rehearsal. Not an error — the spec anticipated a hold with no box to act on yet.

## Phase 4 — DMARC `p=none` → `p=quarantine` ✅ DONE (Viktor 2026-07-18, Path B), gate green

- **No felhom.eu-zone Cloudflare DNS-edit token on 180** (standing posture = per-customer zone-scoped
  tokens only) → **Path B**: Viktor set the record in the Cloudflare dashboard (DNS-only).
- **Applied** `_dmarc.felhom.eu` TXT: `v=DMARC1; p=quarantine; rua=mailto:admin@felhom.eu`.
- **Gate PASSED:** `dig +short TXT _dmarc.felhom.eu @1.1.1.1` **and** `@8.8.8.8` both return
  `"v=DMARC1; p=quarantine; rua=mailto:admin@felhom.eu"`.
- **Alignment confirmed safe:** felhom.eu SPF `v=spf1 include:_spf.mx.cloudflare.net ~all`;
  `send.felhom.eu` SPF `v=spf1 include:amazonses.com ~all` (Resend/SES, DKIM-signs `d=felhom.eu`) — so
  `quarantine` does not endanger legitimate mail.
- **Still Viktor's:** the manual deliverability test-send (gmail + freemail.hu, check `dmarc=pass` in
  Authentication-Results) — the open half of R-4.

## Phase 5 — v1.20.0 universal pairing ISOs ✅

Built on 180 from `~/git/felhom.eu` @ `cfdcb50` (scripts v1.20.0), assistant `felhom-iso-assistant:trixie`.
**Input PVE ISO:** `proxmox-ve_9.2-1.iso`, sha256 `4e88fe416df9b527624a175f24c9aa07c714d3332afb1ee3dbf3879573ef2c6c`
(the exact recorded July input, at `180:/mnt/5_hdd/felhom.eu/drill/`). Both builds: pairing mode, **build manifest
`secret-bearing: no`**, loader `mkimage`, script v1.20.0.

**Build 1 — safety pre-flight ISO** (`profiles/n100.profile` AS-IS, `--pairing`):
- `180:/mnt/5_hdd/felhom.eu/felhom-iso/out/felhom-pve-9.2-1-v1.20.0-n100-generic-mkimage.iso`
- sha256 **`2e1107a15ea2abd892426661a853610dfb2ff49ec1d3e7561a876a393d4ea5c7`**, 1 704 417 280 bytes
- embedded answer.toml: `filter.ID_SERIAL_SHORT = "REPLACE-WITH-TARGET-SSD-SERIAL"` (**match-nothing** →
  installer aborts fail-safe, touches no disk — S5c). Purpose: prove the mkimage loader boots the real
  board before anything destructive.

**Build 2 — real install ISO** (uncommitted working profile `180:/mnt/5_hdd/felhom.eu/felhom-iso/n100-demo.profile`,
`--pairing`; profile mirrors the prior v1.16.0 run + the two allowed v1.20.0 deltas):
- `180:/mnt/5_hdd/felhom.eu/felhom-iso/out/felhom-pve-9.2-1-v1.20.0-n100-demo-generic-mkimage.iso`
- sha256 **`69cd7ba5872bb02c4977959bfe97312690f3a164937e71609bb987ac93e435dc`**, 1 704 417 280 bytes
- embedded answer.toml verified: `fqdn = "demo-felhom.local"`, `filter.ID_SERIAL_SHORT =
  "QDF922W009654S30EX"` (internal SSD; **≠** external HDD `65NOP3HDT` → backup drive protected),
  `root-ssh-keys = ["ssh-ed25519 …Dcz HFRDa7C kisfenyo@windows"]` (durable break-glass, baked).
- **Operator decisions (confirmed with Viktor this run):** FQDN `demo-felhom.local` (mirror prior run,
  satisfies "only deltas = version + serial"); bake the `kisfenyo@windows` ed25519 key (durable
  post-wipe access, as the prior physical run). The working profile is **not committed** (serial + key).

## Observations (recorded, NOT acted on)

- The committed `profiles/n100.profile` ships FQDN `felhom-host.local` and no baked key; the real-ISO
  profile intentionally diverges (Viktor's confirmed decisions above). No repo change made.
- PVE input kept at the recorded `9.2-1` (no newer release substituted for the rehearsal).
- `build-felhom-iso.sh` / `publish-agent.sh` exec bits are stripped by a plain `git` checkout on 180 —
  invoked via `bash`. Cosmetic, not a defect.

---

## Viktor handoff checklist

**A. Hub (Phase 3)** — ✅ DONE + CC-verified (manifest 0.90.0/0.143.0/MinAgent 0.90.0, floor v0.143.0).
**B. DNS (Phase 4)** — ✅ DONE, `dig` gate green. **Remaining: your deliverability test-send** (gmail +
freemail.hu, confirm `dmarc=pass`) — the open half of R-4.

**C. N100 bare-metal rehearsal (unblocked by this run — remaining):**
1. **Flash the safety ISO first** → boot the N100 with **Secure Boot OFF** (mkimage is unsigned;
   SB-enforcing fails `Access Denied`, proven) → expect: loader boots + installer aborts fail-safe,
   **touches no disk**.
2. Then flash the **real ISO** for the rehearsal.
3. Hub pre-steps, in order: **host delete** for the demo host (escrow ack → custody demoted, not
   destroyed) → **customer RESET** (refuses while a host row exists → delete first) → boot the ISO →
   console shows the Hungarian pairing-code banner → **bind** (prefer the self-bind flow to live-validate
   R-27 slice 1) → day-0.

Both ISOs + sha256 + manifest are on `180:/mnt/5_hdd/felhom.eu/felhom-iso/out/`. This run unblocks the R-1 supervised
rehearsal and closes the DNS half of R-4 once B is applied.
