- RAID tests now use /dev/md2 since md0/md1 are skipped for Synology compatibility
- AI handler tests now expect 'AI is not enabled' message after AI gating change
dispatchAlert() now checks if an alert is already acknowledged before sending
notifications. Previously, acknowledged alerts (especially backup-age alerts)
would continue to dispatch notifications every poll cycle because the
acknowledgement check was missing from the dispatch path.
The fix adds an early return in dispatchAlert() when alert.Acknowledged is true,
matching the existing checks for flapping, activation state, and quiet hours.
- Add IsAIEnabled() method to AISettingsHandler for consistent checks
- Gate baseline learning, pattern detector, and correlation detector initialization
in StartPatrol() on AI being enabled
- Add AI enabled checks to all /api/ai/intelligence/* endpoints as defense-in-depth
- Return empty results with "AI is not enabled" message when AI is disabled
This ensures no AI-related data is collected, persisted, or returned when AI is disabled,
preventing the "undismissable alerts" issue where old AI findings would appear.
The findMatchingHostAgent function now uses the proper linkedHostAgentId
relationship from the backend state, falling back to hostname matching only
if no linked agent is found. This fixes the 'Agent required' badge showing
incorrectly when agents are running and collecting data.
- Search for kubeconfig in /home/*/.kube/config in addition to /root/.kube/config
- Add --kubeconfig installer flag to specify custom kubeconfig path
- Auto-detect and pass kubeconfig path to agent when Kubernetes is enabled
- Respect KUBECONFIG environment variable when kubectl is working
Related to discussion #968
- Separated metrics collection into internal/dockeragent/collect.go
- Added agent self-update pre-flight check (--self-test)
- Implemented signed binary verification with key rotation for updates
- Added batch update support to frontend with parallel processing
- Cleaned up agent.go and added startup cleanup for backup containers
- Updated documentation for Docker features and agent security
When returning legacy arrays directly from store state, the createMemo
wouldn't notify dependents because the same array reference was returned
even when its contents changed (via reconcile). The spread operator
creates a new array reference each time, ensuring downstream memos
like sortedHosts() properly re-run.
This is a defense-in-depth fix complementing commit f5c0af77 which
fixed HostRow destructuring. Together these ensure reliable real-time
updates on the Hosts page.
Related to #949
- Add temperature unit preference in Settings > General
- Store preference in localStorage (temperatureUnit: 'celsius' | 'fahrenheit')
- Create formatTemperature() utility that respects the preference
- Update all temperature displays across the UI:
- TemperatureGauge component
- HostsOverview page (sensors, disk temps)
- DiskList (SMART temperatures)
- ThresholdSlider
- ThresholdBadge
- ThresholdsTable
- ProxmoxNodesSection
- NodeSummaryTable tooltips
- Alert formatters
When a host agent is running on a Proxmox node (linked host agent),
merge the agent's SMART disk temperature data into the Physical Disks
view for that node. This allows disk temps collected by pulse-agent
to populate the Physical Disks page without requiring Proxmox SMART
monitoring to be enabled.
Matching is done by WWN (most reliable), serial number, or device path.
Closes part of issue #909 (follow-up from MichiFr)
The SMART disk temperature data was being collected by the agent but not
passed through to the frontend. Fixed by:
1. Added SMART field to HostSensorSummaryFrontend and created
HostDiskSMARTFrontend type in models_frontend.go
2. Updated hostSensorSummaryToFrontend() in converters.go to include
SMART data conversion
3. Added HostDiskSMART interface to frontend TypeScript types
4. Updated HostTemperatureCell to display disk temperatures in tooltip
with a 'Disk Temperatures' section and fallback to SMART temps when
no CPU/sensor temps are available
Strip trailing slashes from PULSE_URL to prevent URLs like
http://host:7655//download/pulse-agent which incorrectly match
the frontend route instead of the download endpoint.
The sensor proxy self-heal script runs every 5 minutes and calls migrate-to-file.
Previously it would print 'Migration complete' every time, even when already in
file mode with nothing to migrate.
Now migrateInlineToFile returns a boolean indicating if migration actually
occurred, and the CLI only prints the message when work was done.
Synology NAS creates multiple shared folders (e.g., /volume1/docker, /volume1/photos)
that are all mount points on the same underlying BTRFS volume. Each reported the same
16TB total, causing Pulse to show 64TB+ instead of 16TB.
The fix tracks device+total combinations and only counts each unique pair once.
When duplicates are found, the shallowest mountpoint (e.g., /volume1) is preferred.
Added a unit test to verify the deduplication works correctly.
Previously, toggling AI Commands in the Agents view would show a pending state
and wait for the agent to confirm the change (up to 2 minutes). If the agent
was slow to report or the WebSocket update was missed, the toggle would appear
stuck.
Now, UpdateHostAgentConfig also updates the Host model in state immediately,
providing instant UI feedback. The agent will still receive the config on its
next report, but users see the change right away.
Added SetHostCommandsEnabled function to models.State for this purpose.
Fixed an issue where all Docker containers were showing 'click to update'
even when they were up to date. The root cause was comparing the wrong
digest types:
- Previously: Compared ImageID (local config hash) vs registry manifest digest
- Now: Uses RepoDigests from image inspect, which is the actual manifest
digest that Docker received from the registry when pulling the image
For multi-arch images, the registry returns a manifest list digest, while
Docker stores the platform-specific image config digest locally. These
will never match, causing false positives for all multi-arch images.
Changes:
- Added ImageInspectWithRaw to dockerClient interface
- Added getImageRepoDigest method to extract RepoDigest from image
- Added matchesImageReference helper for Docker Hub naming conventions
- Added tests for matchesImageReference
Fixes#955
Fixed a critical reactivity bug in HostsOverview.tsx where the HostRow
component was destructuring props.host in the function body. In SolidJS,
this breaks reactivity because the destructured value is a static snapshot
captured at component creation time.
Changes:
- Removed 'const { host } = props' destructuring in HostRow
- Changed all 'host.' references to 'props.host.' to maintain reactivity
- Converted cpuPercent and diskStats to reactive getters (functions)
- Added documentation comment explaining why destructuring breaks reactivity
This fixes Issue #949 where CPU, memory, and disk values on the Hosts
page would stay stale until manual page refresh.
Related to #949
- Add critical release section at top of file
- Make VERSION file requirement impossible to miss
- Explain that version_guard job will fail if VERSION doesn't match
- Document the new one-click container update feature
- Explain update detection, safety features, and requirements
- Bump VERSION to 5.0.6 for release
- Add comprehensive test coverage for agent report, flush buffer, and deps
- Expand flow, HTTP, CPU, and swarm test coverage
- Refactor registry access to use deps interface for better testability
- Add container update and self-update test scenarios
- Add Undismiss() method to FindingsStore to revert dismissed findings
- Include all dismissed findings in GetSuppressionRules() (not just suppressed)
- Add DismissedReason field to SuppressionRule struct
- Update DeleteSuppressionRule to handle dismissed (non-suppressed) findings
- Update frontend to show dismissal type badges (Suppressed/Expected/Noted)
- Change 'Delete' button to 'Reactivate' for dismissed findings
Related to #950
Allows specifying which IP address the agent should report, useful for:
- Multi-homed systems with separate management networks
- Systems with private monitoring interfaces
- VPN/overlay network scenarios
Usage:
pulse-agent --report-ip 192.168.1.100
PULSE_REPORT_IP=192.168.1.100 pulse-agent
Content was being streamed twice:
1. During each iteration of the tool loop (intended for intermediate feedback)
2. Again after the loop ended with finalContent (redundant)
This caused duplicate responses when using Ollama and other providers.
- Add container update command handling to unified agent
- Agent can now receive update_container commands from Pulse server
- Pulls latest image, stops container, creates backup, starts new container
- Automatic rollback on failure
- Backup container cleaned up after 5 minutes
- Added comprehensive test coverage for container update logic
BREAKING CHANGE: AI Patrol now uses EXACT alert thresholds by default
instead of warning 5-15% before the threshold.
Changes:
- Default behavior: Patrol warns at your configured threshold (e.g., 96% = warns at 96%)
- New setting: 'use_proactive_thresholds' enables the old early-warning behavior
- API: Added use_proactive_thresholds to GET/PUT /api/settings/ai
- Backend: Added SetProactiveMode/GetProactiveMode to PatrolService
- Backend: Added GetThresholds to PatrolService for UI display
- Tests: Updated and added tests for both exact and proactive modes
- Also fixed unused imports in dockeragent/agent.go
When proactive mode is disabled (default):
- Watch: threshold - 5% (slight buffer)
- Warning: exact threshold
When proactive mode is enabled:
- Watch: threshold - 15%
- Warning: threshold - 5%
Related to #951
Docker overlay filesystems were being counted as separate disks when
monitoring hosts running Docker. These are virtual layers, not actual
storage.
Added overlay and overlayfs to the virtualFSTypes list so they are
always excluded from disk usage calculations, regardless of their
reported usage percentage.
NFS and CIFS mounts were already being filtered correctly.
Related to #942
Docker deployments with custom port mappings would show incorrect URLs
in email alerts because the auto-detection couldn't determine the
external port.
Added a "Public URL" setting in Settings > Network that allows users
to configure the dashboard URL used in email notifications.
- Added publicURL field to SystemSettings (persistence.go)
- Load/save publicURL in system settings handler
- Apply publicURL to notification manager on change
- Added UI input in NetworkSettingsPanel
- Shows env override warning if PULSE_PUBLIC_URL is set
Related to #944