Commit graph

7668 commits

Author SHA1 Message Date
rcourtman
c0ac0762da Fix security scan findings
Harden proxy-auth admin role checks, metrics listener exposure, Teams webhook escaping, and dependency lockfiles.
2026-07-01 09:55:35 +01:00
rcourtman
9dc4386318 Attach mock availability checks to services 2026-06-30 21:16:16 +01:00
rcourtman
55d9bf8610 Fix Machines filter toolbar layout 2026-06-30 17:27:27 +01:00
rcourtman
c759c9a77a Gate mobile pairing codes on relay readiness 2026-06-30 17:12:55 +01:00
rcourtman
887855e5e5 Remove Proxmox overview Patrol coverage strip 2026-06-30 17:05:06 +01:00
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