# CONTEXT — felhom-agent working state > Snapshot of the current state + open threads. Authoritative history lives in `CHANGELOG.md` (top > entry = current); the end-of-task detail lives in `REPORT.md`. ## Current - **v0.75.0** (2026-07-08, LIVE on felhom-pve) — **GL-5 / go-live G8: guest-loss DR bring-up actually restores** (closes the v0.74.0 OPEN item + SPIKE-dr-bindmount-source §8). DR passes the COMPLETE explicit restore param set derived from the archive's embedded config (NEW `Client.ExtractArchiveConfig`, 200 under the scoped token) — **two live-discovered PVE rules: mpN params need an explicit rootfs, AND unlisted mountpoints are silently DROPPED** (first run booted without mp0/mp1!) — storage mpN passed through, structural mp8/mp9 → throwaways, then step 4d swaps the REAL binds in via the host runner (root pct; new `EngineOptions.HostRunner`+`StateDir` seam) and deletes the unusedN residue. Scratch-DR live-proven end-to-end (9310 from a real 9201 archive: mp0 200G + mp1 50G + real binds + no residue + clean teardown). Provision = nil overrides (regression-tested). NOTE: published/vouch-pending agent is 0.74.0 — publish 0.75.0 before/with the manifest bump. OBSERVATION: the DR selftest hardcodes KeepMAC=true — a scratch DR while the SOURCE guest is live briefly duplicates its MAC on the bridge (pre-existing; fine for supervised runs, worth a -keep-mac flag someday). Full customer-data DR drill = GL-6/S5 family. - **2026-07-07 — v0.74.0 Gitea-PUBLISHED (RUNBOOK GL-1)** — the LIVE felhom-pve binary's exact bytes, sha256 `1ec3f58842edce1e…76af05`, anon-fetch-verified. This supersedes/closes every standing "publish 0.6x + Day-0 vouch" OPEN item below (0.64→0.73 were never published; 0.74.0 is the vouch target). Golden 0.103.0 published in the same run (felhom.eu execution record `documentation/pilot/RUNBOOK-GL1-publish-2026-07-07.md`). **Day-0 manifest vouch = operator step** (agent 0.74.0 / golden 0.103.0). - **v0.74.0** (2026-07-07) — **campaign-2 R2 CLOSED; the mislabelled "R1" was a symptom** (LIVE on felhom-pve). Pool membership is what lets the pool-scoped token reach a guest; `pct restore --pool` sets it only at CREATE, so a restore-over-existing dropped 9201 from the `felhom` pool → no `VM.Audit` → restore-test's *existing* `bindMountOverrides` never ran → "mp8 … only possible for root". Fix: `Client.PoolAddVMID` + bring-up re-asserts membership post-restore (warn-not-fail). Role/ACL + `bindMountOverrides` untouched (both correct). **Live restore-test PASSED for the first time** once the pool was healed (Part A one-liner): read config → neutralize 2 binds → restore → boot+running → clean teardown, 4m35s. B3 (scratch-teardown 403) confirmed a cascade — no code. OPEN: DR `bring-up -mode dr` bind-override gap (spike `SPIKE-dr-bindmount-source-2026-07-07.md`: small known-constant override reusing `bindMountOverrides`; mp8/mp9 are structural constants). - **v0.73.0** (2026-07-06) — **F2 mount-role fallback CLOSED** (LIVE on felhom-pve). `roleForMountPath` gained a mount-table fallback (Impl-2b style): a bind-mounted RAW enrolled user-data drive is not a PVE storage, so it fail-safe'd to `system` and the eject/decommission gates 403'd EVERY user-data drive (campaign F2, `where=/mnt/teszt_enroll role=system`). Device-keyed classification + whole-disk containment (`storage.SameWholeDisk`); Observe-error keeps the fail-safe BEFORE the fallback. Only `roleForMountPath` touched. Live-proven full lifecycle on teszt_enroll (eject/decommission 200, no-rebind across restart, end==pre). OPEN follow-up: the `deviceRole`/`roleForMountPath` unification refactor (deferred). - **v0.72.0** (2026-07-05) — **OOB operator access (merged E1+H1)** — TASK H1, provenance both `SPIKE-{felhom-sshd,oob-wg-operator-peer}-2026-07-05`. Operator `/32` RENDERED into wg-felhom AllowedIPs (survives self-heal, [OF-1]); dedicated `internal/felhomsshd` (port claim + config render→sshd -t→reload + operator authorized_keys + heal + oob heartbeat stanza); static `inet felhom_oob` belt (agent mutates SET ELEMENTS ONLY); `configs/felhom-sshd.service` (NO RuntimeDirectory [SF-1]) + `felhom-oob.nft` + `felhom-op.sudoers`; `FELHOM_SSHD`+`FELHOM_OOB` grants; `oob.enabled` DEFAULT FALSE. Live on felhom-pve (8822, belt filled, operator SSH as felhom-op with scoped sudo); hub v0.35.0. Rollback `.bak-0.71.0`. 5 live-found bugs fixed (port path, self-listen flip-flop, nil-block lockout, reachable-via-dial, operator-configured source). - **v0.71.0** (2026-07-05) — **management-plane break-glass: privsep-dir watchdog + mgmt_plane health** — TASK G1 (prereq for felhom-sshd/H1), provenance `SPIKE-felhom-sshd-2026-07-05` §8. Host artifacts (`configs/felhom-privsep.tmpfiles` + `felhom-mgmt-watchdog.{sh,service,timer}`) make `/run/sshd` boot-persistent AND auto-heal it every ~60s **agent-independently** (heals with the agent stopped — proven live: `/run/sshd` removed → restored in 30.0s, `:22` back, no login). `internal/mgmtplane` reports the additive `mgmt_plane` heartbeat stanza; hub v0.34.1 raises `mgmt_plane_healed`. **NO unit declares `RuntimeDirectory=`** (the incident cause). H1 may now assume `/run/sshd` is guaranteed present. Live on felhom-pve; rollback `.bak-0.70.0`. - **v0.70.0** (2026-07-05) — **agent self-update (operator-signed A/B slots + crash-loop auto-rollback)** — TASK D1, provenance `SPIKE-agent-selfupdate-2026-07-05`. An operator-signed `agent_update` op (version+sha256, sha is the only integrity root) rides the signed-jobs gate; `internal/selfupdate.Executor` downloads+verifies+hands to `felhom-selfupdate-guarded apply` (root re-verify → A/B atomic flip → pending marker → detached restart); the new binary commits after a 60s dwell; a crash-looping binary is auto-reverted by `OnFailure=felhom-agent-rollback.service` (first-crash trigger [SF-1]) with the tuned `[Unit]` start-limit (120s/4) as backstop. Host artifacts + sudoers `FELHOM_SELFUPDATE` + `felhom-host-install.sh` day-0 install + report field `selfupdate_pending`. Green tests + companions. **LIVE-VALIDATED on felhom-pve (2026-07-05): all 4 drills PASS** — happy path (0.70.0→0.70.1 signed op → download+verify+flip+commit), crash-rollback (0.70.2-crash → OnFailure → **~2s crash-to-recovered**, byte-identical revert, no loop), no-pending guard, gate refusal (non-pinned key). Full agent-side pipeline ran real (envelope injected into the hub `signed_jobs` queue — CC lacks the hub global operator key; hub enqueue-auth is hub-unit-tested). Box restored to canonical **v0.70.0** (host artifacts KEPT installed; scratch operator key REMOVED — self-update dormant until an operator pins a real key, a Day-0-vouch-style follow-up). Rollback `felhom-agent.bak-0.69.0`. OPEN (v1 scope-outs): no hub-floor auto-update, no failed-update auto-retry, no pending-timeout auto-rollback; per-crash OnFailure can double-fire (idempotent — future: serialize the rollback oneshot). Detail: REPORT.md. - **v0.69.0** (2026-07-04, live on felhom-pve) — **S5: host-loss DR — safe halves shipped**. **Part 1** `wgtunnel.InstallRecoveredKey` — writes an escrow-recovered WG privkey (create-only, refuse-overwrite) so the tunnel re-establishes with the SAME identity/pubkey (same /32), no keygen; wired into `--selftest=identity-consume -install-wg-key` (opt-in; pre-S3 blob → logged fresh-keygen fallback). **Part 2** new `internal/dr` — consumes the host_loss `restore_directive` (was logged-ignored) into an inspectable RestorePlan via AddConsumer: per-guest {vmid,archive,target, sizing} + per-drive {durable_id→mount} + offsite PBS coord; DERIVE-AND-SURFACE only (Consumer has no restore/destroy dep — execute-nothing is structural). Tests + red-proofs (WG create-only; plan mode-gate). **Part 3** hub escrow-GET NOT needed (operator exports the blob via `sqlite3 writefile` on a cp'd hub.db). **Part 4-A** re-attach wrong-disk safety already unit-proven (`ResolveStorageDevice`: match resolves, absent/mismatch ERRORS, non-uuid scheme refused — never a near disk). **Part 4-B (destructive in-place 9201 restore) PREPARED + OPERATOR-GATED, NOT executed** — pre-flight green (offsite ct/9201 restorable per S4.1); the operator runs the R-consume steps + confirms the destroy (§9-4a: CC never runs a consume/R command — see [[operator-present-one-time-secrets]]). OPEN: the operator-run 4-B drill; guest_loss DR; hub-driven full-auto DR. Rollback felhom-agent.bak-0.68.0. Detail: REPORT.md + doc-06 §3.5/S5. - **v0.68.0** (2026-07-04, live on felhom-pve) — **S4.1: unattended offsite restore-test**. **Tier-aware restore-task deadline:** `RestoreTestSpec.RestoreTaskTimeout` (0→10m default) from `config.RestoreTestPBSRestoreTimeoutSeconds` (accessor default **120m**), set only when `SourceTier=="pbs"` (`main.restoreTaskTimeout`); local tier UNCHANGED. Fixes the WAN restore being killed at 10m → mid-restore teardown → leaked scratch. **Teardown "VM.Allocate" follow-up = PHANTOM (diagnosed, not blind-fixed):** ran the restore-test on the AGENT-TOKEN path sourcing the offsite (pbs) backup → `pass:true verified:boot+running`, teardown succeeded (`torn down vmid=990000`, no 403), scratch band clean. The earlier 403 was the 10m-timeout consequence (guest not yet pool-associated); the scratch is restored INTO `/pool/felhom` (ACL already grants VM.Allocate) so teardown is authorized once the restore completes. **No ACL/host-install change.** OPEN: publish 0.68.0 + Day-0 vouch; Tier-1/Tier-2 split for offsite-as-default; S5 DR consume. Rollback `felhom-agent.bak-0.67.0`. Detail: REPORT.md. - **v0.66.0 + v0.67.0** (2026-07-04, live on felhom-pve) — **S4: PBS over the tunnel**. **v0.66.0**: wgtunnel **v4-pin** (renderConf writes the resolved A LITERAL, never DNS/AAAA; `Resolver` seam, lowest addr; cached → steady-state zero-DNS/zero-exec) + **re-resolve watchdog** (`Manager.Watchdog`, loop-only; handshake stale > `stale_after_seconds`=180 → re-resolve → IP-changed re-render+restart) + FELHOM_WG **Critical** flips (conf-install/enable/restart/handshake-read). **v0.67.0**: **namespace-aware PBS client** (Config.Namespace → `Snapshots ?ns=`, `Verify ns=`; root-ns unchanged) — the operator-approved fix after Phase-1 showed the ns-unaware datastore-root 403s a per-tenant token. **Live Scenario-D (all green):** real vzdump of 9201 → **ciphertext** in ns `demo-felhom-01` over the tunnel; ns-scoped verify=ok under the box's own `felhom@pbs!demo-felhom-01` token; WARN gone; restore round-tripped (decrypt with box-born key → boot → teardown). **Confirmed tenant ACL (felhom-hetzner):** `DatastoreBackup` on `/datastore/felhom-offsite/` (NOT `/ns/`) to BOTH user `felhom@pbs` AND token (privsep=intersection; cross-ns 403); DatastoreBackup can't prune (safety). **FINDINGS:** retarget field is `local_backup_target` (not `backup_target`); retarget REVERTED to `local` (controller backs up ~every 30 min → single-target offsite = near-continuous 20-min uploads; needs Tier-1/Tier-2 split); restore-test scheduler needs a WAN restore deadline + scratch-band `VM.Allocate` before it runs offsite unattended. **OPEN:** escrow-create (OPERATOR-PRESENT, new R); publish 0.66/0.67 + Day-0 vouch; S5 DR consume. Rollback: `felhom-agent.bak-0.65.0`/`.bak-0.66.0`. Detail: REPORT.md + doc-06 §3.4/§4.2 + runbook §4a/§4b. - **v0.65.0** (2026-07-04, live on felhom-pve) — **S3.1 offsite-tunnel client MTU 1420 → 1280**: resolves `06 §4.3`'s OPEN DECISION left by the CGNAT smoke test. 1420 **silently black-holed bulk TCP** on sub-~1480 paths (mobile ~1400, DS-Lite ~1452) — handshake+ping healthy, PBS TLS page (and at S4 the backup itself) drops. New `const clientMTU = 1280` (RFC 8200 IPv6-minimum floor; outer 1340 v4 / 1360 v6 fits every realistic path), **permanent + fleet-wide + family-agnostic**. **Client-only by construction** — interface MTU caps box→PBS, advertised MSS caps PBS→box, so the endpoint's `wg0` is untouched (zero live-endpoint risk). Golden pins exact `MTU = 1280` (red-proofed vs a 1420 flip); no wire/JSON change. Live: agent re-rendered on restart (hash-gated apply), conf + live iface both 1280, PBS page loads at 1280 (no regression on wired). OPEN: true-CGNAT-SIM retest (low risk); publish 0.65.0 + Day-0 vouch (operator); S4 PBS-over-tunnel. Rollback: `felhom-agent.bak-0.64.0` on the box. The v4-pin (§4.2 determinism) is a separate, optional future note — NOT needed for MTU correctness. - **v0.64.0** (2026-07-04, live on felhom-pve) — **S3 offsite WG tunnel**: new `internal/wgtunnel` (keygen 0600/0700, marker-gated one-shot registration, agent-managed `wg-quick@wg-felhom` from the hub's desired-state `wireguard` block via the new `desired.Syncer.AddConsumer` seam, revoked-stays-revoked teardown, report stanza) + `FELHOM_WG` sudoers/capabilities + `IdentityBundle.WGPrivateKey` escrow auto-inject. **`wg_tunnel.enabled` DEFAULTS FALSE** (safety gate — rollout to Peti's box is a no-op until the production endpoint exists; enabled explicitly on felhom-pve only). Live: tunnel to ep0.felhom.eu:443 up 3 s after enable (PBS page through 10.77.0.1:8007), reboot-persistent, revocation drill clean, 30-min keepalive soak. GOTCHAS: hub envelope poll_interval_seconds (hub-side const 900 s) silently overrides agent poll_seconds on cycle 1; `wg show dump` leaks the PRIVATE key (forbidden everywhere — sudoers only grants `latest-handshakes`). OPEN: CGNAT/mobile-hotspot smoke (operator-assisted appendix); publish 0.64.0 to Gitea + Day-0 vouch (operator); S4 points PBS at the tunnel. - **configs: build-golden.sh v2.0.0** (2026-07-03, @ `ceca355`; no agent version change) — **drill findings B5 + B1 FIXED** (`DRILL-golden-098-2026-07-03.md`): the controller tag is a MANDATORY argument (the default rotted twice — a fresh install booted a pre-floor controller, forcing the guide's manual D.1b update) and the golden now bakes a `felhom-controller-bootstrap.path` unit (controller deploys the moment the back-half hot-plugs the bootstrap mount — no reboot; installer v1.9.1's reboot is a redundant belt, kept). **Golden 0.98.3** baked on the drill VM, clean-room validated (bake integrity → isolated hot-plug proof → local-golden Day-0 → published-artifact Day-0), published (sha256 b9a02ef1…fd01) + operator-vouched — Day-0 manifest now vouches **agent 0.63.0 + golden 0.98.3** (the v0.63.0 vouch follow-up below is DONE). Fresh installs land current and self-manage. NEW operator follow-up (SECURITY): the customer-config `git.token` has Gitea package-WRITE rights — scope down + rotate (evidence-doc observation O1). - **v0.63.0** (2026-07-03, live on felhom-pve + Gitea-published sha256 b4a89c81…) — **drill findings B3 + B2 FIXED** (`DRILL-day0-cleanroom-2026-07-03.md`): `TokenStore.Lookup` reloads the append-only store once on a miss (cross-process coherence with the one-shot provisioner — no more fresh-install `/controller/swap` 401 / manual restart; size short-circuit bounds the cost; behind the `TokenAuthority` seam) + `guesthook.InstallSnippet` issues a fenced `mkdir -p /var/lib/vz/snippets` first (fresh boxes lacked the dir → the self-heal hook silently never installed). Sudoers gained exactly that one grant — **ship sudoers WITH the binary** (done on felhom-pve). Red-proofed both; Scenario-E method: compiled test suite run ON felhom-pve + live channel-health hit-path. **OPERATOR FOLLOW-UP: bump the hub Day-0 manifest to agent 0.63.0** — until then fresh installs get 0.62.0 and the guide's D.1b restart-first step still applies (narrowed to "< v0.63.0" in the guide). - **v0.62.0** (2026-07-03) — **audit A1 RESOLVED**: the stale-lock reaper's scan is now pool-intersected (`staleLockController.Guests()` = `ListLXC` ∩ `Client.Pool("felhom")` members), fail-safe skip on pool-read failure; `pve:pool-read` capability (non-critical) + `--selftest` "pool read" line. Companion host-install **v1.9.0** adds `Pool.Audit` to `FelhomAgentGuest` — **deploy order on any box: rescope ACL first, then this agent.** Per `SPIKE-a1-pool-membership-read-2026-07-03.md`; red-proofed tests in stalelock_pool_test.go. - **2026-07-03 — CLAUDE.md refreshed**: version narrative removed (state lives HERE + CHANGELOG top), layout completed (all 17 internal packages + cmd/felhom-opsign); deploy runbook now in the `felhom-build-deploy` skill (`felhom.eu/skills/`). - **2026-07-03 — `REUSE.md` exists at the repo root** (canonical helpers / format-safety guards / traps / seams, code-verified); maintenance rule active: update it in the same commit that changes a shared helper. - **v0.61.0** (2026-07-03) — blast-radius audit fixes **B1 + D1 + D2 + D3** from `felhom.eu/documentation/audits/AUDIT-blast-radius-hostroot-localapi-2026-07-02.md`: random temp staging for root-installed scripts (+ sudoers/manifest glob updates), mkfs-wrapper member/RO re-checks (validated by `scripts/mkfs-guarded-harness.sh`), classifyClaim empty-lsblk fail-safe, and the blank-format anti-retarget (durable-id-bound, AGENT-001's benign-branch twin). - Deployed on demo host `felhom-pve` (node `demo-felhom`), non-root `felhom-agent` service user, pool-scoped token (`felhom` pool). ## Open threads - Deferred audit items (housekeeping/design, all INFO): C1 (controller-swap version floor), C2 (NAS server allowlist), A2 (gate journal cross-check), B2–B5, E1/E2. - Drive-enrollment leftovers: (a) `runStorageInit` slow-device detached-format polling; (b) Impl-3 shared-box operator format gate. - BUNDLE leftover: non-root agent can't read the PBS key; migration must preserve cert/key/tokens. - Not run (needs a supervised session): the destructive D1/D3 live proofs (real mkfs on a crafted member; a live /dev re-enumeration race during a real format).