ten answers: golden 0.300.0 baked (pinned), delivery evidence; R-645 R-856 R-747 R-774 R-734 R-624 R-502 R-99 closed; R-890/R-891 filed; 03 gains FELHOM_FSTRIM and GET /host/crash-guard (149 -> 143)
gates / gates (push) Successful in 2m47s

CI 1423 (929e59e8) was red on golden-currency: it read controller v0.300.0 before this commit recorded its golden.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-10-06 12:24:43 +02:00
parent 929e59e8f4
commit bfdea83204
12 changed files with 453 additions and 8 deletions
@@ -118,6 +118,7 @@ fixed file, delivered by the signed config bundle):
| `FELHOM_OOB` | the OOB firewall sets | `add element` takes exactly `{ <ip>[/n] }` or `{ <port> }` — no chained command | — |
| `FELHOM_PBSDR` / `FELHOM_BACKUPTARGET` | PBS DR entry; whole-system backup target | unchanged: the arguments stay coarse, and the root wrappers (`felhom-pbs-apply`, `felhom-backup-target-apply`) are the gate (fixed verbs, own validation) | coarse argv into a checking wrapper |
| `FELHOM_ESCROW` | the recovery-code ceremony (runs the agent binary as root) | the binary is only ever an operator-signed one (`FELHOM_SELFUPDATE`); as root it pins the PVE secret dir and the WG state dir, refuses a storage id that is a path, and reads its two staged files by walking the path with `openat(O_NOFOLLOW)` (no symlink anywhere) | **by design the agent relays R**, so a compromised agent can still learn this box's PBS key through the ceremony — not root, but the backup key |
| `FELHOM_FSTRIM` | the weekly disk trim of each customer guest (R-444, `09` §3 decision 139; agent v0.149.0) | ONE regex-anchored rule `/usr/sbin/pct ^fstrim [0-9]+$` — no option, no second vmid, no chained command (pinned by `TestSudoersFstrimRuleIsExact`; read live on demo-hp and demo-felhom 2026-10-06: `pct fstrim 9201` allowed, `--ignore-mountpoints`, `;x`, `9201 9202`, `pct destroy` refused) | — |
| `FELHOM_SELFHEAL` / `FELHOM_GUESTNET` / `FELHOM_OSAPPLY` | networking restart; guest DHCP watchdog; OS updates | exact; `felhom-os-apply --plan …` stays the glob line on purpose — the bundle's own self-check reads that exact text, and the wrapper refuses any other plan path (R1) | — |
**What this does not change.** The operator key (`/etc/felhom/operator-signers`, root-owned, never a bundle path) stays
@@ -229,6 +230,10 @@ The controller (in its LXC) reaches the agent (on the host) over the local bridg
- `POST /backup` — request a backup-now of *this* guest (enqueued; non-destructive).
- `GET /backup/due` — whether a policy-scheduled backup is due for *this* guest, so the controller can quiesce then call `POST /backup` (the app-consistent path, §8).
- `GET /backup/status`, `GET /restore-test/status` — read-only status for the controller's UI.
- **Crash-boot fact (R-856, agent v0.149.0):** `GET /host/crash-guard` — the host crash guard's last-boot record
(`present`, `last_boot_at`, `last_boot_unclean`, `tripped`) read from `/var/lib/felhom-crash-guard/state.json`; a missing
or garbled file answers 200 `present:false`, an older agent 404 — both read as a normal boot. The controller waits
~15 min with app mails after a crash boot (`09` §3 decision 143).
- **Host metrics (slice 9):** `GET /host/metrics` — **host-wide** health for the customer's
monitoring view: cpu%/mem/load/uptime, **CPU/chassis temperature** (`cpu_temp_c`, nullable —
"n/a" when the hardware exposes no sensor), and per-storage capacity (total/used/fraction,