channelhealth: controller->agent channel health-check (periodic probe + classified operator alert) v0.90.0

New internal/channelhealth Checker: ~60s probe via the PRODUCTION memoized client
(Server.ProbeAgentChannel, GET /storage), classifies failures (spike Q1 map), debounces transient
reasons (N>=2; construction error latches distinctly), seeds first obs, alerts operator+dashboard on
transition. Notifier.NotifyAgentChannelDown/Recovered (English, operator-only), AlertManager dashboard
banner (Hungarian). No agent/hub change. Spike-proven.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EPZ4GJ8L5Jqf8UiPwbn1kt
This commit is contained in:
2026-06-29 20:28:02 +02:00
parent 77bccf1212
commit a277b18981
7 changed files with 531 additions and 6 deletions
+28
View File
@@ -1,5 +1,33 @@
## Changelog
### v0.90.0 — Controller→agent channel health-check (periodic probe + classified operator alert) (2026-06-29)
- **What:** the next self-health slice — a ~60s scheduler job that proves the controller↔agent
local-API channel, classifies failures, and alerts the operator + dashboard on a state change.
Closes the gap the R1 pin-mismatch incident exposed (the channel was only checked once at startup and
only logged). Spike-proven: `felhom.eu/documentation/audits/SPIKE-controller-agent-channel-health-2026-06-29.md`.
- **`internal/channelhealth` (NEW):** a `Checker` over two seams — a `Probe` (the channel call) and a
`Sink` (dashboard + operator notify). Each run classifies the result into `up | down:<reason>` by the
spike Q1 error-substring map (`pin_mismatch` / `unauthorized` / `unreachable` / `timeout` /
`misconfigured` / `construction_error` / `unknown`). **Debounce:** transient reasons (refused/timeout)
require **N≥2 consecutive** down-probes before alerting — so the ~1s agent-restart socket gap (spike
Q2) does NOT page; pin/401/DNS/construction alert on the first down observation. First scheduler
observation **seeds** state without notifying (mirrors host_staleness/host_capability). A
construction error (`agentClient()` can't build — latches via `sync.Once`) is surfaced **distinctly**.
- **Probe via the PRODUCTION memoized client (`Server.ProbeAgentChannel`):** GET /storage through
`s.agentClient()` — NOT a fresh `agentapi.New` per probe. Per the spike, the memoized client
self-heals after an agent restart, reflects exactly what the disk UI sees (zero divergence), and
avoids the per-call transport leak the singleton fixed.
- **Operator alert + dashboard:** `Notifier.NotifyAgentChannelDown/Recovered` relay an **English,
operator-only** event (the customer can't act on "the agent re-keyed"; the event type isn't a
customer toggle, same as the host_* events) on a **transition** (up→down, down→up, or reason-change),
with the existing hub 1h cooldown. `AlertManager.SetAgentChannelAlert` shows a short Hungarian
banner whenever the channel is down (state-based, idempotent — a born-down channel shows even though
it's seeded silently). **No customer email.** No agent or hub change (the hub relays the new event
types generically).
- Tests: classifier per-reason, transitions (up→down once, no duplicate, recovered, reason-change
re-alerts), first-obs seed, and the **debounce red-proof** (one refused → no alert; two consecutive →
exactly one). Version `0.89.0 → 0.90.0`.
### v0.89.0 — App-email: plaintext-only listener (:2526) + split-From mapping (2026-06-29)
- **What:** closes the two relay gaps from `FINDING-app-email-rollout-2026-06-29.md` so the
opportunistic-STARTTLS clients (cal.com, nextcloud) can use the relay.