Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EPZ4GJ8L5Jqf8UiPwbn1kt
5.2 KiB
REPORT — felhom-agent v0.44.0: privileged-capability self-probe
Date: 2026-06-29 · Class: Implementation (agent self-health, slice 1) · Baseline:
main @ 1958204 (v0.43.0) → v0.44.0 @ 25024d9. Companion hub: felhom.eu v0.19.0.
What & why
A non-root agent that can't run a privileged command it depends on is degraded and must say so — at cutover, not days later. Five casualties of the 2026-06-28 root→non-root migration (leaf/token/ lxc-info/make-private/controller-swap) went undetected until user-visible breakage. This adds a self-check that catches the class at BUILD time (CI) and RUNTIME (startup + each hub-report cycle), serve-degraded + loud. (The controller↔agent channel check is a separate later task.)
internal/capability — manifest + probe
- Manifest (
Manifest()): the required(binary, representative-arg)vectors, seeded from the 2026-06-29 sudoers audit (the OK + CLOSED rows). The SURFACED/DEFERRED rows are excluded: generalpct exec *(controller-swap, arbitrary exec — an open operator decision),pct create(golden build, no daemon caller),mount UUID=…(legacy),sensors -j(callerless + uninstalled). - Critical (hub alerts on these):
guest-init-pid(lxc-info), the intermediary mount surface (parent-self-bind,parent-make-shared,parent-make-private,drive-bind,drive-umount), and the disk format/inspect gate (disk-blkid,disk-lsblk,disk-mkfs-ext4). The rest (provision/dnsmasq/guesthook/mount-units/smart/lvs) are non-critical (degrade reported, no alert). - Probe (
Prober.Probe): per entry,sudo -n -l -- <binary> <reprArgs>— a sudo policy LIST (exit 0 ⇔ permitted), run via a DIRECT runner so it never executes the command (safe for mkfs/pct entries) — plus anos.Statexistence check. Maps took/degraded("sudo policy denied" | "binary not found"). A total sudo failure (drop-in missing) collapses to ONE aggregate signal. Never blocks startup, panics, or errors (serve-degraded).
Build-time gate (the headline) + red-proof
manifest_test.go parses configs/felhom-agent.sudoers, glob→regex translates each grant, and
asserts every manifest vector is covered — exactly what would have caught the dropped
lxc-info/make-private lines in CI. Red-proof (TestRedProof_DroppedGrantFailsCheck): with
the lxc-info line removed from an in-memory copy, the check FAILS for guest-init-pid, and passes
on the real file. Demonstrated: ran with the line dropped → FAIL; restored → PASS (not
hollow).
Runtime wiring + report
Probe runs once at startup (logCapabilities: INFO capabilities self-check N/N ok + an ERROR per
degraded naming the gated feature) and on every collect (Collector.SetCapabilityProber); the
snapshot rides the report as the new non-nil HostReport.Capabilities []capability.Status. Golden +
contract test updated; the cross-repo hub golden mirrors capabilities: [].
Tests (all green: go build/vet/test ./...)
TestManifestCoveredBySudoers(manifest ⊆ sudoers, 40 vectors) +TestRedProof_....- Probe:
AllOK,OneDenied(critical degraded, sibling ok, serve-degraded),BinaryMissing,SudoUnavailableAggregates,NilRunnerNoPanic. Asserted the probe never executes a non--lcall.
Live validation (felhom-pve, guest 9201) — deployed + verified
- Deploy: built
0.44.0on 180 (sha675b711e…), shipped 180→local→felhom-pve (sha verified identical), backed up the prior binary (felhom-agent.bak-0.43.0),install+systemctl restart.systemctl is-active= active. - OK-path (live): startup log
capabilities self-check ok=40 total=40 degraded=0— N/N ok, a live proof the probe agrees with the post-audit-complete sudoers. Hub (v0.19.0) loggedHost capability checker initialized: 1 ok, 0 degraded. - Degraded detection (live, non-intrusive): on the real host, a granted vector
sudo -n -l -- /usr/bin/lxc-info -n 9201 -p -H→ exit 0 (ok); a bogus vector → exit 1 (degraded) — proving the probe's grant discrimination without mutating the freshly-certified live sudoers.
NOT yet live-validated (deferred — see §16 forward note)
- Full live agent→hub→operator-email degraded transition. The hub alert fires only on a
Critical capability degraded, which would require removing a Critical grant on the live host
(forbidden — would re-break drives) or a separate test host, and would send a real operator email.
It is fully covered by unit tests (
hubTestHostCapabilityChecker: one event on ok→degraded, none steady, recovered, non-critical ignored, old-agent ok). Deferred to a scratch-host exercise. - Build-test in CI: runs in
go test ./...; no CI runner is wired in this repo yet (the test is the gate, run on every local/build-servergo test).
Forward note (NOT this task)
The manifest is hand-curated; the build-test locks manifest ⊆ sudoers but code → manifest
drift (a new runner.Run call not added to the manifest) is not auto-caught — a future static check
of runner.Run call sites against the manifest would close that. The controller↔agent channel check
is the next self-health slice.