# RUNBOOK GL-1 — execution record: publish agent 0.74.0 + bake/publish golden 0.103.0 (2026-07-07)

**Class:** runbook execution (go-live item G1, partial). No code changes, no version bumps.
Production touched read-only (felhom-pve: one `scp` out + `--version`/`readlink`; build server:
normal kisfenyo work + drill-VM lifecycle). All root actions inside the disposable nested drill VM.
No credential appears in this record; publish creds were sourced on the build server
(`~/.gitea-token`, user `admin`) and the in-VM copy was shredded before poweroff.

## The four operator values (hub Day-0 manifest → Configs → Day-0 artifacts)

```
AGENT_VERSION=0.74.0
AGENT_SHA256=1ec3f58842edce1e9e32d022eaef15b7ff599c658c7a36bfe7833c708076af05
GOLDEN_VERSION=0.103.0
GOLDEN_SHA256=8481e8a14e2aa0abe3831cc40e5aea4c32f2017e7561c15dca8a674df6a6026e
```

## Gate outcomes

| Gate | Result | Evidence |
|---|---|---|
| 0a baselines | **PASS** | felhom-agent `ced60ddf` (v0.74.0), felhom-controller `59eb3bea` (v0.103.0), felhom.eu `02d63ed0`; published state pre-run: agent 0.63.0 = 200 / 0.74.0 = 404, golden 0.98.3 = 200 / 0.103.0 = 404 |
| 0b drill VM | **PASS** | `~/drill/drill.qcow2` on 192.168.0.180 with internal snapshot `virgin` (2026-07-03) intact |
| 0c controller image | **PASS** | `docker manifest inspect gitea.dooplex.hu/admin/felhom-controller:0.103.0` OK on the build server |
| 0d headroom | **PASS** | 213 G free on 180 (golden ≈ 0.6 G); VM local-lvm 76 G thin, 0% used post-virgin |
| 1a running binary | **PASS** | felhom-pve `readlink /proc/$(pidof felhom-agent)/exe` → `/usr/local/bin/felhom-agent`, `--version` → 0.74.0; selfupdate state dir EMPTY (no pending A/B marker) |
| 1b agent publish | **PASS** | the LIVE bytes (sha identical across felhom-pve→CC→180 hops) published via `publish-agent.sh 0.74.0` from `main`@`ced60ddf`: pre-delete 404, PUT 201, round-trip GET sha match |
| 2a drill VM ready | **PASS** | virgin-restored + booted (slirp `dhcpstart=10.0.2.30` + hostfwd 2222→10.0.2.15:22, per DRILL precedent); PVE 9.2.2 responsive; gitea HTTP/2 200 from inside; debian-13 template ABSENT on virgin (expected) → `pveam download` inside, checksum verified |
| 2b bake + publish | **PASS** | `build-golden.sh` (main@`ced60ddf`, script v2.0.0) as root@pam in-VM, baking `felhom-controller:0.103.0`; transcript: `docker OK (overlay2; data-root /var/lib/docker)`, both `/var/lib/docker` + `/mnt/sys_drive` separate ext4 mounts, `including mount point` rootfs+mp0+mp1 (NO exclusions, no FATAL), archive 583MB, publish upload HTTP 201, `GOLDEN_VERSION=0.103.0` + sha printed. Token-leak grep over the transcript: 0 hits |
| 3a agent fetch | **PASS** | **ANONYMOUS** GET → 200, sha256 == AGENT_SHA256 (from the build server; HEAD 200 from the CC machine off-LAN too) |
| 3b golden fetch | **PASS** | **ANONYMOUS** GET → 200, 611 696 321 bytes, sha256 == GOLDEN_SHA256 (streamed to disk, hashed, deleted) |
| 3c read-token finding | **see below** | |

## Gate 3c — read-credential finding (feeds G3)

The specified reviewer read-only token (`8417…4140`) was **not locatable by CC** (not in the repos,
not on the build server under any evident name). The gates were instead run with **no credential at
all — and passed**: both generic packages are **anonymously world-readable** (deliberate posture;
the agent's self-update and guest 9201's image pulls already rely on anon Gitea reads; integrity
comes from the hub-manifest sha, not transport auth). Consequences:

- A read-scoped token is trivially sufficient for artifact fetch — **rotating the customer
  `git.token` to read-only (or revoking its package-write) CANNOT break fresh installs' fetch
  path.** The G3 rotation is unblocked from this side.
- Nuance: the installer (`resolve_git_creds`) **dies on an empty git token** even though the fetch
  itself would succeed anonymously — the credential is required by the script, not by Gitea. If the
  operator ever wants credential-less artifact fetch, that is a small host-install follow-up; until
  then customer configs still need a (read-scoped) token present.

## Drill-VM end state

Build guest 9100 destroyed (`--purge`, all 3 LVs removed); in-VM cred file shredded; VM powered
off; **qcow2 restored to `virgin`** (snapshot list verified) — the drill environment is exactly as
found. Evidence retained on the build server: `~/drill/bake-0.103.0.log` (next to the prior
`bake-A.log`).

## Deviations from the letter of the runbook

- Gate 2a's template check was satisfied by downloading the template inside the VM (the virgin
  image never has it — same as the 0.98.3 bake) — anticipated by the runbook, noted as required.
- Phase 3 ran anonymously instead of with the reviewer token (see Gate 3c) — a strictly *stronger*
  proof of fetchability.
- The bake was launched as a transient systemd unit (`systemd-run --unit=golden-bake`) inside the
  VM instead of bare `nohup` (a plain ssh-backgrounded nohup did not survive session close).

## Follow-ups (operator)

1. **Hub Day-0 manifest bump** (Configs → Day-0 artifacts): enter the four values above. Until
   then fresh installs still fetch agent 0.63.0 + golden 0.98.3.
2. **Operator-key pin** (out of scope here): day-0 installs no operator pubkey, so agent
   self-update stays dormant on fresh boxes; (a) pin the real key on felhom-pve, (b) small
   host-install follow-up ships the pinned pubkey at day-0 — candidate to fold into GL-4.
3. **G3 git.token rotation**: unblocked per Gate 3c — issue read-scoped tokens (or rely on anon +
   keep a placeholder until the installer follow-up); also fixes demo-felhom's currently-EMPTY
   git credentials (found in GL-2; blocks any step-5 (re)install for that customer).
4. GL-6 supervised drill consumes the bumped manifest end-to-end (Scenario-D-style proof for
   0.74.0/0.103.0 — deliberately NOT run here).
