Commit Graph

18 Commits

Author SHA1 Message Date
admin 7f11cfb36c hub v0.65.0 — PBS DR storage visibility (ep0 usage op) + Offsite tab split + dual dashboard gauges (R-5)
Makes PBS DR storage visible like the restic pool box (v0.64.0), differentiated. Scoping
correction: restic = subaccounts on the shared Hetzner Storage Box (Hetzner API); PBS DR =
the felhom-offsite PBS datastore on the ep0 endpoint VM (NO Hetzner API). Option A
(Viktor-ruled): a read-only `usage` op on the felhom-tenantsync ep0 forced command (twin of
fingerprint), polled by a new hub checker on the 15-min throttle. READ-ONLY throughout.

Phase-0 (gate PASSED): on ep0 (PBS 4.2.3), df -B1 --output=size,used,avail <datastore path>
yields bytes (39990112256/7627939840/... ~19%), read-only, existing sudo context, no admin token.

- scripts/felhom-tenantsync.sh -> v1.2.0: read-only `usage` short-circuit (df on the datastore
  path), no customer_id, no admin token, NO mutation. + a bash harness proving zero mutation.
- tenantsync.Client.Usage() + BoxUsage; unknown-op -> typed ErrUsageUnsupported (graceful).
- monitor.PBSDRBoxChecker: OffsiteBoxChecker clone over a usageReader seam; 15-min throttle,
  cached PBSBoxSnapshot, escalation-only pbsdr_box_fill on the "pbsdr-box" scope (operator only,
  no SaveEvent), recovery re-arm. Fill only. THREE states: ok / unavailable (ep0 <=v1.1.0,
  neutral no-alert) / degraded (exec failed, keep last).
- config: Alerting.PBSDRBoxFill{Warn,Crit}Percent (80/90); built with the tenantsync client,
  60s sweep, SetPBSDRBox. Hub deploy INDEPENDENT of the ep0 update (graceful degradation).
- web: /offsite splits into Restic + PBS DR hash tabs (endpoint cards under PBS DR); PBS panel;
  the single dashboard tile becomes two gauges (RESTIC pct.ratio, PBS DR pct / n/a).
- runbook offsite-endpoint.md 10: v1.2.0 update steps (no sudoers/authorized_keys change).

Tests: 10 Go + the harness; 3 red-proofs (usage mutation, escalation-only, unavailable-drives-band)
confirmed red then restored. go build/vet/test + bash -n + hub confirm gate all pass.
2026-07-17 21:13:30 +02:00
admin 4bb2df0dc4 hub v0.64.0 — offsite pool-box aggregate: fill, oversubscription, per-customer bars, operator alert (R-5)
The operator sees the shared pool box's real state on the hub: total box fill vs
capacity, Σ(shared soft quotas) vs capacity (the oversubscription ratio), per-customer
usage/quota bars, and a box-level operator alert (fill % + oversub ratio) on the existing
dispatcher's operator channel. Per-customer fill alerts already existed; the box-level
aggregate was the gap. READ-ONLY against Hetzner (GET only).

Phase-0 probe (gate PASSED): the live pool box 611714 returns capacity via
storage_box_type.size (1 TiB / bx11) and usage via a stats object (size/size_data/
size_snapshots), all bytes; our token reads it (200).

- hetznerapi: additive StorageBoxType + StorageBoxStats on StorageBox (no existing field/
  method changed); fake carries them + a GetBoxCalls counter; golden decode test.
- monitor.OffsiteBoxChecker: OffsiteChecker-sibling for the box; fetch-throttled (1 GET/
  15min), cached BoxSnapshot, escalation-only + recovery re-arm. FILL (used/capacity 80/90)
  + OVERSUB (Σ shared+enabled quotas / capacity, 2.0x) — independent. Σ from the ConfigJSON
  Descriptor (offsite.ReadDescriptor, new), never the report echo; dedicated+disabled
  excluded. Scope "pool-box" -> operator channel only, no SaveEvent. Failed fetch keeps the
  last snapshot degraded; missing data never becomes 0% and never transitions a band.
- config: Alerting.OffsiteBoxFill{Warn,Crit}Percent + OffsiteOversubWarnRatio (80/90/2.0
  defaults; thresholds pending Viktor's ruling). Constructed in the HETZNER_TOKEN branch,
  60s sweep, snapshot handed to the web server.
- web: Offsite-tab panel (fill bar, Σ+ratio, per-customer usage/quota rows) + a compact
  dashboard tile; reads the cached snapshot only, never fetches; nil -> "not configured".

Tests: 10 new + 4 red-proofs (throttle, Σ filter, escalation-only, failed-fetch honesty),
all confirmed red then restored. go build/vet/test all pass; hub confirm gate OK.
2026-07-17 20:15:34 +02:00
admin 36c72138f1 hub: dead-host roll-up honesty - customer status folds worst expected host
Customer status (dashboard row, /configs list, detail header + strip) is
now worst(controllerDerived, hostStatusOf(each expected host)) via the ONE
staleness definition (Server.hostStatus, hosts.go - shared with the
HostStalenessChecker; no second threshold). Any host down/stale caps the
customer at WARN with a cause chip naming the host ("host down: <id>");
pending (never-reported) hosts worsen only once the customer has reported
(onboarding exclusion). The three previously-inlined controller-status
chains collapse into controllerStatus() (rollup.go). Display + derivation
only - checker alerting untouched.

Live shape pinned (drill-1 / Peti cluster): host down 23h + controller
report minutes old rendered a GREEN row - TestRollup_DeadHostMasking now
fails that exact outcome. Red-proof: short-circuiting foldHostStatus to
controller-only flips C + two D subtests red ("dashboard row is GREEN
over a 23h-dead host").
2026-07-13 14:52:04 +02:00
admin 1d94b1a9e9 hub: scoped auto-refresh + style.css cache-bust + staging rule (v0.48.0 parts 3-4)
- Auto-refresh: the 60s reload fires only while a live tab is active
  (nav data-live-tabs="overview,applications,events,host") AND no form is
  dirty (delegated input/change listener; never reset — a reload clears it).
  Skipped ticks reschedule; a muted (paused) hint shows next to the toggle on
  non-live tabs / dirty forms. Toggle, localStorage key, 60s cadence, and
  default-on behavior unchanged. The refresh script resolves the legacy
  settings→edit hash alias like the tabs script.
- Rider 4a: every template's stylesheet link is /style.css?v={{hubVersion}}
  (the v0.47.0 gotcha: max-age=3600 served stale styling for up to an hour
  after a deploy). Red-proof run: a reverted bare link fails the test.
- Rider 4b: CLAUDE.md standing rule — never git add -A in this repo
  (the 146d165 sweep incident); explicit paths + pull-rebase + one writing
  session per clone.
- Tests: Group C structural pins (attribute read, dirty listeners, alias x2,
  hint element, cadence/key survivors) + Group D cache-bust over six pages.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TZc5w5jDhFLv6qDC32KN5v
2026-07-12 17:37:04 +02:00
admin 13203c2452 hub: S2 /offsite registry page (read-only) + nav + WGPeer.CreatedAt
Endpoint card + peers table (truncated pubkeys with full-value title attr,
bound peers link to /hosts/<id>); Offsite nav link in all 9 page templates;
render tests for endpoint/peers, empty, and not-configured states.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-04 00:48:03 +02:00
admin b5f00509ee hub v0.31.0: accept 'critical' severity at event ingest + UI badges/CSS; event_test.go (red-proofed); REUSE.md §1/§3 updated
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-03 11:08:44 +02:00
admin c136308aad D4 Part 2: hub style.css v2 + template sweep
- style.css: slate tokens -> the canonical navy block + @font-face
  (self-hosted, latin-ext for Hungarian customer names); single 2px
  radius; hairline table rows (--line-soft dividers, density kept);
  status classes re-expressed per the design-system addendum:
  .status-badge = outline tag + CSS ::before dot keyed by the raw
  status the templates emit (ok=blue, warn/blocked/stale=amber,
  down/fail=red, pending/disabled=quiet neutral); .status-dot =
  class-based 8px dot; severity badges stay filled amber/red
  (exceptions stay loud); config badges = filled informational chips
  in v2 colors; row tint only for warn/down (warn-dim/crit-dim).
- Two-tone brand heading on all 8 'Felhom Hub' pages
  (<h1>Felhom <span>Hub</span></h1>, span = blue-bright).
- app_detail chart retinted: avg #2EA8F5 (primary data), peak #8E7CE8
  (secondary DATA series — not status red), catalog-limit line #E0A93E
  (threshold marker); legend/tick/grid -> v2 literals.
- customer_unified: JS status-message colors (success -> blue-bright,
  error -> crit), all inline slate hexes -> tokens.
- Login page inline HTML retinted (#0088cc -> #0083D8).
- Grep gate: every slate hex (#0f172a #1e293b #334155 #60a5fa #4ade80
  #facc15 #f87171 #94a3b8 #64748b #475569 #e2e8f0) at ZERO across
  hub/internal/web (non-test); statusIcon 0; inline statusColor
  style 0. go build/vet/test green; api/ + store/ untouched.
2026-07-02 22:38:37 +02:00
admin bc8d54df6a D4 Part 1: hub fonts + sprite + statusColor semantic remap
- static/fonts/: the 4 vendored woff2 (byte-copied from
  felhom-controller), embedded (embed.go) and served at /static/fonts/
  (font/woff2, immutable) mirroring the chart.min.js pattern. No CDN
  before, none now.
- templates/icons.html: 12-symbol Lucide sprite partial (icon_sprite),
  included at the top of <body> on all 9 pages ({{template}} — the hub
  has no shared layout; per-page include is the minimal shared block).
- statusColor now returns v2 semantic tokens (nominal/warn/crit/
  neutral) consumed as class suffixes: ok->nominal, warn+stale->warn,
  down+fail->crit, pending+disabled->neutral (a not-yet-provisioned or
  deliberately paused customer is a normal fleet state), blocked->warn
  (intentional operator cut-off, attention-worthy not an outage),
  unknown->neutral. The inline style="color: {{statusColor}}" pattern
  is dead: dashboard + customer_unified render a class-based
  .status-dot-<token>; statusIcon (constant "●") retired from funcmap
  and templates.
- Tests (new; the hub web package had no funcmap/template tests):
  TestStatusColorTruthTable over the full enumerated status set —
  red-proven vs the old implementation (ok returned "#4ade80") — and
  TestTemplatesParseWithFuncmap.
2026-07-02 22:34:40 +02:00
admin 2289fc907c hub v0.27.0: Hosts page — read-only fleet view (audit F-M1)
New Hosts nav section: a fleet list (/hosts) + per-host detail (/hosts/{id}),
read-only (GET only, no host actions). Surfaces identity, agent version,
online/stale status (reusing the HostStalenessChecker threshold), guests,
vitals, storage targets with SMART/thin-pool, and DR/escrow presence.

- store: new ListGuestsForHost reader (reality cols only; omits api_key/
  desired_spec_json) + scanGuest helper.
- web: handleHostsList + handleHostDetail (hosts.go); hosts.html +
  host_detail.html; Hosts nav link on every page; timeAgoPtr helper; routes.
- tests: store getter, both handlers, no-secret (api_key) assertion, 404,
  no-report empty state, status-band mapping.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 06:10:02 +02:00
admin 1e354cbd41 feat(hub): Configuration page, asset seedOrUpdate, English UI
- Add Configuration page with "Refresh Assets" button
- Replace seedIfEmpty with seedOrUpdate (SHA-256 compare on startup)
- Translate all Hungarian text on Apps pages to English
- Add Configuration tab to all template navigation
- Expand isAssetFile to match favicon patterns
- Add felhom-logo.svg to website assets for the pipeline

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 09:34:43 +01:00
admin a757bee07a feat(hub): app telemetry analytics dashboard (v0.4.0)
- store/telemetry.go: new app_telemetry + app_log_issues tables with
  SaveAppTelemetry, GetFleetAppSummary (with P95), GetAppTelemetryHistory,
  GetAppCustomerBreakdown, GetCustomerAppSummary, GetAppIssues, prune methods
- api/handler.go: parse and save optional app_telemetry from report body,
  backward-compatible with old controllers
- cmd/hub/main.go: prune app_telemetry (90d) and stale issues (30d)
- web/apps.go: handleApps + handleAppDetail + chart data aggregation helpers
- web/server.go: routes for /apps, /apps/{name}, /static/chart.min.js;
  added memoryColor/accuracyClass/gt template functions
- web/embed.go: embed static/chart.min.js
- web/configs.go: add app telemetry section to handleCustomerUnified
- templates/apps.html: fleet-wide app list with summary cards and sortable table
- templates/app_detail.html: per-app page with Chart.js memory trend,
  customer breakdown, and known issues table
- templates/customer_unified.html: new Alkalmazás telemetria card
- templates/style.css: badge, summary-card, chart, period-selector,
  accuracy-dot, mem-color, data-table styles
- All templates: added Alkalmazások nav link

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 10:46:50 +01:00
admin 6bcbaa1574 hub v0.3.2: Show Hub version in page footers
Add hubVersion template function via closure in web.New(). Version is
passed from main.go (set via ldflags at build time) and displayed in
the footer of all 6 page templates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 19:39:52 +01:00
admin 3217cb4751 feat: Hub monitoring takeover — event system, dead man's switch, notifications (v0.3.0)
Replace external Healthchecks.io with Hub-native monitoring. New events
table + /api/v1/event endpoint for structured events from controllers.
Staleness checker (60s) detects unresponsive nodes. Backup deadline
checker (daily 05:00) catches missed backups. Notification dispatcher
sends operator (English) + customer (Hungarian) emails via Resend with
per-event cooldowns. Event timeline on customer page, dashboard badges.
Config form deprecates Monitoring UUIDs section.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 18:53:24 +01:00
admin 42e0617a6c hub: unified customer page, blocked status, dashboard merge
- Replace separate config detail and report detail pages with unified
  /customers/{id} page showing both config info and live report data
- Add "blocked" status for customers (hidden from dashboard, notifications
  suppressed, still accepts reports)
- Dashboard now shows config-only customers as "PENDING" status
- Customers list: all rows link to /customers/{id}, show BLOCKED badge
- New actions: block/unblock, push config to controller, auto-create
  config from report data
- /configs/{id} now redirects to /customers/{id}
- Add config-badge CSS classes for MANAGED/MANUAL/BLOCKED badges

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 15:57:39 +01:00
admin cb425d8086 feat: merge report-only customers into Customers page, rename tab
- Customers page now shows ALL customers: both pre-configured (managed)
  and report-only (manual) — merged from customer_configs + reports tables
- Renamed "Configurations" → "Customers" in navigation tabs
- Renamed "+ New Configuration" → "+ Add Customer"
- Status column with ok/warn/down badges, version column, managed/manual badge

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 15:27:05 +01:00
admin 4c8bf63ce3 feat: customer config management — CRUD, API retrieval, per-customer auth (v0.2.0)
New "Configurations" section lets operators pre-configure customer settings
in the Hub, then docker-setup.sh can download a ready-made controller.yaml
using just a customer ID and retrieval password.

- Store: customer_configs table with CRUD + per-customer API key lookup
- API: GET /api/v1/config/{id} with X-Retrieval-Password auth
- Auth: per-customer API keys alongside existing global key (backward compatible)
- Web UI: /configs list, create, edit, delete, YAML preview, copy-to-clipboard
- YAML gen: deep-merge controller.yaml.example template with customer overrides
- Template fetcher: background goroutine refreshing template from Gitea repo
- Navigation: Dashboard / Configurations tabs on all pages

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 13:36:32 +01:00
admin d3d3044b98 hub v0.1.6: Handle disabled reporting status, storage labels, date in history 2026-02-19 09:45:45 +01:00
admin 77b5a4ce4e Add felhom-hub: multi-customer dashboard service
- Hub service receives reports from customer controllers
- SQLite store with 90-day retention and auto-prune
- REST API: POST /api/v1/report, GET /api/v1/customers
- Dark theme dashboard with status overview table
- Customer detail page with system, storage, containers, backup, health
- Bearer token auth for report ingest, bcrypt auth for dashboard
- K8s manifest for felhom-system namespace (Deployment, Service, Ingress, PVC)
- Dockerfile with multi-stage build

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 13:19:25 +01:00