hub v0.159.0: a refused whole-guest backup has its own alarm (R-1150); a reinstall re-issues the claim code and the off-site login when the old host is stale or off (R-1154)

R-1150: the agent's localapi:backup-cadence-cap is a separate per-host state in
HostCapabilityChecker: alone it raises whole_guest_backup_refused (warning, plain text,
agent reason in details) and whole_guest_backup_refusal_cleared (info); other critical
caps keep agent_capability_degraded/_recovered unchanged. New types operator-only and
hub-origin only (not in allowedEventTypes: the suspected controller must not post them).
agent_capability_degraded/_recovered join operatorOnlyEvents (the checker's "never the
customer" claim was configuration only).

R-1154: handleHostEnroll's reuse path re-issues (claim code + off-site login) when no
controller wait is open and either deliberate_off_at is held or both the host report and
the controller report are older than alerting.stale_threshold (wired from main); once per
silence (host_reenroll_reissued record, also written at mint); a live box gets nothing
re-issued and the skip is recorded (host_reenroll_not_reissued). 05 section 8 [DESIGN].

Tests red-proved; go build/vet/test green.
This commit is contained in:
admin committed 2026-10-19 03:18:34 +02:00
1 parent e4f022a0d9
commit 5121536f10
14 files changed
+995 -33

No files matched your search

+8 -2
View File
@@ -148,7 +148,13 @@ Events are the primary monitoring mechanism. Each event has: customer_id, event_
- `node_stale` / `node_down` / `node_recovered` — dead man's switch from staleness checker (every 60s)
- `host_stale` / `host_down` / `host_recovered` — host-domain dead man's switch (agent host reports)
- `agent_capability_degraded` / `agent_capability_recovered` — `HostCapabilityChecker`: an agent's
required `sudo -n` grant went missing (the non-root cutover class)
required `sudo -n` grant went missing (the non-root cutover class). Operator-only since v0.159.0.
- `whole_guest_backup_refused` (warning) / `whole_guest_backup_refusal_cleared` (info) — the same checker, a
SEPARATE state (R-1150, v0.159.0): the agent's `localapi:backup-cadence-cap` reports that it refused a whole-guest
backup a guest asked for beyond its schedule (R-973). Operator-only; hub-origin only (not POST-able by a box).
- `host_reenroll_reissued` / `host_reenroll_not_reissued` (info) — `POST /host-enroll` reused an existing host
record and re-issued the claim code and the off-site login (the old box is gone), or did not (the box is live)
(R-1154, v0.159.0). Operator-only; hub-origin only.
- `host_leaf_changed` — `HostLeafChecker` (`monitor/host_leaf.go`, every 60s): an agent's reported
local-API **leaf fingerprint** changed (an agent re-key) — proactive, fleet-wide, independent of any
controller's channel-health check. Trust-on-first-report baseline; empty fp = unknown (never alerts).
@@ -181,7 +187,7 @@ Config retrieval uses a separate per-customer retrieval password (not the API ke
|--------|------|-------------|
| `POST` | `/api/v1/host-enroll` | Mint-or-reuse a host credential, body `{customer_id}` (auth: `X-Retrieval-Password` header) → `{host_id, api_key}` |
The Day-0 first-boot handshake (`documentation/audits/SPIKE-day0-firstboot-handshake-2026-06-26.md`) enrolls a Proxmox host's agent **using the customer's retrieval passphrase only** — the operator-tier global key never enters the field deploy path. `host-enroll` mints the per-host credential on the first call and **reuses it byte-for-byte** on every later call (idempotent — re-running the host-bootstrap never orphans a running agent's key), backed by `Store.GetHostByCustomer`. Auth is checked **before** any mint, so a wrong passphrase never creates a host row. Behaviour: first → `201`, reuse → `200`, wrong/missing passphrase → `401`, unknown customer → `404`, missing `customer_id` → `400`.
The Day-0 first-boot handshake (`documentation/audits/SPIKE-day0-firstboot-handshake-2026-06-26.md`) enrolls a Proxmox host's agent **using the customer's retrieval passphrase only** — the operator-tier global key never enters the field deploy path. `host-enroll` mints the per-host credential on the first call and **reuses it byte-for-byte** on every later call (idempotent — re-running the host-bootstrap never orphans a running agent's key), backed by `Store.GetHostByCustomer`. Auth is checked **before** any mint, so a wrong passphrase never creates a host row. Behaviour: first → `201`, reuse → `200`, wrong/missing passphrase → `401`, unknown customer → `404`, missing `customer_id` → `400`. **Reinstall (R-1154, v0.159.0):** on a reuse whose old box is gone — its host report AND its controller report both older than `alerting.stale_threshold` (or absent), or the household switched it off on purpose, and no controller wait connection open — the enroll also runs the clean-slate re-issues (claim code, off-site login), once per silence; a live box re-enrolling gets nothing re-issued (`internal/api/reenroll.go`, `05` §8).
The global-key `POST /api/v1/admin/hosts` (operator/HQ pre-mint) remains as the escape hatch pending the enrollment-cutover lock-down; it and `GET /config/{id}` are unchanged by this endpoint.