Commit Graph

1267 Commits

Author SHA1 Message Date
rcourtman 9ef4e588b7 Add TrueNAS services and stabilize dev startup 2026-05-21 12:08:25 +01:00
rcourtman 497390ff95 Scope TrueNAS resources by appliance
Scope TrueNAS child source IDs under the system source key so duplicate pool, dataset, app, VM, share, and disk names from different appliances do not merge.

Use the resulting hierarchy for per-system TrueNAS overview counts and scoped mock metric targets.
2026-05-21 00:59:00 +01:00
rcourtman 0801ede8c3 Add native TrueNAS network share inventory 2026-05-20 23:50:37 +01:00
rcourtman 3fa46aa836 Add native TrueNAS VM inventory to overview
Project TrueNAS vm.query inventory into canonical VM resources and render it on the TrueNAS overview.
2026-05-20 22:32:46 +01:00
rcourtman 142236d797 Seed mock Discovery fixtures 2026-05-20 14:54:19 +01:00
rcourtman 435cf816fb Harden Discovery command-scan gating 2026-05-20 12:40:04 +01:00
rcourtman 6ce593f474 Skip monitor reload on no-op auto-register
Every ~5 minutes the Pulse agent on a node already covered by an
existing cluster endpoint re-announces itself via POST
/api/auto-register. The handler ran the full add-the-standalone +
consolidate path, which produced an identical persisted-config
state. But it still wrote nodes.enc and called reloadFunc, which
stops the multi-tenant monitor, sleeps 1 s, and rebuilds it from
scratch. During the rebuild the WebSocket-visible resource store
goes empty (nodes:0, vms:0, containers:0, dockerHosts:0), so every
connected client sees every row flash blank and reappear. The
Docker page made the flicker obvious because the rows are dense
and stacked, but the regression hit every page in the UI.

Fingerprint the persisted-config payload before any mutation and
again after consolidation; when the fingerprints match, the
auto-register is a no-op and SaveNodesConfig + reloadFunc are both
skipped. Real changes (a new node, a rotated token, a captured
fingerprint) still flow through unchanged.

Add two regression tests: one drives the warm-up + steady-state
heartbeat pair and asserts the second call does not call
reloadFunc; the other rotates a token and asserts reloadFunc still
fires.
2026-05-19 11:09:14 +01:00
rcourtman 90482607da Bind Proxmox install command tokens on first use 2026-05-19 09:47:05 +01:00
rcourtman 0c1cdf7488 Add opt-in Proxmox LXC Docker inventory 2026-05-18 23:44:24 +01:00
rcourtman 5a405f7f07 Protect inside-guest Docker visibility boundaries 2026-05-18 14:16:15 +01:00
rcourtman a26dad0607 proxmox(mail): bespoke drawer with cluster nodes + queues + stats
Mail Gateway rows now expand inline to show the detail the slim
ResourcePMGMeta projection couldn't carry: every cluster node with
status / role / uptime / loadavg / postfix queue (active/deferred/
hold/incoming counts + oldest message age), quarantine breakdown
(spam / virus / attachment / blacklisted), spam-score distribution,
top 8 domains by mail volume, and the mail-flow detail strip
(bounces in/out, greylist, junk, RBL + pregreet rejects, bytes
in/out, average process time).

Drawer fetches /api/pmg/instances?id=<id> on first open so the row
keeps its slim payload; the new endpoint projects
Monitor.GetState().PMGInstances directly under ScopeMonitoringRead
with optional id / name filters.

Same inline expand-row pattern as the ceph and workloads drawers
(TableRow inserted below, colspan all columns).
2026-05-16 16:47:20 +01:00
rcourtman 27f35d9819 proxmox(replication+backups): bespoke tables backed by new read-only endpoints
Both sub-tabs were mounting the same RecoverySurface with a forced
platform filter, so /proxmox/replication and /proxmox/backups looked
identical and neither was bespoke to its title.

Replication is a PVE-only concept (zfs send/receive between nodes).
Surface it through a dedicated /api/replication/jobs endpoint that
projects Monitor.ReplicationJobsSnapshot, and render a bespoke
ProxmoxReplicationTable with status, job ID, guest, source→target
nodes, schedule, last sync, duration, fail count and error columns.

PVE backups split into three meaningfully different artifacts:
qm/pct snapshots, vzdump archives on storage, and recent backup-job
tasks. Expose all three through /api/backups/pve (projecting
Monitor.PVEBackupsSnapshot) and render a bespoke ProxmoxBackupsTable
with three sub-tabs, each with columns tuned to that artifact class.

Both endpoints are read-only under ScopeMonitoringRead and accept
optional instance/node/vmid/platform query filters.
2026-05-16 13:57:53 +01:00
rcourtman 976e7c6b42 Add settings discovery refresh action
- expose a manual discovery sweep API

- wire Assistant & Patrol settings to run new, changed, and stale workload refreshes
2026-05-15 23:27:08 +01:00
rcourtman 6e8a7ae5fc Checkpoint current workspace progress 2026-05-15 22:45:48 +01:00
rcourtman 526549dea1 Add Proxmox node thermal history 2026-05-15 21:27:39 +01:00
rcourtman 303a8e2fe8 Remove Assistant handoff decision guidance 2026-05-15 16:57:25 +01:00
rcourtman 14d3284233 Remove Assistant prompt routing heuristics 2026-05-15 16:10:58 +01:00
rcourtman 99fce8e5a3 Remove Assistant status routing residue 2026-05-15 15:19:26 +01:00
rcourtman 348582df66 Fix Assistant chat model-owned routing 2026-05-15 10:50:23 +01:00
rcourtman 22ea442ed7 Fix default agent rollout status noise 2026-05-14 14:37:24 +01:00
rcourtman 439e252a64 Harden infrastructure workload identity regressions 2026-05-14 12:49:16 +01:00
rcourtman a6c460daa0 Stabilize infrastructure realtime identity 2026-05-14 11:50:29 +01:00
rcourtman 321f563a52 Surface blocked workload inventory sources
Show workload-capable source failures on Workloads and keep matching Proxmox host agents attached to their API source when inventory collection is blocked.
2026-05-14 00:22:18 +01:00
rcourtman 823bd3fbb1 Fix fleet command policy convergence 2026-05-13 22:18:47 +01:00
rcourtman ceb9b87cfb Correct fleet config drift truth 2026-05-13 20:38:26 +01:00
rcourtman 17253d27fd Surface connection rollout posture 2026-05-13 20:16:35 +01:00
rcourtman e8b3c7fcf7 Restore remote config signature compatibility
Keep desired config fingerprints as response metadata derived from the signed command and settings payload.

Use merged agent profile settings when building remote config fingerprints.
2026-05-13 19:00:02 +01:00
rcourtman 554158c575 Add desired config fingerprint metadata 2026-05-13 18:51:24 +01:00
rcourtman dbe31bd8d6 Fix action verification read projections 2026-05-13 18:36:00 +01:00
rcourtman f024d3b560 Align action audit verification projection 2026-05-13 18:36:00 +01:00
rcourtman d55888fb7f Correct PBS job health evidence boundaries 2026-05-13 17:05:03 +01:00
rcourtman dd8c3a78ce Add PBS job health evidence ledger 2026-05-13 17:05:03 +01:00
rcourtman da2537e4ab Add self-hosted commercial continuity proof 2026-05-13 16:44:26 +01:00
rcourtman c1db220e3a Make maintenance evidence writes atomic 2026-05-13 14:19:59 +01:00
rcourtman 14d5cd4b3b Record maintenance evidence in resource timelines 2026-05-13 14:19:59 +01:00
rcourtman eb78bf9f37 Persist permanent action refusal outcomes 2026-05-13 14:04:14 +01:00
rcourtman 1a47c03b2b Fix auto-register refresh notifications 2026-05-13 13:59:11 +01:00
rcourtman cfef1b67ae Fix action execution dry-run guard and verification bridge
Ensure dry-run-only actions fail with action_dry_run_only before executor availability checks, and bridge action completion verification projection through router payload mapping.
2026-05-13 11:16:28 +01:00
rcourtman 0e0c90da53 emit reliability finding when an alert starts flapping
Wire the alerts manager's new flapping-detected callback in the AI
intelligence initialization path. Two things happen on each first
transition into the flapping cooldown window for a tracking key:

1. A reliability-category finding is written directly to the findings
   store via emitFlappingPostmortemFinding. Path B from the lane brief:
   the finding is durable without depending on patrol synthesis, so
   the operator sees the diagnosis the moment Pulse decides to
   suppress. The finding ID is derived from the canonical tracking
   key ("alert-flapping:<trackingKey>") so re-detection inside the
   cooldown window folds into the existing record via the same-ID
   branch of FindingsStore.Add -- one finding per flapping condition,
   not one per dispatch.

2. A scoped FlappingPostmortemPatrolScope is enqueued on the trigger
   manager so an actual patrol run can enrich the finding with deeper
   context once it lands.

The finding body names the flapping threshold, window, and cooldown
the manager is currently configured with, plus an action hint
(widen threshold, raise cooldown, or stabilise the resource). That
turns the suppressed alert from silence into a closable item on the
FindingsPanel.

FindingCategoryReliability is reused; no new category, no parent/
child finding structure -- those are deferred per the lane brief.
2026-05-13 00:28:24 +01:00
rcourtman 006821327f add verification outcome and capability postcondition substrate
ActionAuditRecord gains a VerificationOutcome{status, evidenceSummary}
field with a closed enum (unknown/verified/unverified/failed). Existing
records read back as unknown by default via the normalizer and a new
SQLite column verification_outcome_json. The redaction pass scrubs the
evidence summary alongside other operator-authored text.

A new agentexec/verifier_postconditions.go registers postconditions for
qm.start, pct.start, docker.restart, systemctl.restart, and
kubectl.rollout, each parsed by verifier_postconditions_test.go.

Three pre-existing action JSON snapshot tests
(TestContract_ActionDecisionJSONSnapshot,
TestContract_ActionExecutionJSONSnapshot,
TestContract_UnifiedActionAuditsJSONSnapshot) now include the new
verificationOutcome field. The two flagged failing contract tests on
this branch
(TestContract_ActionDryRunOnlyExecutionErrorJSONSnapshot,
TestContract_RouterBridgesVerificationOntoActionCompleted) are
unrelated to this change and were left alone per lane D-002 scope.
2026-05-12 21:53:49 +01:00
rcourtman 015e7f6555 Add maintenance verification reports
When a maintenance window ends on a resource, the sentinel runs
deterministic checks (active alerts, Patrol findings, failed actions
since window start, basic post-window metric recovery) and writes a
durable LoopReport. Operators can list reports per resource, mark them
reviewed, or rerun verification immediately. UI surfaces the section in
the resource detail drawer; scoped Patrol runs and Assistant deep-link
are deferred until those entry points stabilise.
2026-05-12 21:10:58 +01:00
rcourtman 8b0f3564f6 Fail closed on stale API action plans 2026-05-12 17:32:11 +01:00
rcourtman c6d5c4590a Keep agent heartbeats stream local 2026-05-12 16:14:28 +01:00
rcourtman 0b98cded45 Bulk count agent fleet approvals 2026-05-12 16:00:31 +01:00
rcourtman 4cf16ec9cb Stabilize summary chart SLOs 2026-05-12 14:40:55 +01:00
rcourtman 1726cf47b4 Harden Patrol and Assistant action boundaries 2026-05-12 12:06:27 +01:00
rcourtman 89379c4b5c Use effectiveLoadP95Budget for metrics-history load test CI variance 2026-05-12 09:19:58 +01:00
rcourtman a5d8b43088 Let assertJSONSnapshot exclude dynamic top-level fields for patrol_preflight 2026-05-12 01:38:59 +01:00
rcourtman 5fd05efa83 Add connection-degraded alert for wedged platform connections
A Proxmox host wedged on a ZFS deadlock yesterday took the cluster API poll
with it (context deadline exceeded). The unified connections aggregator
flipped the Connection from active to stale to unreachable, and the
Settings / Infrastructure page rendered the right badges, but no top-nav
alert ever fired because nothing was actively notifying off that derived
state. Patrol's deterministic triage flagged it every minute, but its LLM
investigation stage has been broken since 2026-02-26 so flags never
escalated into user-visible findings. Result: a 3 hour outage I only
noticed because I happened to open Settings.

This wires an active notification off the same connection state the
Settings badges already use:

- internal/alerts/connection.go: new CheckConnection +
  clearConnectionDegradedAlert that fire connection-degraded after three
  consecutive stale or unreachable observations. Severity scales: stale
  warning, unreachable / unauthorized critical. Clear runs through the
  same recovery-confirmation gate as clearNodeOfflineAlert so a single
  flap back to active doesn't silently resolve a real outage. Paused,
  disabled, and non-platform connections are no-ops.

- internal/api/connections_alerts.go: snapshot translator that turns
  api.Connection into the narrow alerts.ConnectionSnapshot view. Keeping
  the snapshot type inside the alerts package preserves the existing
  api -> monitoring import direction; the monitor would have cycled if
  it called back into api directly.

- internal/monitoring: new SetConnectionsSnapshotLister hook + a
  per-tick checkConnectionAlerts call in the main poll loop, alongside
  the existing evaluate*Agents passes.

- internal/api/router.go: register the lister closure on r.monitor so
  the alerts loop sees the same Connection rows the HTTP handler does.

- internal/alerts/specs/types.go: add "connection" to the migration
  bridge list of accepted ResourceTypes, alongside node / docker-host /
  proxmox-disk / etc. The connection concept doesn't have a canonical
  unified resource type yet; this matches the existing pattern for
  alert-keyed resources that aren't first-class canonical.

Test coverage in internal/alerts/connection_test.go covers active never
fires, three stale observations escalate from pending to warning,
unreachable escalates warning to critical, unauthorized fires critical
cold, paused / disabled / agent never fire, recovery confirmation gate,
and a stale flap during recovery resets the gate.
TestResourceAlertSpecValidateAllowsConnectionMigrationBridgeType mirrors
the existing migration-bridge proof tests for the new type.
2026-05-12 00:29:04 +01:00
rcourtman 16963e415c Drop t.Parallel from dismiss/snooze finding tests that race on global session store 2026-05-11 23:10:35 +01:00