Commit graph

7663 commits

Author SHA1 Message Date
rcourtman
4ac224084e Guard Proxmox Patrol coverage before hydration 2026-06-30 16:56:35 +01:00
rcourtman
f0c81de00e Add Patrol safe workflow row guidance 2026-06-30 16:47:55 +01:00
rcourtman
477d549efd Clarify Patrol mode boundaries 2026-06-30 16:14:55 +01:00
rcourtman
5dbf750136 Add dashboard Explore boundary guardrails 2026-06-30 15:55:22 +01:00
rcourtman
4dacd93078 Add monitor-context Patrol coverage 2026-06-30 15:40:20 +01:00
rcourtman
238c806d1f Add Patrol row issue scaffolding 2026-06-30 15:20:42 +01:00
rcourtman
a8726a7bc7 Add Patrol calm-day protection posture 2026-06-30 15:06:25 +01:00
rcourtman
9ba5cf1e0f Group Patrol work signals in the workbench 2026-06-30 14:46:04 +01:00
rcourtman
a05c273b6a Localize pricing handoff and restore frontend lint 2026-06-30 12:19:00 +01:00
rcourtman
d4e4c7ed79 Add monitor-first Patrol browser proof 2026-06-30 11:44:57 +01:00
rcourtman
229867636d Add resource priority to Patrol queue 2026-06-30 10:37:54 +01:00
rcourtman
7c75b13d2c Carry host power sensor readings 2026-06-30 10:02:49 +01:00
rcourtman
d393ccf310 Add typed NVIDIA GPU stats 2026-06-30 09:43:33 +01:00
rcourtman
fe3c0f3ee3 Collect NVIDIA GPU temperatures 2026-06-30 09:11:18 +01:00
rcourtman
d8de95b91f Show custom provider model catalogs 2026-06-29 23:19:13 +01:00
rcourtman
dcf23fb2ca Test selected provider model from settings 2026-06-29 23:14:33 +01:00
rcourtman
c8dc45577c Emit TrueNAS pool status incidents 2026-06-29 22:54:19 +01:00
rcourtman
4eac3b535c Accept ping alias for availability targets 2026-06-29 22:43:13 +01:00
rcourtman
597c369a63 Add alert delivery diagnosis endpoint 2026-06-29 22:27:01 +01:00
rcourtman
a801f456d6 Improve Patrol SMART disk evidence 2026-06-29 18:17:24 +01:00
rcourtman
6b7d0b45f9 Add agent fleet diagnostics endpoint 2026-06-29 18:03:46 +01:00
rcourtman
73208a2863 Document state summary integration contract 2026-06-29 17:39:11 +01:00
rcourtman
e103278885 Capture v6 GA release target 2026-06-29 15:26:42 +01:00
rcourtman
1510a73e1f Guard guest disk alert migration identity
Add regression coverage ensuring per-disk guest alert migration cannot remap an active alert from one guest onto another. Refs #1511
2026-06-28 21:41:07 +01:00
rcourtman
d20c556db3 Preserve platform tab route state
Remember each primary platform tab's last route so URL-backed filters survive tab switches without leaking query state across tabs.
2026-06-28 17:46:00 +01:00
rcourtman
fe32dfffb8 Pin update telemetry privacy boundary
Add regression coverage that raw updater failure text cannot escape through the coarse failure category.
2026-06-28 15:40:33 +01:00
rcourtman
d6a21eba98 Add update funnel telemetry
Record anonymous 30-day update attempts, successes, failures, and coarse failure categories from local update history.
2026-06-28 15:19:39 +01:00
rcourtman
15856ce760 fix(frontend): hide stale agent prompts in demo mode 2026-06-28 13:30:53 +01:00
rcourtman
c03c3231c1 fix(frontend): route infrastructure entry to workspace 2026-06-28 09:52:55 +01:00
rcourtman
2900e41b2d Refresh RC7 packet after release-gate fixes
Some checks failed
Build and Test / Secret Scan (push) Has been cancelled
Build and Test / Frontend & Backend (push) Has been cancelled
2026-06-28 03:30:31 +01:00
rcourtman
d796928969 Install signature verifier dependencies 2026-06-28 03:26:13 +01:00
rcourtman
85c46cfcd1 Track alert save workers during shutdown 2026-06-28 01:39:12 +01:00
rcourtman
4fc56162f6 Harden hostagent SMART no-device test
Some checks are pending
Build and Test / Secret Scan (push) Waiting to run
Build and Test / Frontend & Backend (push) Waiting to run
2026-06-28 01:01:20 +01:00
rcourtman
ea15f9f3bf Refresh RC7 packet for current head 2026-06-28 00:05:22 +01:00
rcourtman
fc10de9b54 Sanitize Patrol runtime failures in history 2026-06-27 23:08:40 +01:00
rcourtman
8fff04eac5 fix(ai): query capacity forecast history under metrics-target ID not canonical ID
The deterministic capacity forecast queried utilization history using the
canonical resource ID (storage-<hash>), but the monitoring layer records
metrics under the metrics-target/source ID (MetricsTarget.ResourceID). For
most storage sources these two IDs diverge, so the forecast query always
found nothing and the feature stayed dormant even for pools with full
ingestion history (Proxmox, TrueNAS, PBS, agent-backed storage).

This splits the two ID roles in the precompute path: a new metricsID field
on patrolStoragePoolRow / patrolPrecomputeStorageSource carries the
metrics-target ID (resolved from SourceID() in the readState branch, or
MetricsTarget.ResourceID in the unified-resource branch) for history
queries, while the canonical id is still used for stamp-matching findings.

Adds a test proving the forecast fires when history exists only under the
metrics-target ID and that the resulting forecast resourceID stays
canonical so StampCapacityForecasts can still match it.

Contract (ai-runtime): the forecast must query history under the
metrics-target ID (StoragePoolView.SourceID()), not the canonical ID; the
forecast's own resourceID stays canonical for stamp-matching.

Refs lane: protection-posture-attention-queue
2026-06-27 22:18:09 +01:00
rcourtman
d45715d083 feat(ai): surface deterministic capacity forecasts as first-class finding signals
Capacity forecasts (days-to-full, current usage, trend) were previously
computed deterministically but only fed into LLM prompt text. They never
reached the finding as structured data, so the frontend could not render a
verified urgency signal and the model's speculation was the only thing the
operator saw.

This persists CapacityForecast on Finding (marshal-mirror pair) and stamps
it post-analysis via a service join (StampCapacityForecasts). The forecast
filter now also keeps stable-high (>=80%) pools so the deterministic "no
fill trend" reading wins over model speculation, and fixes isQuiet wrongly
treating stable (-1) trends as filling.

Frontend maps the forecast through the UnifiedFinding view model and renders
a deterministic urgency line (Filling up / Stable / days-to-full · % used)
in the expanded finding detail.

Note: forecasts only populate for resources whose usage is ingested into
metrics history (Proxmox/Ceph storage, nodes, guests). Agent-host storage
(unraid pools) is not yet ingested as a time-series and remains a follow-up
to activate the feature for those pools.

Refs lane: protection-posture-attention-queue
2026-06-27 22:03:28 +01:00
rcourtman
f676dea097 fix(monitoring): prefer agent source for physical disk SMART metrics
When a Proxmox node has an agent installed, SMART/temperature metrics are
written by the host agent under the agent's disk source ID. But
BuildMetricsTarget preferred the Proxmox source first, and Proxmox API
does not expose detailed SMART data. The mismatch meant the frontend
queried a metrics key that nothing writes, so disk metrics appeared empty.

Move the Agent source check ahead of Proxmox/TrueNAS for
ResourceTypePhysicalDisk, mirroring the existing pattern for
ResourceTypeAgent where agent source already wins over platform sources.

When a disk has a serial number, it is used as the canonical metric ID
regardless of source (PreferredPhysicalDiskMetricID), so the reorder
only affects disks without serials — exactly the case where source
priority determines the key.

Refs #1487
2026-06-27 20:22:04 +01:00
rcourtman
028e8c8df2 fix(unifiedresources): add space reclamation and retention for all append-only tables
The unified_resources.db grew without bound (2GB reported) because:

1. No VACUUM: DELETE freed rows internally but never shrank the file.
   Added auto_vacuum(INCREMENTAL) to the DSN for new databases, plus a
   one-time migrateAutoVacuum() that converts existing databases.
   reclaimFreePages() now runs after each prune cycle to return freed
   pages to the OS via PRAGMA incremental_vacuum.

2. Missing retention: action_lifecycle_events, export_audits, and
   loop_reports had no retention at all. Added 90-day retention for
   lifecycle/export audits and 30-day for loop_reports, matching the
   existing action_audits/resource_changes cadence.

3. Slow cleanup cadence: the retention loop ran every 6h and never on
   startup. Reduced to hourly and added an initial prune 30s after
   startup so a restart with a bloated DB starts recovering immediately.

Mirrors the proven pattern from metrics.db (auto_vacuum INCREMENTAL +
incremental_vacuum + WAL checkpoint).

Refs #1496
2026-06-27 18:38:56 +01:00
rcourtman
d6aed650b1 fix(docker): stop manual update check from looping on ack failure
getDockerCommandPayload returned dispatched commands on every report
fetch, causing the agent to re-execute check-updates on every poll
cycle. When the ack also failed, the report was buffered and retried,
creating an infinite loop.

- Only return command payload on the queued->dispatched transition;
  subsequent fetches return nil (agent already received it).
- Don't propagate ack errors from handleCheckUpdatesCommand; the report
  was delivered and check-updates is fire-and-forget. Command expires
  if ack never succeeds.

Refs #1504
2026-06-27 18:26:23 +01:00
rcourtman
b5ddbd5606 fix(monitoring): use MemAvailable instead of MemTotal-MemFree for Linux guest/node memory
When the Proxmox API's meminfo/status payload omits Available, Buffers,
and Cached (common for QEMU guest-agent and node status responses), the
code derived 'available' from Free alone — producing used = Total-Free
which counts reclaimable page cache as used memory (e.g. 94% instead of
the correct 76%).

Guest path (deriveGuestMemInfoAvailable): return 0 when cache metrics
are completely missing and only Free is available, so resolveGuestStatusMemory
tries better sources: guest-agent /proc/meminfo file-read (returns
MemAvailable), RRD memavailable, or the linked host agent.

Node path (resolveNodeMemory): always try RRD memavailable when cache
metrics are missing, not only when effectiveAvailable == 0 — previously
a non-zero Free value blocked the RRD fallback.

Refs #1501
2026-06-27 17:41:00 +01:00
rcourtman
22ed97f562 fix(email): eliminate data race on provider username resolution
sendViaProviderWithAddresses mutated the shared e.config.Username for
provider-specific defaults (SendGrid, Postmark, SparkPost, Resend).
If concurrent goroutines sent email simultaneously, this was a data
race on the config struct.

Move the resolution into negotiateAuth via a local variable
(resolveProviderUsername helper) so the shared config is never mutated.
2026-06-27 17:36:51 +01:00
rcourtman
c07fce0c33 fix: clean up copy-to-clipboard timeouts on unmount
Some checks are pending
Build and Test / Secret Scan (push) Waiting to run
Build and Test / Frontend & Backend (push) Waiting to run
Add timer cleanup to copy-to-clipboard handlers in TokenRevealDialog,
UpdateBanner, QuickSecuritySetup, SetupCompletionPanel, and WelcomeStep.
Rapid clicks no longer accumulate dangling setTimeout callbacks.
2026-06-27 17:25:54 +01:00
rcourtman
cc1ebb569f fix(keyboard): Cmd+K command palette works from editable fields
The isEditableTarget early-return blocked Cmd+K when focus was in any
input, textarea, or select element. The command palette shortcut must
work from anywhere — it is the primary keyboard navigation path.

Move the Cmd+K check before the editable-target guard.
2026-06-27 17:20:28 +01:00
rcourtman
28d2413c71 fix(hostagent): clear stale Unraid sync action when resync position is zero
Unraid's mdResyncAction field can retain its last value (e.g. "check")
after a parity check is canceled, causing Pulse to report a stale sync
action indefinitely. The mdResync/mdResyncPos field is the authoritative
indicator: it drops to 0 when no resync is running. Gate SyncAction on
a non-zero position so the alert clears once the sync actually stops.

Refs #1485
2026-06-27 17:08:02 +01:00
rcourtman
5bc209d919 fix(alerts): deduplicate flapping history entries on load and periodic cleanup
Add deduplicateHistory() to HistoryManager that collapses consecutive
same-alert entries within a 5-minute window. The flapping/re-fire bugs
(lifecycle and stateful paths) created many duplicate history entries
before the cooldown fixes were deployed. This cleanup runs on startup
after loadHistory and periodically alongside cleanOldEntries, so both
existing noise and any future edge-case duplicates are handled.

On this install: 332 entries → 130 (60% reduction).
2026-06-27 16:36:29 +01:00
rcourtman
125959e4e0 fix(dev): login endpoint respects admin bypass mode
handleLogin rejected all credentials when ALLOW_ADMIN_BYPASS=1 because
it validated against config AuthUser/AuthPass directly without checking
the bypass flag. This made dev-mode browser testing impossible after a
backend restart with bypass env vars — the API middleware accepted all
requests but the login page could never obtain a session cookie.

When bypass is enabled, accept any credentials and create a session as
'admin'.
2026-06-27 16:09:23 +01:00
rcourtman
fa7e4711e7 fix(alerts): re-fire cooldown for stateful alerts to prevent duplicate history
Stateful alerts (ZFS pool/device, storage topology) were creating
duplicate history entries every poll cycle because SyncStorageAlertsForInstance
clears alerts between evaluations, making the existing-alert check miss.
Add the same 5-minute re-fire cooldown used in the lifecycle path: when a
stateful alert re-fires within 5 minutes of resolution, reactivate the
original (preserving StartTime) and update the existing history entry's
LastSeen instead of appending a new entry.
2026-06-27 15:58:22 +01:00
rcourtman
40ad4178ba fix(alerts): prevent duplicate history entries for flapping alerts
When an alert fires, resolves, and re-fires within 5 minutes (the
recently-resolved retention window), the previous implementation created
a new history entry for each fire cycle. For a flapping connection like
an unreachable Proxmox node, this produced hundreds of duplicate entries
in a single night — 302 identical 'pi unreachable' alerts in one case.

The fix checks the recently-resolved map before creating a new history
entry. If the alert was resolved within the cooldown window, it
reactivates the original alert (preserving StartTime) and updates the
existing history entry's LastSeen instead of appending a new one.
2026-06-27 15:38:13 +01:00
rcourtman
f7f573fe98 fix(security): resolve all Dependabot vulnerabilities
Upgrade dompurify 3.4.1 -> 3.4.11 fixing 8 XSS/sanitization bypass CVEs.
Add form-data ^4.0.6 npm override fixing CRLF injection (high severity).
All 6727 frontend tests pass.
2026-06-27 15:37:37 +01:00