7 Commits

Author SHA1 Message Date
Anand d9b2519b75 Fleet audit fixes: PBS page, shared SSE, safer ops, deep links + comment cleanup
- New PBS Backups page (/pbs): datastore usage, group browser, per-group
  prune, GC with live status, sync/verify jobs with admin-gated run
- Shared single-connection SSE store (lib/sse.ts) feeding NotificationBell
  and the telemetry pill; global offline banner with reconnect invalidation
- Correctness: CIFS storage credentials sent in JSON body; per-row alert
  silencing + unsilence endpoint; graceful stop-and-delete for running
  guests; accent/shadow token fixes for all look presets
- Safer operations: confirms for SDN apply and host upgrade-all; Refresh
  control on every page; 30s polling added to previously-static pages
- Wayfinding: guests deep-linkable via /inventory?focusGuest, scoped
  inventory views, topology guest click-through, Overview links
- Forms/consistency: node storage/bridge pick-lists in CreateGuestDialog,
  backup schedule validation, BulkOperationsPage on shared DataTable,
  progressive pagination on cluster access lists, shared Timestamp and
  chartToneFor helpers, editable alert rules and webhooks
- Security/tests: CSRF, authz-matrix, redaction and PBS test coverage;
  webhook outbox + connection TLS fingerprint migrations (00032/00033,
  sqlite + postgres)
- Comment audit: removed stale, duplicated and orphaned comments; corrected
  inaccurate doc comments (UpdateGuestConfig, InvalidateAll, breakpoint
  references); no behavior changes
2026-09-13 23:29:33 +05:30
Anand 4dfeb9848e Wave 2: real-time events/webhooks, cross-remote tags/search/bulk-ops, cert & session monitoring
- internal/events: in-process pub/sub bus; GET /api/v1/events SSE stream
- internal/notify/webhooks.go: signed outgoing webhook dispatcher w/ retry + delivery log
- internal/api/tags.go, search.go, bulk.go: cross-remote tag aggregation, global search, fleet-wide bulk guest actions
- internal/auth/sessions.go: session listing/revocation (self-service + admin), extends the existing stateful session table
- internal/poller/certificates.go: cert-expiry + connection-staleness alerting via the existing alert_rules pipeline
- web: useEventStream hook, GlobalSearch + BulkOperationsPage (not yet wired into nav), SessionsCard on ProfilePage
2026-09-10 11:49:11 +05:30
Anand 22c1dd382c Add API keys, MCP server, admin AI providers, and a built-in local LLM option
- User-scoped API keys (Profile > API Keys) for 3rd-party REST API access
  and MCP clients, each locked to one scope at creation, with expiry,
  revocation, and last-used tracking.
- A hand-rolled MCP (Model Context Protocol) server exposing the fleet
  (connections, nodes, guests, storage, pools, alerts, cluster status) as
  read tools plus one admin-gated power-action tool, so Claude Code/Desktop
  or any other MCP client can query and operate the fleet directly.
- Both the REST API and MCP are off by default and toggleable instance-wide
  from Settings > API & MCP, enforced live on every request.
- Admin-managed AI providers (any OpenAI-chat-completions-compatible
  endpoint) backing the AI Assistant's tool-calling loop, replacing the
  single hardcoded provider.
- A built-in, zero-config, no-API-key local provider backed by Needle 2
  (internal/needle) for fully offline tool-calling, wired in as a one-click
  preset. Requires the operator to separately download the Needle 2 binary
  and point FERRUM_NEEDLE_BIN at it -- Ferrum never fetches executable
  content from the network itself; see README "Built-in LLM (Needle 2)".
- System settings (CORS allow-list, instance-wide toggles) moved to the
  admin Settings UI; environment variables are now scoped to true
  bootstrap-level config only (listen address, TLS, DB connection, secret,
  optional Needle binary path).
- Fixed: node Journal tab 502'ing with "unexpected end of JSON input" on an
  empty response, and separately with a decode error on PVE versions that
  return a bare-string journal line instead of the documented {n,t} object.
- Fixed: bottom content padding disappearing on every page except the AI
  Assistant (an unconditional h-full on the content wrapper let overflowing
  content bleed through where the padding should render).
- Fixed: Profile page felt cramped despite a wide viewport (stray max-w-2xl
  cap not present on the equivalent Settings page).
- Test coverage added for the previously-untested MCP package and the new
  Needle adapter (20 new Go tests), plus a regression test for the journal
  decode fix.
2026-09-06 13:26:30 +05:30
Anand 7295a28ec9 Opt-in SSO single logout, nice chart scales, storage usage donut, topology export/snap, disk SMART + temperature
- OIDC: RP-Initiated Logout is now opt-in (default off) via a new
  'single_logout' setting, with the exact post-logout redirect URL shown
  in Settings for the admin to register at their provider. Fixes the
  regression from last time: enabling it unconditionally broke sign-out
  for anyone whose IdP hadn't been told to trust the redirect yet
  (Keycloak's invalid_redirect_uri, browser stuck on a stale page).
- formatBytes shows up to 2 decimals (was an adaptive 0-or-1 rule); every
  bytes/rate chart now computes a rounded 'nice' axis scale (0/5/10/15/20
  GB, the standard Heckbert algorithm) and locks every tick + the tooltip
  to one consistent unit derived from the axis's own max.
- Storage page: the capacity donut is now sized by used bytes + a free
  remainder instead of by total capacity share, which was always 100%
  the moment there was only one pool — completely disconnected from the
  "224 GB used" text next to it.
- Topology: Export SVG (fits the full diagram regardless of current pan/
  zoom) and a Snap-to-grid toggle.
- New Disks tab on the node detail page: every physical disk with model/
  serial/size/type, PASSED/FAILED health, SSD/NVMe wearout %, and a
  per-drive temperature read from SMART, plus a full SMART attribute
  table per disk. Backed by new /nodes/{node}/disks and
  /nodes/{node}/disks/smart endpoints.
- CPU/GPU temperature is not exposed by Proxmox's own API (no built-in
  lm-sensors/nvidia-smi integration) and isn't something this can add
  without a node-side agent Proxmox doesn't ship — disk temperature via
  SMART is the thermal data actually available.
2026-09-04 00:22:53 +05:30
Anand d04b769670 Fix storage metrics double-counting, sign-out, SMTP plaintext, and OIDC gaps
- Fleet Overview's Storage KPI and per-connection totals (internal/api/
  overview.go) were summing shared storage once per node that mounts it,
  since PVE's own Shared flag is unreliable for storage added per-node
  without ticking it. New dedupeSharedStorage collapses those by an exact
  (name, total, used) match within a connection; local-only plugin types
  are never touched. Mirrored on the Storage page, which also now
  disambiguates same-named local pools (e.g. every node's "local-lvm") by
  node so a bar chart's category axis never gets duplicate labels.
- Sign-out: React Query keeps a query's last-successful data through a
  failed refetch, so the cached signed-in user survived the post-logout
  401 and the app never noticed. signOut() now pins ["auth","me"] to null
  directly instead of invalidating-and-hoping a refetch lands in time.
- SMTP: net/smtp.SendMail opportunistically attempts STARTTLS whenever the
  server advertises it regardless of the admin's "Use STARTTLS" toggle, so
  an internal relay with a bad cert failed sends the admin explicitly asked
  to be plaintext. A dedicated sendPlain path never attempts TLS.
- OIDC: added an "Auto-create new accounts" toggle — when off, a
  first-time SSO login for an unrecognized identity is refused instead of
  silently provisioning one. Also added RP-Initiated Logout: signing out of
  an SSO session now also ends the session at the identity provider when it
  advertises support for it (end_session_endpoint), via a stored id_token
  on OIDC sessions (new sessions.oidc_id_token column).
- ResourceAreaChart tooltips now fall back to the same yTickFormatter given
  to the axis when a series has no formatter of its own — fixes raw
  unformatted numbers in Fleet Trend and every RRD chart built on it
  (Node/Guest detail) that only ever set the axis formatter.
2026-09-03 23:17:38 +05:30
Anand 61ee806af6 Add realtime polling defaults, Gotify/SMTP notifications, UI-configurable OIDC, and expanded admin settings
- Default all queries to a 20s poll + refetch-on-focus (main.tsx) instead of
  a per-page opt-in, so every page/widget stays live without manual tuning.
- New internal/notify package: Gotify and SMTP (stdlib net/smtp, STARTTLS
  and implicit-TLS-on-465) notifications, each independently optional. Fires
  from the alert evaluator on new alert triggers; admin-configurable from
  Settings with a send-test-notification action per channel.
- OIDC/SSO moved from config.yaml-only to a DB-backed, admin-editable
  Settings card — swaps the live client with no restart. config.yaml is
  used to seed the database once on first boot after upgrading.
- New Security settings: session TTL, login lockout policy, and a real
  "require 2FA for admins" enforcement (requireTOTPEnrolled middleware)
  that blocks non-enrolled admins from everything but /profile and logout.
- New org-wide default preferences (theme/accent/look/landing page) for
  brand-new accounts, plus a personal landing-page picker and an
  email-me-alerts opt-in on Profile.
- Storage page: separate Local vs Shared/External storage tables and
  capacity donuts, fixing shared-storage totals that were being summed once
  per node that mounts them (e.g. a 2TB NFS share on 4 nodes read as 8TB).
- RankedBarChart: stop the longest bar's value label wrapping onto two
  lines (recharts auto-wraps LabelList when space is tight).
2026-09-03 22:00:41 +05:30
Anand 9ab6b4340a Initial commit 2026-09-03 08:36:04 +05:30