Files
felhom.eu/REPORT.md
T

51 lines
3.5 KiB
Markdown

# felhom.eu — task reports
> **Overwrite** this file with a summary of the most recent task only (uniform with the other repos; not cumulative). The cumulative hub history lives in [hub/CHANGELOG.md](hub/CHANGELOG.md).
## Hub-side agent leaf-fingerprint change detection (HostLeafChecker) — hub v0.22.1
**Date:** 2026-06-29. Companion: felhom-agent **v0.48.0** (Part A — reports the served leaf fp). This
repo = **Part B (hub)**. The last self-health leg: a proactive, fleet-wide agent-re-key alert.
### Implementation
- **`monitor.HostLeafChecker` (NEW):** sibling of `HostCapabilityChecker`. **Trust-on-first-report**
the first leaf fp seen per host is the baseline; a later change emits **`host_leaf_changed`**
(operator-only, English; details carry old+new fp) and advances the baseline. First-obs seeds
silently (a change needs a prior value → no F2 issue). An **empty** reported fp (pre-v0.48.0 /
local-API-disabled) is unknown — never seeds, never alerts, never overwrites a baseline.
Customer-blocked hosts dropped; unseen pruned. Same 60s sweep.
- **`store.GetHostLeafFingerprints` (NEW):** latest reported fp per host, parsed from `report_json`
(mirrors `GetHostCapabilities``MAX(id)`, **no schema migration**; chose the report_json read over a
denorm column to avoid an `ALTER TABLE`).
- **No allowlist change:** `host_leaf_changed` is **hub-generated** (`SaveEvent` + `dispatcher.ProcessEvent`),
not controller-pushed, so it bypasses the `/api/v1/event` `allowedEventTypes` gate (same as
`host_*`). The generic operator template relays it.
- **Known limitation (documented):** trust-on-first-report can't detect a re-key that happened *before*
the hub's first report — but the controller channel-check catches the downstream pin mismatch, so
this is defense-in-depth, not the sole guard. (The served-fp-vs-pinned-fp authoritative cross-check is
a deliberate future enhancement.)
### Tests (green: `go build/vet/test ./...`)
Change **red-proof** (A→B → one `host_leaf_changed` + baseline advanced; companion: unchanged → none),
first-obs seeds silently, change-back re-alerts, empty fp skipped, customer-blocked dropped. Cross-repo
golden mirrors `leaf_fingerprint`.
### Live validation — PASS (+ a wiring bug the live test caught)
- **v0.22.0 shipped the checker but the `cmd/hub/main.go` goroutine wiring never applied** (a concurrent
file-touch made me wrongly assume the edit landed). The **live test caught it**: a leaf regen produced
no `host_leaf_changed` — only the controller's complementary `agent_channel_pin_mismatch`. Fixed in
**v0.22.1** (wired + redeployed). A good example of live validation catching what the build/test
couldn't.
- After v0.22.1: `Host leaf checker initialized: 1 host fingerprint(s) seeded`; then restoring the leaf
(a change from the seeded baseline) →
`[WARN] Host leaf: demo-felhom-01 fp 82078fab77ea… → 60b5974d586f… (host_leaf_changed)` (~8 s, one
sweep), event saved (no error) + dispatched to the operator. **Independent of** the controller's
channel-check, which also fired `agent_channel_pin_mismatch` during the regen — the two are
complementary. Final state: leaf == pin `60b5974d…`, channel recovered, capabilities 45/45,
controller healthy.
### Self-health arc — complete
Agent watches its own privileged capabilities (v0.44.0) + the controller watches its link to the agent
(controller v0.90.0/F2) + the hub proactively watches every agent's leaf fp fleet-wide (this). The
original silent-multi-day-outage incident class is now caught from three independent angles.