11 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 4ae41a6f1d Add public docs/demo site, fix logout getting stuck on loading
- docs/: GitHub Pages site (landing page, docs, live click-through demo)
  served straight from /docs, no build step.
- web-demo/: source for the demo (a copy of web/ with its API layer mocked
  in-memory) — see web-demo/README.md to rebuild docs/demo after a change.
- web/src/lib/auth.tsx: signOut() no longer clears the whole query cache;
  wiping ["auth","setup-status"] forced it to refetch and re-armed
  `loading`, stranding the user on a loading/failed screen instead of the
  Login page. Only non-auth queries are dropped now.
- .gitignore: exclude web-demo/node_modules and web-demo/dist (build output,
  already published as docs/demo).
- Drop PRODUCT.md (internal planning brief, not meant for the public repo).
- README: link the new docs/demo site.
2026-09-12 15:22:55 +05:30
Anand f47c60ce68 Refresh screenshots against a richer, multi-connection fleet
The old screenshots (prod-cluster, 3 nodes, 16 guests) predated Cluster &
SDN, AI Assistant, Bulk Operations, Webhooks, and the accent-color/look
presets added since — none of it showed up in the README. Recaptured every
fleet-data screenshot (dashboard, inventory, topology, storage, backups,
HA, firewall, connections) against a richer fleet: 3 connections (two
clusters, one standalone host), 6 nodes, 26 VMs/LXCs, Ceph + NFS storage.
settings-appearance also refreshed (now shows the amber/rose/teal accents).
setup.png is untouched — it's fleet-data-independent and regenerating it
would mean resetting a real admin account.

Updated the screenshots section caption to match the new fleet size.
2026-09-10 20:37:18 +05:30
Anand d63c284fbf Fix build errors and a batch of UI/data bugs found in review
Frontend build:
- combobox.tsx: DropdownMenuContent doesn't expose onOpenAutoFocus (Radix
  Popper primitive, unlike Dialog/Popover) — tsc failed. Focus the search
  input from an effect on `open` instead.

Backend data bugs (JSON parsing / validation):
- pve.JournalEntry: "n" is sometimes sent quoted by PVE ("n":"1"), which
  failed the whole unmarshal and blanked the node Journal tab. Decoded
  loosely, same as the existing cpuinfo.mhz string/number quirk.
- pve.ClusterConfigNode: pve_addr was typed int but PVE always sends a
  string (an IP) — populated it 502'd Cluster & SDN > Members. Fixed to
  string.
- ClusterPage: PVE also errors /cluster/config/nodes outright on a
  standalone (non-clustered) node — that hit the same 502 error card
  instead of the existing "not part of a cluster" message.
- ai_providers.go: updateAIProvider rejected the built-in Needle 2
  provider's "needle://local" baseUrl on every save (missing the
  needle.IsBuiltin bypass createAIProvider already had), so it could only
  ever be disabled, never edited. Deleting it only lasted until the next
  restart (the seeder always re-created it) — now tracked via a
  "dismissed" flag in the settings table so a delete sticks.

UI polish:
- Added amber/rose/teal accent color presets (theme.tsx, AppearanceCard,
  index.css, backend whitelist).
- StatusDot's glow was clipped on one side wherever it sat inside a
  `truncate` (overflow-hidden) flex row — moved truncation to just the
  text sibling (ConnectionsPage, AlertActivityWidget).
- ResourceAreaChart: a wide Y-axis tick ("47.68 MB/s") wraps onto two
  lines in recharts, and the chart only had 8px of top margin — the first
  line rendered off the top edge. Widened the axis gutter and margin.
- AlertActivityWidget required *both* of two independent queries to fail
  before showing an error, so one broken endpoint alone rendered stale
  counts instead of the error state.
- DonutChart: the hover tooltip followed the cursor by default, which on
  a compact ring collided with the centered value/label text. Pinned it
  below the ring instead.
- NodeDetailPage storage rows: a variable number of badges before the
  usage bar made every row's bar start at a different x. Switched to a
  fixed-width grid, same pattern already used for two other bar lists.
- RunningTasksWidget: a failed task's PVE status is a full sentence, not
  a short word — stuffing it into a badge blew the row's layout up.
  Collapsed to "Failed" with the full text as a hover title.
- Alert value/threshold formatting assumed every metric is a 0-100
  percent; three built-in alerts (cert_expiry/connection_stale in days,
  storage_orphan_disk in bytes) printed nonsense like "67108864.0%".
  Added a shared formatAlertValue() and used it everywhere alert
  instances are rendered (AlertsPage, OverviewPage, NotificationBell).

README: added a Features section summarizing the fleet management,
automation, integration, and access-control capabilities.
2026-09-10 19:40:00 +05:30
Anand ad43bec538 Merge PBS remotes + cross-cluster migration (backend) and UI modernization pass (frontend)
- internal/pbs: PBS client, datastores/namespaces/snapshots/prune/GC/sync/verify jobs
- connections: type column (pve|pbs), PBS-aware client resolution
- pve/guests: RemoteMigrateGuest + POST .../remote-migrate endpoint for true cross-cluster live migration
- web: shared ListSearch/Textarea primitives, DataTable search modernized
2026-09-10 11:31:57 +05:30
Anand 9b3d093eb8 Expand Needle 2 tool catalog, fix diagnostics, and publish Docker images to GHCR
- Add 48 new MCP/AI-assistant tools covering guest lifecycle, node
  operations, firewall/security, and backup/replication/HA/storage/SDN
  management. Every mutating tool is admin-gated the same way
  guest_power_action already is; migrate/resize/move-disk, node
  reboot/shutdown, disk wipe, cert revocation, and cluster-node removal are
  deliberately left out as being as destructive as a delete.
- ai_chat.go: when a provider (chiefly Needle, a pure tool-router with no
  narrative output of its own) finishes calling tools but returns nothing to
  say, render the tool results themselves as the answer instead of the
  misleading "ran out of tool calls" message.
- needle.go: serialize every request against the shared Needle subprocess
  (it handles one request at a time) to stop concurrent callers from racing
  it, and surface the subprocess's captured output when a request fails
  because it died mid-response, instead of a bare network error.
- Dockerfile: switch the final stage from distroless "static" to "base" —
  the bundled Needle CLI is a dynamically-linked glibc binary and cannot run
  in an image with no libc at all.
- .github/workflows/release.yml: build and push a multi-arch (amd64/arm64)
  Docker image to ghcr.io on every version tag, tagged with the version and
  "latest".
- Dockerfile/README: add OCI image labels and document the published GHCR
  image as the primary Docker install path.
2026-09-09 23:03:54 +05:30
Anand 846cb8ce63 Bundle Needle 2 as a built-in, default AI assistant
Ships the official Needle 2 CLI binary (Apache-2.0) baked into the ferrum
binary itself for windows/amd64, linux/amd64, linux/arm64, and
darwin/arm64 via go:embed behind per-platform build tags. No download,
no FERRUM_NEEDLE_BIN, no manual provider setup on those platforms.

- internal/needle: resolveBinPath prefers an explicit FERRUM_NEEDLE_BIN,
  otherwise extracts the embedded binary to a cache file on first use.
- Fixed a port collision: Needle's --serve defaulted to :8080, the same
  default as ferrum's own server; it now runs on a dedicated port.
- Fixed the real 'assistant times out' bug: Needle's tool-retrieval does
  a one-time embedding pass on its first request once the tool catalog
  exceeds 5 tools (ferrum declares 10), which routinely took longer than
  the old readiness check's 500ms-per-attempt retry loop allowed. Each
  cancelled attempt kept occupying Needle's single-threaded request
  loop, so retries piled up and never let a real response through.
  Replaced with two phases: cheap/retryable raw TCP dials until the
  socket accepts a connection, then exactly one real request allowed to
  run for the full startup budget.
  Subprocess stdout/stderr are now captured so a future failure surfaces
  a real reason instead of a bare timeout.
- seedBuiltinNeedleProvider now runs on every startup (not just fresh
  installs), idempotently upserting the built-in provider and always
  reasserting its model as the global default AI assistant.
- .gitignore: carved out an exception for the bundled Windows binary,
  which the blanket *.exe rule would otherwise have silently excluded.
2026-09-06 20:26:00 +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 d9880f4faa Fix CI build and untrack local-only files
- ci.yml: build the frontend before go vet/build/test — web/embed.go's
  //go:embed all:dist has no dist/ to embed otherwise (dist/ is gitignored,
  not committed), which was failing every CI run with "pattern all:dist:
  no matching files found".
- Untrack Claude-Account-1.cmd (personal local launcher script, already
  gitignored — file stays on disk, just stops being pushed).
- README: rework the Screenshots section into a proper 2-column HTML
  table gallery with clickable thumbnails, instead of ten full-width
  images stacked one after another.
2026-09-03 13:57:37 +05:30
Anand 7d3a1fa134 Add git hygiene, screenshots, and cross-platform deployment tooling
- .gitignore/.dockerignore: cover Go build artifacts, env files, logs,
  editor/OS cruft, and local runtime data (sqlite db/secret, config.yaml).
- README: add a Screenshots section (captured against a mock Proxmox
  cluster) and a Deploying a release build section.
- LICENSE: MIT.

Deployable binaries:
- cmd/ferrum: -version flag with build-time version/commit/date via
  -ldflags; -log-file flag (Windows services don't capture stdout/stderr
  the way systemd does); native Windows Service Control Manager support
  (service_windows.go) so ferrum.exe manages its own start/stop lifecycle
  under a Windows service, same graceful-shutdown path SIGTERM already used
  on Linux.
- packaging/systemd/ferrum.service: hardened systemd unit.
- scripts/build.sh, build.ps1: cross-compile linux/windows/darwin x
  amd64/arm64, package as .tar.gz/.zip with an install script and
  checksums.txt.
- scripts/linux/install.sh, uninstall.sh: create a dedicated system user,
  install the binary, seed /etc/ferrum/config.yaml, enable + start the
  systemd service.
- scripts/get.sh: one-line curl-pipeable installer (get.docker.com style)
  that resolves the latest release, verifies its checksum, and hands off
  to install.sh.
- scripts/windows/install-service.ps1, uninstall-service.ps1: register/
  remove the self-managing Windows service.
- .github/workflows/release.yml: publish all platform archives + checksums
  as GitHub Release assets on a vX.Y.Z tag push.
- .github/workflows/ci.yml: go vet/build/test, frontend lint/test/build,
  and shell-script syntax checks on push/PR.
2026-09-03 13:38:12 +05:30
Anand 9ab6b4340a Initial commit 2026-09-03 08:36:04 +05:30