Commit Graph

5047 Commits

Author SHA1 Message Date
rcourtman bf1d7d4e09 Rename "telemetry" to "metrics" in agent-facing copy
The word "telemetry" reads as vendor phone-home (especially since Pulse
has a separate, clearly-labelled "Anonymous outbound telemetry" system
on the server for usage pings). What the agent actually does is collect
host-level metrics that flow to the user's own Pulse deployment, with
no vendor endpoint.

Updated in three places to match:

- Explainer subhead and always-on chip ("Hardware metrics",
  "host-level metrics").
- Add-connection Unified Agent section ("Host-level metrics on
  Proxmox...").
- Matching assertion in ConnectionsExplainer.test.tsx plus a guard in
  ConnectionEditor.test.tsx that the old "host-level telemetry" copy
  does not come back.

Also:

- Strip the em dash from the Add-connection workspace subheader.
- Drop the redundant inner "Add a connection" heading now that the
  outer card already titles it "Add connection".
2026-04-20 10:01:53 +01:00
rcourtman 2e1e682f70 Carry the Platform API / Unified Agent vocabulary into the add flow
The explainer at the top of the ledger teaches two paths (Platform API
and Pulse Unified Agent), but clicking Add dropped that vocabulary: the
user landed on a generic address box with no label and could only reach
the agent by clicking "Enter credentials manually" and picking it from
a flat list of six raw-token types.

Reshape the pre-credential step into two labeled sections that mirror
the explainer:

- "Platform API" section wraps the probe input and the manual-type
  fallback (now filtered to the five API types; heading reads "Choose
  Platform API type manually").
- "Pulse Unified Agent" section with a direct "Install the Unified
  Agent on a host" button, tinted to match the explainer's agent card.

Also aligns the CONNECTION_TYPE_LABELS entry for agent to "Pulse Unified
Agent" so the header on the install surface matches the brand used in
the ledger and the explainer.
2026-04-20 09:47:32 +01:00
rcourtman da23b5fb9e Sync Pulse Account specs with governed lane 2026-04-20 09:42:22 +01:00
rcourtman c28aa6ab83 Clarify opt-in nature of agent commands in explainer
Split agent capability chips into two labeled groups: "Always on"
(Hardware telemetry) and "Off by default, opt in per host" (Assistant
commands, Patrol remediation). Visually distinguishes the opt-in chips
with a dashed border and muted text so users can see at a glance that
command execution is a deliberate choice, not the default posture.

Also strips em dashes from copy and the one inline comment for
consistency with the rest of the product voice.
2026-04-20 09:40:31 +01:00
rcourtman f62b6f5e6d Promote Pulse Account to a governed lane 2026-04-20 09:37:15 +01:00
rcourtman 6ed6b84ef8 feat(connections): polish explainer with agent capability + trust chips
The first pass explained both modes but gave users no reason to
choose the agent when the API also covers their platform. Polish the
layout and add concrete positives grounded in what the agent actually
ships:

- Two-column split with a divider and a subtle blue-tinted accent on
  the Agent panel. Agent icon badge uses the same accent so it pairs
  visually without shouting.
- "Recommended" pill next to Pulse Unified Agent — it unlocks more
  features, so the default bias is toward installing it.
- Capability chips on the agent panel: Hardware telemetry, Assistant
  commands, Patrol remediation. These match what agentexec, hostagent,
  and ai/patrol_findings actually do at runtime.
- Trust strip of verifiable facts: Single Go binary · ~13 MB download
  · No runtime dependencies · Open source. Specific numbers beat
  vague adjectives like "lightweight" — the ~13 MB figure is the
  actual compressed size of pulse-agent-v6.0.0-rc.1-linux-amd64.tar.gz.
- Tests pin the capability labels and trust facts so the benefit copy
  can't silently drift.
2026-04-20 09:30:29 +01:00
rcourtman c0de1a4021 feat(connections): explain API vs Unified Agent above the ledger
New users landing on an empty monitored-systems ledger had no way to
know when they should connect via a platform API vs install the Pulse
Unified Agent, and no signal that the agent unlocks Assistant command
execution and Patrol remediation on the host.

- ConnectionsExplainer: dismissible (localStorage) callout above the
  table, two-mode grid with lucide glyphs (Cloud for API, Cpu for
  agent), short copy tying each mode to concrete capabilities.
- Mount in InfrastructureWorkspace only in ledger mode (hidden during
  add/edit flows so it doesn't compete for attention).
- Rename the agent type label from "Pulse agent" to "Pulse Unified
  Agent" in CONNECTION_TYPE_LABELS. Inline prose elsewhere (tooltips,
  install guides) keeps the conversational "Pulse agent" form — the
  formal branded name only lives where the ledger treats it as a
  type.
2026-04-20 09:27:02 +01:00
rcourtman cdeb257754 Finish Pulse Account workspaces shell 2026-04-20 09:25:42 +01:00
rcourtman c1e94878c5 fix(connections): tidy agent ledger rows
Two papercuts in the monitored-systems ledger:

- Agent rows stacked the hostname twice in the System column because
  the aggregator emits Name = Address = host.Hostname for agents. The
  repeated "delly / delly / Pulse agent" noise adds nothing. Suppress
  the host line whenever it equals name so the agent row becomes just
  "delly / Pulse agent", and Proxmox-style rows that still have
  distinct https://host:port URLs keep both lines.
- Coverage label for agents rendered as lowercase "host" because the
  aggregator's Surfaces slice is ["host"] (singular — one host per
  agent) but SURFACE_LABELS only mapped the plural "hosts". Add the
  singular key.
2026-04-20 09:16:42 +01:00
rcourtman cba8cd2d49 refactor(connections): drop detail page, render actions inline on ledger rows
The detail panel never earned its keep — it showed the same status, host,
and last-seen already on the row, and gated Edit / Pause / Remove behind
a navigation hop. Collapse it into row-level actions so the ledger is the
single surface for managing a monitored system:

- New useConnectionRowActions hook: per-id pending / error / confirming
  state so pause + two-click remove coexist across rows without
  component-per-row signal proliferation. Confirm window bumped to 6s.
- ConnectionsTable: inline Edit / Pause / Remove cluster per row, red
  alert row for action errors, expansion row with agent uninstall
  commands (Linux + Windows, copy-to-clipboard) during remove-confirm.
- InfrastructureWorkspace: 3-mode shape (ledger / add / edit), no
  detail selection state, pulls uninstall commands from the operations
  context and wires clipboard + notifications.
- Deleted ConnectionDetailPanel + its test; subsystem contracts
  (agent-lifecycle, frontend-primitives, api-contracts, registry)
  updated to point at useConnectionRowActions and ConnectionsTable
  as the canonical drill-in surface.

All 30 Settings tests pass; type-check clean.
2026-04-20 08:38:13 +01:00
rcourtman e6352afbef refactor(connections): drop drawer, render detail inline in workspace
Row click on the ConnectionsTable was still popping a right-side Dialog
drawer. That re-wrapped the per-type panels rather than replacing them —
"consolidation means shared fabric, not shared container."

Swap ConnectionDetailDrawer for ConnectionDetailPanel: same props, same
pause/remove handlers, but rendered as an inline bordered card instead
of a Dialog. The workspace Switch/Match gains a 'detail' mode that
shows the panel with a "Back to systems" header. Removing a connection
exits detail mode via onRemoved; editing routes through the existing
handleEditConnection.

Subsystem contracts updated to match: agent-lifecycle, frontend-primitives,
and api-contracts now name ConnectionDetailPanel.tsx (with a note that
it must not be re-wrapped as a floating Dialog); registry owned_files
and platform-connections-workspace-lifecycle-proof match_files renamed
to match the new filename.
2026-04-19 22:33:36 +01:00
rcourtman 3dbe638f70 Shrink welcome tour into compact coachmark
Refs #1429
2026-04-19 22:21:50 +01:00
rcourtman a34bfcc037 Refocus welcome tour on v5 wayfinding
Refs #1429
2026-04-19 22:05:41 +01:00
rcourtman e769c3be7f feat(connections): surface pve/pbs scope toggles in node modal
Proxmox VE and PBS nodes already carry Monitor* booleans on their config
structs, but the node modal only surfaced the advanced PVE toggles (SMART,
temperature) and none of the PBS surfaces — so save paths hardcoded every
VE/PBS collector to true. The PMG side has had a real Data-collection UI
for a while; bring PVE and PBS to parity so users can opt out of surfaces
without going through the retired "Stop this surface" dialog.

- nodeModalPresentation: add monitorVMs/Containers/Storage/Backups and
  monitorDatastores/SyncJobs/VerifyJobs/PruneJobs/GarbageJobs fields to
  the form data shape; default all true.
- useNodeModalState: populate the new fields from editingNode (matching
  the existing pmg pattern) so edit-mode reflects stored scope.
- buildNodeModalMonitoringPayload: dispatch form values through instead
  of hardcoded true, so the save body honors the toggles.
- NodeModalMonitoringSection: render Data-collection sections for pve
  and pbs mirroring the pmg section, with short per-surface copy.

Tests: extend nodeModalPresentation.test to assert scope toggles round-trip
through buildNodeModalMonitoringPayload for pve and pbs.
2026-04-19 22:03:43 +01:00
rcourtman a7ba4206fe Restore flat welcome tour styling
Refs #1429
2026-04-19 21:51:10 +01:00
rcourtman 7b2c37de94 Refine welcome tour step map
Refs #1429
2026-04-19 21:46:29 +01:00
rcourtman 5c5425aea8 Polish welcome tour layout
Refs #1429
2026-04-19 21:41:14 +01:00
rcourtman 3ff7c2f2e3 Replace v6 banner with guided welcome tour
Refs #1429
2026-04-19 21:26:54 +01:00
rcourtman 9bd5d37f6a feat(connections): wire vmware/truenas edit through ConnectionEditor
Route the unified drawer Edit button for vmware and truenas connections into
the existing credential slots in edit mode, mirroring the pve/pbs/pmg path
already landed.

- ConnectionDetailDrawer: widen EDITABLE_CONNECTION_TYPES to include vmware
  and truenas so the Edit button surfaces for those rows.
- VMware/TrueNAS CredentialSlot: accept editingConnection prop; onMount
  branches to openEditDialog(entity) when present, otherwise openCreateDialog.
- InfrastructureWorkspace: resolve aggregator IDs (vmware:<id>, truenas:<id>)
  back to the underlying VMwareConnection / TrueNASConnection via the
  respective panel state's connections() accessor, then dispatch by type.

Tests: drawer renders Edit for vmware and truenas rows and invokes onEdit
with the full connection payload.
2026-04-19 21:17:28 +01:00
rcourtman 1f1bc57e2c Route connection edit-mode into NodeCredentialSlot (pve/pbs/pmg)
The drawer had pause/remove after cc963391a, but edit still lived
only in the legacy per-type panels. This wires the drawer's new
Edit button into the existing NodeCredentialSlot via the editor's
mode="edit" path.

InfrastructureWorkspace gains an editingConnection signal and a
three-way Switch (ledger | add | edit). Edit route looks up the
underlying NodeConfigWithStatus by name from the existing pveNodes/
pbsNodes/pmgNodes accessors and hands it to NodeCredentialSlot as
editingNode — which already handles the redacted-secret convention
via useNodeModalState.

VMware + TrueNAS edit is a separate commit: their credential slots
currently call openCreateDialog() on mount and need an analogous
editingConnection branch. Agent edit has no obvious surface today
so the drawer simply omits the button for it.

Vitest adds: Edit-present for pve, Edit-absent for agent.
2026-04-19 21:09:54 +01:00
rcourtman cc963391a3 Wire pause/remove into connection detail drawer
The ledger could show connections but not act on them: pause and
remove buttons existed only as legacy per-type panels. Drawer now
exposes both, dispatching through ConnectionsAPI.setEnabled/remove
that were added in ec28bb331.

- Pause toggle hidden when capabilities.supportsPause is false
  (agents today).
- Remove uses a two-click confirm with a 4s timeout rather than a
  nested modal; clearer UX inside a drawer, cheaper to escape.
- Agent branch adds a footnote that removal stops recording but
  requires running the uninstall command on the host for full
  detach; history is retained. Surfacing the uninstall command
  inline is a follow-up.
- Errors render inline; the drawer does not close on failure so
  the user can retry or copy the message.
- Parent wires onMutated to ledger.reload() so the row state
  reflects the new enabled/presence status on the next tick.

Vitest coverage: agent-hides-pause, pause-succeeds-and-notifies,
pause-error-renders-inline, two-click remove calls API + closes.
2026-04-19 21:03:38 +01:00
rcourtman ec28bb3314 Accept aggregator semantic IDs on node mutation endpoints
The unified /api/connections aggregator emits IDs as {type}:{name}
(e.g. "pve:delly"), but the PUT/DELETE/refresh-cluster/test endpoints
only parsed the legacy {type}-{index} array-position form. That left
the new Connection surface unable to drive any mutation against the
entries it lists.

HandleUpdateNode, HandleDeleteNode, HandleRefreshClusterNodes, and
HandleTestNode now route the incoming ID through a shared
resolveNodeID helper: colon-form resolves by Name (404 on miss),
dash-form keeps the existing index semantics. Frontend connection
client gains setEnabled/remove that dispatch to the right per-type
endpoint by ID prefix.
2026-04-19 20:42:22 +01:00
rcourtman 9a8b6fb746 Add dashboard navigation migration notice
Refs #1429
2026-04-19 20:35:18 +01:00
rcourtman 89fdf2108c Restore self-hosted plan compare prompt
Refs #1429
2026-04-19 20:22:27 +01:00
rcourtman f221a71569 Probe web-UI root so PVE/PBS/PMG detection survives the 3s auth delay
/api2/json/version requires authentication, and PVE/PBS deliberately delay
401 responses by ~3s as a timing-attack mitigation. Our 3s probe budget
fired right as the 401 arrived — every probe returned zero candidates
against real targets.

Switch the PVE/PBS/PMG fingerprint path to the web-UI root (/), which
serves the login page in <100ms and carries the same identifying Server
banner (PVE/PMG) or an unambiguous HTML title (PBS, which omits the
Server header on /). Drop versionHintsFromProxmoxBody — /version was the
only caller, and we can surface version after authentication instead.

Verified end-to-end against a live PVE (delly:8006, 41ms) and live PBS
(100.106.60.119:8007, 70ms); fan-out on a bare hostname returns the
correct product.
2026-04-19 20:10:45 +01:00
rcourtman f4feccc151 Use shared self-hosted plan metadata
Refs #1409
2026-04-19 19:59:03 +01:00
rcourtman 238545b207 Normalize infrastructure onboarding route contract 2026-04-19 19:37:19 +01:00
rcourtman f8d0839806 Reorder dashboard summary before problem resources
Refs #1429
2026-04-19 19:37:19 +01:00
rcourtman 9de093725f Clarify dashboard workload and trend states
Refs #1429
2026-04-19 19:18:18 +01:00
rcourtman 29d96aec33 Reduce metrics store transaction churn
Refs #1124
2026-04-19 17:11:14 +01:00
rcourtman 960a93a86c infrastructure workspace — drop retired ignored-row drawer and stop-monitor dialog
Phase 9 deleted InfrastructureIgnoredRowDetails and
InfrastructureStopMonitoringDialog; remove the lingering imports and
JSX, collapse the manage-action switch to the single surviving
'connection' kind, stop reaching into useInfrastructureOperationsContext
for state hooks that no longer exist, and update the workspace test to
drop the now-obsolete reporting-row / ignored-row scaffolding.

Contract-neutral: no public-contract delta. The retired surfaces were
already deleted in the prior phase-9 commit; this pass only scrubs the
compile-time references they left behind.
2026-04-19 17:04:25 +01:00
rcourtman d75b10df57 phase 9 — retire stop-monitoring & legacy per-type shells
Delete the stop-monitoring dialog plus the retired per-type settings
shells now that the unified ConnectionEditor replaces them: remove
PlatformConnectionsWorkspace / ProxmoxSettingsPanel /
ProxmoxDirectWorkspace / NodeModal et al and the accompanying tests.
Strip the lingering reporting-state hook from
useInfrastructureOperationsState and the guardrail test reinforces the
removal. Reconcile registry.json, frontend-primitives.md,
api-contracts.md, agent-lifecycle.md, status.json, and the
release-control guard/test fixtures so governance audits stay clean.
2026-04-19 16:55:05 +01:00
rcourtman 5296bbf4c4 Complete Pulse Cloud runtime handoff 2026-04-19 15:42:08 +01:00
rcourtman 406b966b86 v6(settings): phase 8 — per-surface Monitor* scope for TrueNAS and VMware
Adds positive MonitorDatasets/Pools/Replication booleans to TrueNASInstance
and MonitorVMs/Hosts/Datastores to VMwareVCenterInstance, matching the
PVE/PBS/PMG scope pattern. NewInstance defaults all surfaces to true;
ApplyDefaults migrates legacy all-false records to all-true so existing
truenas.json / vmware.json on disk continue monitoring after upgrade.

The unified connections aggregator now reads those booleans into the
Scope map and flips SupportsScope to true for both types, so the Scope
UI in the ConnectionEditor is a straight wire-through to the native
config fields — no new storage or adapter layer. Per-type API clients,
form state, and the TrueNAS and VMware credential slots render the same
three-checkbox "Collection scope" panel used by PVE/PBS/PMG, replacing
the old per-type Stop-this-surface dialog end-to-end from the editor's
side.

Contracts updated: agent-lifecycle, api-contracts, storage-recovery.
Tests: truenas.test.ts and vmware.test.ts round-trip the new monitor*
flags through list + update payloads; config tests cover the legacy
all-false ApplyDefaults migration; aggregator test asserts the scope map
and SupportsScope: true for both types.

Provider Refresh paths still fetch everything in one trip; honoring
Monitor* inside VMware and TrueNAS pollers is deferred to a follow-up.
2026-04-19 15:02:18 +01:00
rcourtman bc2cf8d400 v6(settings): phase 7 — ConnectionsTable sources from the unified aggregator
Switches the configured-connections ledger in the infrastructure workspace
from the legacy `useInfrastructureReportingState`-derived rows to
`GET /api/connections`, so the table now reflects aggregator-derived state
(active/paused/unauthorized/unreachable/stale/pending) and per-connection
scope directly from the per-type config stores plus scheduler health.

New frontend surfaces:

- `useConnectionsLedger.ts` polls the unified aggregator every 15s and
  maps each `Connection` into an `InfrastructureSystemRow` with state
  badges, collection label, active-scope coverage labels, and a
  `connection` manage action.
- `ConnectionDetailDrawer.tsx` renders the aggregator fields (type,
  address, state, stateReason, enabled, surfaces with active scope,
  last seen, last error, source) when a unified row is clicked,
  replacing `InfrastructureActiveRowDetails` for configured connections.

`InfrastructureWorkspace` now composes its row list from the ledger plus
the legacy monitoring-stopped rows (still driven by
`useInfrastructureReportingState`) and no longer mounts the legacy
active-row drawer. Ignored-inventory drill-in continues to use
`InfrastructureIgnoredRowDetails` pending the phase 9 cleanup.

`SystemManageAction` gains a `connection` variant so the workspace can
differentiate unified-row clicks from monitoring-stopped rows without
conflating them through the legacy `rowKey` path.

Contracts updated: agent-lifecycle, frontend-primitives.
Tests: `InfrastructureWorkspace.test.tsx` extended with a
`connectionFixture`, asserts unified rows render with the new state
badges, and the View-details click opens the new connection-detail
drawer instead of calling `setExpandedRowKey`.
2026-04-19 14:03:07 +01:00
rcourtman 5dd6ea88cb v6(settings): phase 6b — TrueNAS and VMware credential slots inside ConnectionEditor
Extracts the inner form bodies from TrueNASSettingsPanel and
VMwareSettingsPanel into dedicated credential slot components mounted
inline under ConnectionEditor. When the user adds a TrueNAS or VMware
connection, the editor now shows a clean form instead of re-rendering
the full platform panel (which listed every other saved connection
before exposing the add dialog).

State still lives on TrueNASSettingsPanelState and
VMwareSettingsPanelState, so save, test, preview, and admission-preview
behavior remain identical. The slots prime the existing create-dialog
state on mount and close it on cancel; save completion is detected via
the state's dialogOpen transition.
2026-04-19 13:43:25 +01:00
rcourtman 4a587ba544 v6(settings): phase 6a — PVE/PBS/PMG credential slot inside ConnectionEditor
Replaces the ProxmoxSettingsPanel stopgap that rendered an entire
Proxmox workspace (discovery card, configured nodes table, node modal
stack) inside the add-connection credential slot. The add flow now
mounts NodeCredentialSlot, which drops the Dialog chrome and reuses the
NodeModalBasicInfoSection / AuthenticationSection / MonitoringSection /
StatusFooter primitives directly so the user sees a fresh credentials
form — not the configured-nodes table they just came from.

Save dispatch still routes through the existing per-type saveNode
plumbing; the unified form is additive and does not change backend
contracts.
2026-04-19 13:38:53 +01:00
rcourtman a1f8dbe8f9 v6(settings): phase 5 — inline the add-connection flow, retire the add drawer
Replaces the right-side drawer wrapper around ConnectionEditor with an
inline content swap inside InfrastructureWorkspace. When add mode is
active, the inventory table is replaced in place by the editor with a
back-to-systems affordance, instead of floating over a preserved list
behind modal chrome. Putting per-type panels behind a common drawer was
re-wrapping, not consolidating; the unified add surface must take over
the workspace.

Updates subsystem contracts for agent-lifecycle and frontend-primitives
to forbid the drawer/modal overlay for the add flow, so the pattern
cannot silently return.
2026-04-19 13:30:04 +01:00
rcourtman e4663561dd phase 5: wire ConnectionEditor into the add drawer
The Add infrastructure drawer now opens ConnectionEditor instead of the
legacy type picker + per-type step branches. Paste an address, Pulse
probes, detected type dispatches into a credential slot. The slot still
mounts the existing per-type panels (ProxmoxSettingsPanel,
TrueNASSettingsPanel, VMwareSettingsPanel, InfrastructureInstallerSection)
so mature credential flows keep working end-to-end; slot components are
a later phase.

- InfrastructureWorkspace: panelStep state machine replaced with
  addDrawerOpen + initialAddType. Legacy deep links still redirect and
  pre-select the matching type. Agent setup handoff still auto-opens
  into the agent slot. Read-only mode still suppresses the drawer.
- AddSystemPicker + its test deleted (dead code, the picker UI is gone).
- InfrastructureWorkspace.test.tsx rewritten for the probe-first flow
  (probe button, manual-type escape hatch, back-to-probe, per-type
  credential slot routing, legacy redirects, read-only).
- settingsArchitecture guardrail swapped from panelStep string
  assertions to ConnectionEditor + renderCredentialSlot assertions.
- agent-lifecycle + frontend-primitives contracts extended to require
  the unified add surface run through ConnectionEditor.
2026-04-19 13:21:45 +01:00
rcourtman 3608bf34ff Retire legacy uncapped Pulse Pro usage tab 2026-04-19 13:00:29 +01:00
rcourtman 303988e482 phase 4: ConnectionEditor shell + probe step
Adds the unified connection flow's frontend skeleton. ConnectionEditor
owns a probe-first UX: paste address → POST /api/connections/probe
fans out fingerprints → detected type dispatches into a credential slot
via renderCredentialSlot. Manual type selection falls through when the
probe returns no match. In edit mode the probe step is skipped and the
slot renders immediately for the caller-supplied type.

- useConnectionEditor: probe state machine (idle/probing/detected/
  no-match/error) + CONNECTION_TYPE_LABELS shared with the ledger.
- AddressProbeStep: address input, probe button, candidate list with
  hints, error/empty states, manual-fallback escape hatch.
- ConnectionEditor: composes probe step with credential-slot dispatch;
  maintains selected type + selected candidate; back-to-probe action.
- ConnectionEditor.test.tsx: probe → dispatch, no-match → manual pick,
  initialType skips probe (edit mode). Three tests passing.

Nothing is wired into InfrastructureWorkspace yet — that happens in the
add-path replacement phase, where the shell also gains scope UI and
per-type credential slots.
2026-04-19 12:58:29 +01:00
rcourtman 6c6221b4af Make infrastructure settings ledger responsive
Refs #1430
2026-04-19 12:46:05 +01:00
rcourtman 9e60f2aec6 Scrub stale community billing caps
Refs #1429
2026-04-19 12:17:20 +01:00
rcourtman 9c3d96cab2 Add unified connections API (list + probe) with Disabled flag
Introduces GET /api/connections and POST /api/connections/probe as the
backend half of the one-ledger / one-editor connection redesign.

- GET /api/connections aggregates PVE/PBS/PMG/VMware/TrueNAS/agent rows
  into a unified Connection shape with derived state (active, paused,
  unauthorized, unreachable, stale, pending) computed from in-memory
  scheduler health plus agent Host.LastSeen. No new persisted state.
- POST /api/connections/probe fingerprints a host across the five
  supported products in parallel (2s dial + 1s read, 3s total, max 5
  concurrent). Admin-gated (RequireAdmin + ScopeSettingsWrite) to block
  unauthenticated SSRF against internal hosts.
- Disabled bool on PVEInstance/PBSInstance/PMGInstance (zero-value =
  enabled, preserves existing nodes.json); pollers skip disabled
  instances at client init, reconnect, and per-node iteration.
- NodeConfigRequest/Response gain Enabled; write path translates
  *bool -> Disabled so omitted field leaves state untouched.
- ConnectionsAPI frontend client (list/probe) typed off the Go shape.

Contracts updated: api-contracts, monitoring, agent-lifecycle,
performance-and-scalability, storage-recovery. Proofs added:
contract_test.go JSON snapshot for Connection and ProbeResponse,
monitoring guardrails for the Disabled-skip behavior, and a vitest
mock-client test for ConnectionsAPI.

Frontend editor / drawer / table rewrite lands in a separate block.
2026-04-19 11:42:53 +01:00
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