hub v0.47.0: CHANGELOG + README tab/host-lifecycle/offsite docs + REUSE map + manifest bump to 0.47.0

CONTEXT.md: v0.47.0 arc entry (resolves the 146d165 swept-WIP flag) + the
multi-endpoint-allocation deferral + pre-GitOps stale-template note.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vvz1NCu22p8dGkRCpeX9re
This commit is contained in:
2026-07-11 21:39:44 +02:00
parent 0daddcd1c4
commit 95d71fef2e
5 changed files with 105 additions and 10 deletions
+24 -4
View File
@@ -232,11 +232,31 @@ Synchronizer-token CSRF protection on all browser POST/DELETE/PATCH operations:
- **Customers (`/configs`)** — Customer management list. Shows all customers (both managed and manual), their status, controller version, and config type (MANAGED/MANUAL). Blocked customers shown grayed-out with BLOCKED badge.
- **Fleet App Analytics (`/apps`)** — Fleet-wide app telemetry overview (v0.4.0+). Shows all deployed apps across all customers with deployment count, avg/P95 memory, catalog estimate/limit accuracy indicators, and 24h error/warning badge counts. Sortable columns (deployments/memory/errors), 24h/7d/30d time period selector.
- **App Detail (`/apps/{name}`)** — Per-app drill-down page with Chart.js memory trend (avg + peak lines, catalog limit dashed line), per-customer breakdown table, and known log issues table (severity, message, occurrence count, affected customers, first/last seen). Shows suggested mem_limit from P95×1.2 rounded to 32 MB.
- **Unified Customer Detail (`/customers/{id}`)** — Single page per customer combining config management and live monitoring. Auto-refresh toggle (localStorage-persisted, enabled by default) replaces the previous hardcoded 60s meta-refresh. Adapts content based on available data:
- **Managed + reporting:** Full view — config info, system metrics, storage, containers, backup status, events timeline (last 50, severity filter), credentials, setup commands, YAML preview, controller update, notifications (with channel column), history
- **Managed + no reports yet:** Config info, credentials, setup commands, "Waiting for first report" indicator
- **Manual (report-only):** System metrics, storage, containers, backup, with "Create Config" button to convert to managed
- **Unified Customer Detail (`/customers/{id}`)** — Single page per customer combining config management and live monitoring. Auto-refresh toggle (localStorage-persisted, enabled by default) replaces the previous hardcoded 60s meta-refresh. Since v0.47.0 the sections are organized into **8 client-side hash tabs** (`#tab=<name>`, deep-linkable, survives auto-refresh) under a sticky summary strip (name, status, controller version, last report, containers). Panels hide only via a JS-added body class — with JS off every section renders stacked (graceful degradation). Tab map:
- **Overview** (default) — customer info, health/issues/warnings, system metrics, storage, backup
- **Applications** — containers, app telemetry, received app log tails
- **Setup** — credentials, setup command generator, YAML preview
- **Settings** — controller update + version floor, geo-restriction
- **Backup & DR** — DR recipe panel + download
- **Events** — events timeline (severity filter) + report history; the tab label carries a red count badge when error events exist (last 24h)
- **Notifications** — prefs + recent notification log
- **Host** — the customer's enrolled host(s), rendered via the shared `host_detail_body` sub-template (a list by design: 1 today, N for a future HA cluster) + cross-link to `/hosts/{id}`
- **Config Form (`/configs/new`, `/configs/{id}/edit`)** — Create/edit customer configurations with identity, infrastructure tokens, and monitoring overrides. Legacy Monitoring UUIDs section collapsed by default with deprecation notice. CF API token requires **Zone DNS:Edit** (ACME) and **Zone WAF:Edit** (geo-restriction) permissions.
- **Hosts (`/hosts`, `/hosts/{id}`)** — fleet list (read-only, zero buttons — pinned by test) + per-host detail (identity, vitals, guests, storage targets, log-bundle diagnostics, DR/escrow presence). The detail body is the shared `host_detail_body` sub-template also rendered on the customer page's Host tab.
- **Offsite (`/offsite`)** — WireGuard endpoint cards + peer registry, with endpoint management since v0.47.0 (see below).
### Host lifecycle — stale host removal (v0.47.0)
Enrollment mints the host's API key exactly once (Day-0 passphrase flow); host reports authenticate via that key, so deleting a host row permanently bricks its heartbeat channel. The delete flow is therefore gated:
- The **danger-zone card** renders only for non-online hosts (stale / down / no-report). An ONLINE host is never deletable — `POST /hosts/{id}/delete` returns 409 unconditionally; no override exists.
- `GET /hosts/{id}/delete-impact` returns the blast radius as counts/booleans only (guests, reports, host-scoped log bundles, escrow/wg-peer/PBS-secret/recovery presence) — never a secret or blob.
- The dialog requires **retyping the host id**; when a key escrow exists, an unchecked-by-default checkbox ("also delete the key escrow + DR bundle") must be ticked — otherwise 409 and the transaction never starts (`store.ErrHostEscrowPresent`).
- `store.DeleteHost` cascades in ONE transaction: guests, host_reports, signed_jobs, host_recovery, host_pbs_secrets, log_bundle_requests/log_bundles with `scope_id == host_id` (customer-scoped bundles are untouched), the bound wg_peers row, host_escrow (only when acked), then the hosts row. The wgsync reconciler's 5-minute declarative push converges the endpoint after the peer row disappears.
### Offsite endpoint management (v0.47.0)
`/offsite` lists **all** `wg_endpoints` rows as cards and can add/edit/delete them (`POST /offsite/endpoints`, `POST /offsite/endpoints/{id}/delete`). Guards: full field validation (CIDR subnet, PBS IP inside subnet, port 165535, pubkey non-empty, id `[a-z0-9-]+`) → 400 stores nothing; changing an endpoint's `tunnel_subnet` or deleting it is refused with 409 while any peer's /32 lies inside the (current) subnet; a server-pubkey change requires a type-to-confirm (peers converge on their next desired-state pull). The peer table shows each peer's containing endpoint. **Scope guard:** peer allocation, the wgsync reconciler push, and the desired-state merge still use the lowest `endpoint_id` only (`GetWGEndpoint`) — per-endpoint allocation (`wg_peers.endpoint_id`) is a deferred future arc.
### Customer States