Commit graph

5153 commits

Author SHA1 Message Date
rcourtman
fae27b3a8f Fix first-run bootstrap token guidance 2026-04-19 10:33:11 +01:00
rcourtman
329f59eddb Add health-state indicators to Infrastructure, Storage, Workloads, and Recovery summaries
Summary cards now show degraded/alerting/failing counts rather than raw
online/offline splits, giving operators an at-a-glance health posture without
drilling into the full resource list.

- InfrastructureSummary/infrastructureSummaryModel: adds degraded and alerting
  counts derived from resource statuses and active alerts
- StorageSummary/StoragePageSummary/useStoragePageSummary: adds poolsDegraded
  and disksFailing indicators, shows "all healthy" when zero degradation
- WorkloadsSummary/useDashboardWorkloadDerivedState: adds alerting guest count
  from activeAlerts accessor, included in summary header counts
- RecoverySummary: adds aggregate health-state summary row
- useDashboardState: threads alertsEnabled through workload derived state
- Fix monitored-system cap test helper to use TierEnterprise so limits are
  honored (self-hosted tiers are now uncapped per the v6 product model)
- Update registry and governance test snapshots to include
  useStoragePageSummary.test.ts in the storage-product-surface proof set
2026-04-19 08:35:45 +01:00
rcourtman
edb732074f Route pro-feature paywall keys to billing plan page for self-hosted instances
The purchase-start handoff (/auth/license-purchase-start) requires a configured
PublicURL and fails silently on local self-hosted instances without one. Routes
ai_alerts, ai_autofix, relay, rbac, audit_logging, advanced_sso,
agent_profiles, long_term_metrics, and trial_expired to the self-hosted billing
plan page so upgrade CTAs remain functional on local instances.
2026-04-19 00:33:02 +01:00
rcourtman
85a2dd06d4 Remove hasAIAlertsFeature and runtimeCapabilitiesLoading from alert surfaces
These props were passed through the entire alerts component tree but are no
longer needed — AI alert gating is handled by the shared license/entitlements
layer without each surface needing its own capabilities fetch. Removes the
onMount capabilities load, the paywall-viewed tracking effect, and the
dependent prop chain through OverviewTab, HistoryTab, and all alert card
surfaces.
2026-04-19 00:31:59 +01:00
rcourtman
fa2768ae78 Align test suite with single-route infrastructure architecture
Updates all test files broken by the collapse of infrastructure sub-routes
(/install, /platforms, /connections) into one /settings/infrastructure entry.
Key contract deltas:

- settingsNavCatalog/Model: infrastructure-connections and infrastructure-install
  tabs removed; infrastructure-systems is the single sidebar entry
- infrastructureWorkspaceModel: getInfrastructureWorkspaceViewFromPath renamed
  to deriveAddStepFromLegacyPath; buildInfrastructureWorkspacePath now always
  returns /settings/infrastructure regardless of argument
- settingsNavigationModel: deriveAgentFromPath and isProxmoxSettingsPath use
  normalised (not canonical) path so Proxmox deep links still resolve to the
  correct agent before the canonical-path redirect fires
- useSettingsNavigation: Proxmox agent sync moved before the redirect early-
  return so deep links seed the correct selectedAgent
- InfrastructureInstallerSection: "Open Platform connections" button replaced
  with informational text; navigate target updated to flat workspace path
- useInfrastructureDiscoveryRuntimeState: currentTab parameter removed (tab
  collapse means discovery is always active when the workspace is mounted)
- SetupCompletionPanel: unified INFRASTRUCTURE_PATH constant replaces the two
  distinct INFRASTRUCTURE_INSTALL_PATH / INFRASTRUCTURE_PLATFORMS_PATH
- All navigation call sites (Dashboard, DashboardStateCards, Infrastructure
  empty-state) updated to /settings/infrastructure
- Contract updates: agent-lifecycle, api-contracts, frontend-primitives
2026-04-19 00:29:38 +01:00
rcourtman
9bac3f421d Fix agent-token fallback to reject cross-org tokens and update security contract test
Two test regressions introduced when agent-report tokens were allowed as
fallback auth for /api/auto-register:

1. Org mismatch was not checked: a token belonging to org-a could authenticate
   a request whose context carried org-b. Add an explicit org consistency check
   before setting authenticated=true in the fallback path.

2. The security regression test assumed only setup tokens could authenticate
   auto-register. That contract has intentionally changed: agent-report tokens
   can now authenticate but are restricted to updating existing nodes (403 for
   new-node attempts). Update the test to assert the actual security boundary.
2026-04-18 23:10:50 +01:00
rcourtman
264c9377a2 Remove infrastructure setup modals 2026-04-18 22:58:17 +01:00
rcourtman
688bdd4246 Fix PVE connection health key in registration check
The isKnownDisconnected helper was building the key as
instanceType+"-"+instanceName ("pve-delly"), but the PVE
PollProvider's connectionKey function returns the bare instance
name ("delly"). PBS uses "pbs-"+name. The mismatch meant the
disconnected-node check always missed, rendering the server-side
stale-token detection inert.

Fix: use type-specific key construction matching the PollProvider
connectionKey implementations.
2026-04-18 22:37:24 +01:00
rcourtman
40a25f82d1 Add periodic Proxmox registration health check loop
The cold-startup race: if Pulse and the agent restart together, the
monitor has no connection-health data when the agent calls
checkRegistrationWithPulse at startup. The server defaults to
registered=true (no known-disconnected entry), so the agent skips
re-registration even though the token is stale. The node stays broken
until the next manual agent restart.

Fix: after the initial runProxmoxSetup call, start a background goroutine
that waits 2 minutes (giving the monitor time to poll PVE and record
failure state), then rechecks every 5 minutes via RunHealthCheck.

RunHealthCheck only acts on types that have a local registration marker.
Types without a marker are skipped to prevent uncontrolled token rotation
when Pulse is temporarily unreachable — those need a full startup setup
cycle via RunAll.

Together with the two earlier commits this closes all three stale-token
scenarios: install-time 401, long-running stale state, and cold-startup
race.
2026-04-18 22:25:18 +01:00
rcourtman
b850e4fb2c Rework infrastructure setup flows as centered modals 2026-04-18 22:16:13 +01:00
rcourtman
501c61b82f Fix PVE stale token self-healing after failed registration
Two gaps in the existing flow allowed a disconnected PVE node to stay
broken indefinitely even after the agent restarted:

1. Server-side: autoRegisteredNodeExists checked only that a PVE/PBS
   instance existed in the config, not whether its connection was
   healthy. A node with a stale token would return registered=true on
   every check, causing the agent to skip re-registration forever.
   Fixed: also consult GetConnectionStatuses(); return registered=false
   when the monitor has a definitive disconnected entry so the agent can
   rotate and re-register.

2. Agent-side: the type-specific registration marker was cleared only on
   success. If rotation succeeded but the Pulse update failed (e.g.
   transient network error), the old marker from a previous successful
   registration persisted, leaving next-startup to skip setup again.
   Fixed: clear the marker before entering the token setup/rotation
   phase so any failure leaves the system in a retriable state.

Together these two fixes make the stale-token scenario self-healing:
the monitor detects the broken connection, the next agent startup sees
registered=false, clears its marker, rotates the token, and updates
Pulse — without manual intervention.
2026-04-18 22:07:30 +01:00
rcourtman
b0b790cf55 Fix PVE token re-registration after agent reinstall
When the agent is reinstalled on a Proxmox host, it rotates the PVE API
token in Proxmox but the Pulse server's /api/setup-script-url endpoint
requires settings:write scope — agent tokens only have agent:report — so
the 401 aborted the update, leaving Pulse with a stale token and a
disconnected PVE node.

Three-part fix:
- server: accept agent API tokens on /api/auto-register for updating
  existing nodes (new nodes still require setup-token auth)
- agent: fall through instead of aborting when setup token fetch returns
  4xx; send X-API-Token header so the server can authenticate via the
  agent token instead
- update: allow HTTP auto-update URLs for RFC 1918 private network
  addresses (LAN installs without HTTPS no longer block auto-update)
2026-04-18 21:44:42 +01:00
rcourtman
f754dfa9b9 Redesign infrastructure settings around one ledger 2026-04-18 21:33:37 +01:00
rcourtman
1c2b195d51 Collapse infrastructure settings to one sidebar entry 2026-04-18 21:10:01 +01:00
rcourtman
295753be1a Move infrastructure IA split into settings shell 2026-04-18 20:45:30 +01:00
rcourtman
8f133b1be1 Fix agent infinite update loop on dev builds
The /api/agent/version endpoint was returning the git-describe version
(e.g. 6.0.0-rc.2+git.58.g53a9339.dirty) for dev builds, which is always
semantically newer than the binary's embedded version (v6.0.0-rc.1).
This caused agents to loop: check version → see "newer" → self-update →
restart → still same binary version → loop again.

The agent's guard ("skip if server reports 'dev'") only fires for the
literal string "dev". Fix: return "dev" whenever IsDevelopment is true,
which covers all git-built/dirty dev instances.
2026-04-18 20:42:12 +01:00
rcourtman
53a9339e4e Add infrastructure workspace peer navigation 2026-04-18 19:09:06 +01:00
rcourtman
90c5c63ced Separate infrastructure subviews from systems ledger 2026-04-18 18:52:11 +01:00
rcourtman
d78bc64583 Recenter infrastructure settings on monitored systems 2026-04-18 18:46:25 +01:00
rcourtman
8883e36f0b Collapse infrastructure settings to ledger-first workspace 2026-04-18 17:13:34 +01:00
rcourtman
847d09d17b Unify settings infrastructure ledger 2026-04-18 14:54:53 +01:00
rcourtman
a6145510d0 Route Proxmox picker choices to kind-scoped platforms URL
Picking PVE, PBS, or PMG from the Add System picker navigated to the
bare /settings/infrastructure/platforms/proxmox URL and used an
onSelectAgent state callback to choose the sub-tab. The URL was the same
for all three kinds, so the Proxmox sub-tab chrome (driven by the URL)
stayed on Virtual Environment regardless of the user's choice, making
PVE/PMG landings pixel-identical and PBS show mismatched header/data.

Route directly to the canonical deep link
/settings/infrastructure/platforms/proxmox/{pve|pbs|pmg} so the URL,
sub-tab selection, and data panel always agree.
2026-04-18 13:15:46 +01:00
rcourtman
b666d131b0 Migrate Docker integrations to maintained Moby modules 2026-04-18 11:40:38 +01:00
rcourtman
35429774b4 Probe /api/health in hot-dev backend monitor
The supervisor's backend health monitor in scripts/hot-dev.sh only
checked whether a `./pulse` process existed via pgrep. If the Go binary
hung, panicked into recovery without exiting, or failed to bind :7655
while the process stayed alive, the monitor saw a healthy process count
and never restarted, leaving the dev frontend talking to a dead listener.
Hit that twice in one session.

Add an HTTP probe of /api/health on the dev port. After two consecutive
5s polls where the process exists but /api/health is unreachable, kill
the process and restart the same way the dead/duplicate branches do.
Cut the poll interval from 10s to 5s so the worst-case unresponsive
window is ~10s instead of ~30s. Bring deployment-installability into
line by naming alive-but-unresponsive recovery as part of the dev-runtime
orchestration boundary, and cover the new branch with static-source
assertions in scripts/tests/test-hot-dev-bg.sh.
2026-04-18 10:56:33 +01:00
rcourtman
e6b0d47bd6 Gate Docker mutations on authz-plugin posture 2026-04-18 10:45:25 +01:00
rcourtman
8d0aec6e4d Add back-to-inventory header on infrastructure subviews
The connections-table flip in 00c6dc2dd removed the workspace's top-of-
page subtab strip but left the install panel and platforms workspace
rendering bare with no path back to the inventory table. Operators who
clicked Add a system, picked a platform, and landed on
/settings/infrastructure/install or /settings/infrastructure/platforms/*
had to leave Settings entirely and return to the tab to get back to the
table.

Render a persistent breadcrumb header above every non-inventory view
inside InfrastructureWorkspace: a "← Connections and Inventory" button
that navigates back to the bare /settings/infrastructure route, plus a
heading naming the current subview. Inventory itself is unchanged.

Bring agent-lifecycle rule 7 into line by requiring this back-to-
inventory affordance on every detail route under /settings/infrastructure
so deeper surfaces are never reachable without a visible return path.
Cover the new header on install and platforms subviews — and its absence
on the inventory landing — in InfrastructureWorkspace.test.tsx.
2026-04-18 10:43:38 +01:00
rcourtman
2dc4e43033 Wire Connections & Inventory nav to the unified table
settingsTabPath('infrastructure-operations') and the initial-tab resolver
still returned /settings/infrastructure/install from the pre-flip
workspace, so clicking the Settings nav dropped users on the install
detail page instead of the unified Connections table shipped in
00c6dc2dd. Point the tab path and canonical resolver at the bare
/settings/infrastructure, extend deriveTabFromPath to recognise that
prefix as the infrastructure-operations tab, and bring
frontend-primitives rule 13 into line with the already-flipped
agent-lifecycle rule 7 so both contracts name the bare
/settings/infrastructure as the canonical landing. Update the three
guardrail suites (settingsRouting, settingsNavigation.integration,
settingsArchitecture) that encode the tab->path map so they reflect the
new landing.
2026-04-18 10:27:04 +01:00
rcourtman
fb7b9b01fc Update pinned Go toolchain to 1.25.9 2026-04-18 10:04:34 +01:00
rcourtman
f0b37dbfe1 Document remediation execute approval contract 2026-04-18 09:57:02 +01:00
rcourtman
5ebb3d9952 Harden session and setup token auth flows 2026-04-18 00:06:50 +01:00
rcourtman
00c6dc2dd4 Collapse infrastructure workspace onto one connections table
Replace the three-subtab install/connect/inventory narrative with a
single unified connections view at /settings/infrastructure. The base
route now renders one alpha-sorted table whose rows cover every
monitored system — Proxmox VE, PBS, PMG, TrueNAS, VMware, and agent
hosts — under the same name/kind/method/status/last-reported shape.
Adding a new system goes through a single "Add a system" picker whose
tiles route operators straight into the right flow:
/settings/infrastructure/install for the agent choice and
/settings/infrastructure/platforms/<kind> for Proxmox, TrueNAS, and
VMware. The install, platforms, and operations routes remain reachable
as detail surfaces, and read-only sessions continue to redirect the
install view back to the inventory base and suppress the add-system
entry point.

Update the agent-lifecycle subsystem contract to reflect that the
workspace shell no longer mandates a "bare routes default to install"
first-host narrative or a three-subtab layout. Refresh the guardrail
and integration tests that pinned the old tab shape and the legacy
operations landing route.
2026-04-18 00:00:34 +01:00
rcourtman
49c8c84387 Add system-picker dialog for target-first onboarding
The picker presents the six target kinds Pulse knows how to connect
(Proxmox VE, PBS, PMG, TrueNAS, VMware, agent host) as a single list
with a short description and a method badge, so operators choose what
they are connecting rather than which Pulse workflow to enter. The
component stays method-agnostic: the caller decides what to do with
the selection in a follow-up commit that wires it into the workspace
shell.
2026-04-17 23:48:02 +01:00
rcourtman
1d66d62c3e Add unified connections table primitive
Introduce a single row shape that spans PVE, PBS, PMG, TrueNAS, VMware,
and agent-host systems so the infrastructure workspace can surface every
monitored system as one alpha-sorted table instead of splitting systems
across method-specific subtabs. The model maps per-provider status
semantics into a shared reporting/pending/offline/error/unknown vocabulary
and carries a canonical last-reported timestamp for consistent relative
rendering. The component is not wired into any route yet; it lands as a
library piece so the workspace shell can consume it in a follow-up commit.
2026-04-17 23:45:07 +01:00
rcourtman
280b3dd5f7 Distribute infrastructure full-width columns 2026-04-17 23:39:19 +01:00
rcourtman
446a70ee69 Trim infrastructure workspace onboarding papercuts
Rename the third subtab to "Inventory" and hide the first-system
orientation card once any platform connection or agent resource exists.
In the installer, drop the two cross-pointer callouts, replace the
locked-state placeholder with a disabled command preview, collapse the
uninstall block into a details disclosure, and keep the Platform
connections CTA as an inline link next to the target-profile note.
Remove the dead CalloutCard branch from ProxmoxSettingsPanel and update
the architecture guardrails accordingly. Pin the "Inventory" label and
the orientation-card gating into the agent-lifecycle contract so the
workspace-shell rule stays aligned with the surfaced narrative.
2026-04-17 23:37:15 +01:00
rcourtman
beb143aade Fix missing utility navigation icons 2026-04-17 23:17:38 +01:00
rcourtman
0721669067 Surface container runtime as a chip next to the image name
Add a compact Docker/Podman/TrueNAS runtime chip beside the image in the
Workloads image column. The Type badge stays a kind taxonomy (Container);
runtime is a separate axis and now glance-readable without hovering the
badge tooltip.
2026-04-17 23:05:13 +01:00
rcourtman
5d75d562f6 Collapse OCI as a workload kind and unify group-header labels
OCI is a flavor of system-container, not a distinct workload kind, but
WorkloadTypePresentationKey had 'oci-container' with its own purple
badge — creating two presentation paths for the same thing. Group
headers in workloadSelectors had inline label strings
('App Containers', 'Container', 'Pods', 'VM') that drifted from the
row badge's PRESENTATION_MAP whenever either side changed.

Drop 'oci-container' from the workload-kind union and presentation
map; normalize 'oci-container' input to 'system-container'. Add
pluralLabel to WorkloadTypePresentation and route getWorkloadGroupLabel
through it so the row badge and group header share one source. Collapse
the matching branch in recoveryItemTypePresentation. Align the relevant
assertion tests with the canonical VM/LXC/Container/Pod labels.
2026-04-17 22:49:28 +01:00
rcourtman
0756ae9279 Forward real Proxmox OCI metadata into WorkloadGuest
useWorkloads.mapResourceToWorkload was hardcoding isOci:false and
osTemplate:undefined for every resource, so the OCI-specific branch
in useGuestRowState.typeInfo was unreachable. Backend already sends
isOci and osTemplate on resource.proxmox; the local APIResource shape
just didn't declare them.

Add isOci/osTemplate to ResourceProxmoxMeta and the local APIResource
proxmox shape, then forward the real values for system-container rows.
Simplify typeInfo to a single getWorkloadTypeBadge call with the OCI
tooltip carried via the title override, and update the GuestRow OCI
test to exercise the new flow end-to-end.
2026-04-17 22:48:51 +01:00
rcourtman
ba9589f091 Fix hot-dev mock mode env contract 2026-04-17 20:49:36 +01:00
rcourtman
00da144dd8 Remove v6 RC banner from app shell 2026-04-17 20:06:57 +01:00
rcourtman
4e4fcb5dbe Move operations tools into settings support 2026-04-17 20:03:54 +01:00
rcourtman
3a5f36da19 Retain tab data across recovery and patrol revisits 2026-04-17 19:38:43 +01:00
rcourtman
deb701e340 Align patrol page shell with utility header stack 2026-04-17 19:11:53 +01:00
rcourtman
2d0784ca61 Stabilize recovery and patrol tab loading shells 2026-04-17 19:10:06 +01:00
rcourtman
df06fe84b2 Normalize alerts and patrol page headers 2026-04-17 18:47:02 +01:00
rcourtman
50a7d73293 Add workloads column layout regression 2026-04-17 18:35:34 +01:00
rcourtman
2f8a91d7a6 Rebalance runtime table columns 2026-04-17 18:10:00 +01:00
rcourtman
14b092ce9e frontend: drop PageHeader from Dashboard/Infrastructure/Storage/Ceph
The title-only header still cost vertical space without adding signal.
These surfaces already identify themselves via the left nav tab state,
so the in-page heading was pure chrome. Remove the PageHeader import
and element on each of the four pages that still carried it.

Patrol retains its header because it hosts active controls; Alerts
retains SectionHeader because it orients between sibling tabs.
2026-04-17 18:03:14 +01:00
rcourtman
393521f5eb Strip explanatory subtitles from Ceph, Patrol, and Alerts headers
Follow-up to fc019780c. Ceph and the Patrol intelligence header carried
the same "let me narrate what this page does" blurbs under their titles;
Alerts routed five tab-specific descriptions through
getAlertsPageHeaderMeta().

Removed the Ceph and Patrol description props, dropped the description
field from each entry in getAlertsPageHeaderMeta() (and the six unused
ALERTS_PAGE_*_DESCRIPTION constants), and pared the Alerts SectionHeader
usage to title-only. Test updated to match.
2026-04-17 17:57:32 +01:00