- 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
When a node was manually added with a hostname (e.g., pve.example.com)
and then the agent registered using its IP address, the code would
correctly deduplicate but incorrectly overwrite the user's configured
hostname with the agent's IP.
Now when matching by IP resolution (hostname resolves to agent's IP),
we preserve the user's original hostname configuration instead of
replacing it with the IP.
Related to #940
The GuestURL field was missing from NodeFrontend and its converter,
causing configured Guest URLs to be ignored when clicking on cluster
node names. The frontend would fall back to the auto-detected IP
instead of using the user-configured Guest URL.
Related to #940
The port field would immediately jump back to 587 when trying to clear
it because parseInt("") returns NaN, and NaN || 587 becomes 587.
Now the field allows empty values while typing, and only applies the
default (587) when the field loses focus with an invalid value.
Related to #935
TLS and STARTTLS are mutually exclusive encryption methods:
- TLS (port 465): Implicit encryption from connection start
- STARTTLS (port 587): Starts unencrypted, upgrades to TLS
- None (port 25): No encryption
Having both as independent checkboxes allowed invalid configurations.
Now uses a single dropdown with the three options.
Related to #936
When a powered-off VM is backed up by Proxmox, the VM status briefly
changes (e.g., to "running" during backup). This caused the powered-off
alert to be cleared, deleting the ackState record. When the backup
completed and the alert was recreated, it appeared as a new unacknowledged
alert, generating a new notification.
The fix preserves ackState when alerts are removed, allowing
preserveAlertState to restore the acknowledgement when the same alert
reappears. Stale ackState entries (for alerts that don't exist) are
cleaned up after 1 hour.
Related to #937
When GetWebhooks returns webhooks, headers and customFields are masked
with ***REDACTED*** for security. However, when the frontend toggled
a webhook's enabled state, it sent back the redacted values, which
overwrote the actual header values (like Authorization tokens).
This broke webhooks after disabling and re-enabling them, as the auth
headers were replaced with "***REDACTED***".
Now UpdateWebhook detects redacted values and preserves the original
headers/customFields from the existing webhook.
Related to #938
The previous commit forced the builder stage to linux/amd64 to avoid
QEMU timeouts, but forgot to add explicit GOARCH for the main pulse
binary. This caused amd64 binaries to be shipped in arm64 images,
resulting in "Exec format error" on ARM64 hosts.
Now the main pulse binary is cross-compiled for both amd64 and arm64
(like all the agent binaries already were), and the runtime stage
selects the correct binary based on TARGETARCH.
Related to #933
- Add registry checker tests (caching, enable/disable, parsing, concurrency)
- Add alert integration tests for update detection and Pro license gating
- Add API handler tests for /api/infra-updates endpoints
- Test cleanup of tracking maps when containers are removed
- Test threshold-based alerting behavior
- Add FeatureUpdateAlerts constant for Pro license gating
- Add feature to all Pro tier feature lists
- Add SetLicenseChecker method to alerts Manager
- Check Pro license in checkDockerContainerImageUpdate before alerting
- Wire license checker from router to alert manager
Free users still see update badges in the UI.
Pro users get proactive alerts after 24h of pending updates.
- Add GHCR (GitHub Container Registry) token support for public images
- Clean up dockerUpdateFirstSeen tracking when containers are removed
- Improve UpdateIcon tooltip to show digest info
- Add cursor-help to indicate hoverable tooltip
- Add routes for infrastructure update detection API:
- GET /api/infra-updates - list all container updates with filtering
- GET /api/infra-updates/summary - aggregated stats per host
- GET /api/infra-updates/host/{hostId} - updates for specific host
- GET /api/infra-updates/{resourceId} - specific resource update status
- POST /api/infra-updates/check - trigger update check (placeholder)
- Update handlers to query Docker container updates from monitor state
- Protected by auth and monitoring_read scope
When a Proxmox host has multiple network interfaces (management, Ceph,
cluster ring), the agent would use heuristic scoring to pick an IP,
which could select an isolated network instead of the management network.
Now the agent first determines which local IP is actually used to connect
to the Pulse server, ensuring registration uses a reachable IP. Falls back
to the heuristic scoring if connection-based detection fails.
Related to #929
When the same Ceph cluster is reported from multiple sources (PVE API
and host agent), it showed up twice in the UI. Now we deduplicate by
FSID before converting to frontend format, keeping the cluster entry
with the most complete data (most monitors/managers/pools reported).
Related to #928
Ubuntu was showing as "debian 24.04" because we used PlatformFamily
(which is "debian" for all Debian derivatives) instead of Platform
(which is "ubuntu" for Ubuntu).
Now uses Platform first, falling back to PlatformFamily only if empty.
Related to #927
Previously, preflight only built amd64 images, so multi-arch failures
(like the QEMU timeout in 5.0.5) weren't caught until after the
release was published.
Now preflight builds linux/amd64,linux/arm64 staging images. If
multi-arch build fails, the release pipeline stops before publishing.
Combined with the Dockerfile fix (forcing amd64 for build stages),
this ensures Docker build issues are caught early.
The multi-arch Docker build was timing out after 1 hour because
the backend-builder stage was running under QEMU emulation for arm64.
Building 31 Go binaries under QEMU is extremely slow.
Since Go cross-compiles all target architectures anyway, and the
frontend build produces platform-independent JS, there's no need
to run these stages under QEMU. Force them to linux/amd64 and let
Go handle cross-compilation natively.
Only the runtime stages need to be multi-arch (for the correct
Alpine base image and binary selection).
Show the actual update commands with copy buttons for Docker installations,
making it easier for Docker users to update. Includes both docker run and
docker-compose variants.
This addresses audit finding that Docker updates need better documentation
since they can't auto-update like LXC/systemd deployments.
When an agent registers using an IP address, check if any existing node's
hostname resolves to that same IP. This prevents duplicates when a node
was manually configured via hostname and later the agent is installed
which registers using the host's IP.
Changes:
- Add extractHostIP() to extract IP from URL if present
- Add resolveHostnameToIP() with 2s timeout for DNS resolution
- During agent auto-registration, check if existing hostname-based
configs resolve to the new IP and update instead of creating duplicates
- Add test for extractHostIP helper function
The legacy state file could represent either PVE or PBS registration,
depending on what was installed at the time. Now we check what's
currently installed to determine the correct behavior:
- If PVE is installed: legacy file means PVE was registered
- If PBS-only (no PVE): legacy file means PBS was registered
When PBS is installed directly on a PVE host (an officially supported
configuration), the agent now detects and registers BOTH products instead
of only detecting PVE.
Changes:
- Add detectProxmoxTypes() to detect all Proxmox products on a host
- Add RunAll() method to register each detected product separately
- Use per-type state files (proxmox-pve-registered, proxmox-pbs-registered)
to track registration status for each product independently
- Maintain backward compatibility with legacy single state file
- Add tests for new state file path logic
- Add DELETE /api/agents/unregister endpoint for agent self-unregistration
- Agent now unregisters itself from Pulse server when uninstalled
- Add clarifying note in UnifiedAgents explaining linked agents behavior
- Linked agents are managed via their PVE node but this is now explained in UI
- Add LastSeen field to HostAgent model for better agent status tracking
- Add /api/agents/host/uninstall endpoint for agent self-unregistration
- Update install.sh to notify server during --uninstall (reads agent ID from disk)
- Update install.ps1 with same logic for Windows
- Update frontend uninstall command to include URL/token flags
This ensures that when an agent is uninstalled, the host record is
immediately removed from Pulse and any linked PVE nodes have their
+Agent badge cleared.
When the unified agent binary isn't found locally (happens on LXC/barebone
installations that update via web UI which only updates the pulse binary),
redirect to GitHub releases using HTTP 307.
This complements the install.sh GitHub proxy fallback from 7b6613bb.
Related to #909
When install.sh or install.ps1 don't exist locally (happens on LXC/barebone
installations that were updated via web UI which only updates the binary),
fallback to fetching from GitHub raw content.
Related to #909
When nodes are updated, now validates that LinkedHostAgentID points to
an existing host agent. References to deleted host agents are automatically
cleared, fixing the 'Agent' tag persistence for users who removed agent
entries before commit c394d24.
Related to #920
The healthcheck was hardcoded to use HTTP, which fails when
HTTPS_ENABLED=true. Now uses a script that detects the protocol
and uses --no-check-certificate for self-signed certs.
Related to #922
Add ?kiosk=1 URL parameter to hide the filter panel and section nav
for a cleaner dashboard view. Useful for dedicated LCD displays or
wallboard setups.
Related to #917
When a host agent is deleted via the UI, the LinkedHostAgentID on any
PVE nodes that were linked to it was not being cleared. This caused
the "Agent" tag to persist in the UI after uninstalling the agent.
Related to #920
Acknowledged alerts were still triggering repeated webhook notifications
because the re-notification logic only checked cooldown period, not
acknowledgment status. Now acknowledged alerts are skipped entirely.
Related to #921
- Check if tag exists before creating (skip if pointing to HEAD, fail with
helpful message if pointing elsewhere)
- Check if draft release exists before creating (update existing draft)
- Add --clobber to all asset uploads to allow re-uploading on retry