- Replace 'enterprise authentication' with 'team authentication'
- Replace 'Enterprise Insights' with 'Advanced Insights'
- Deprecate isEnterprise() in favor of isPro() and hasFeature()
- Update Settings.tsx to use isPro() for badge visibility
Major changes:
- Add audit_logging, advanced_sso, advanced_reporting features to Pro tier
- Persist session username for RBAC authorization after restart
- Add hot-dev auto-detection for pulse-pro binary (enables SQLite audit logging)
Frontend improvements:
- Replace isEnterprise() with hasFeature() for granular feature gating
- Update AuditLogPanel, OIDCPanel, RolesPanel, UserAssignmentsPanel, AISettings
- Update AuditWebhookPanel to use hasFeature('audit_logging')
Backend changes:
- Session store now persists and restores username field
- Update CreateSession/CreateOIDCSession to accept username parameter
- GetSessionUsername falls back to persisted username after restart
Testing:
- Update license_test.go to reflect Pro tier feature changes
- Update session tests for new username parameter
Recovery notifications were bypassing the quiet hours check, causing
users to receive recovery alerts during their configured quiet hours
window even though the original "down" alerts were suppressed.
- Add ShouldSuppressResolvedNotification() to alert manager
- Check quiet hours before sending recovery notifications in monitor
- Recovery notifications now follow same suppression rules as alerts
This commit adds enterprise-grade reporting and audit capabilities:
Reporting:
- Refactored metrics store from internal/ to pkg/ for enterprise access
- Added pkg/reporting with shared interfaces for report generation
- Created API endpoint: GET /api/admin/reports/generate
- New ReportingPanel.tsx for PDF/CSV report configuration
Audit Webhooks:
- Extended pkg/audit with webhook URL management interface
- Added API endpoint: GET/POST /api/admin/webhooks/audit
- New AuditWebhookPanel.tsx for webhook configuration
- Updated Settings.tsx with Reporting and Webhooks tabs
Server Hardening:
- Enterprise hooks now execute outside mutex with panic recovery
- Removed dbPath from metrics Stats API to prevent path disclosure
- Added storage metrics persistence to polling loop
Documentation:
- Updated README.md feature table
- Updated docs/API.md with new endpoints
- Updated docs/PULSE_PRO.md with feature descriptions
- Updated docs/WEBHOOKS.md with audit webhooks section
- Added Roles and Users settings panels
- Implemented OIDC group-to-role mappings in config and auth flow
- Standardized API token context handling via pkg/auth
- Added Pulse Pro branding and upgrade banners to RBAC features
- Cleanup: Removed empty code blocks and fixed lint errors
- Replace barrel import in AuditLogPanel.tsx to fix ad-blocker crash
- Remove all Enterprise/Pro badges from nav and feature headers
- Simplify upgrade CTAs to clean 'Upgrade to Pro' links
- Update docs: PULSE_PRO.md, API.md, README.md, SECURITY.md
- Align terminology: single Pro tier, no separate Enterprise tier
Also includes prior refactoring:
- Move auth package to pkg/auth for enterprise reuse
- Export server functions for testability
- Stabilize CLI tests
Users providing base URLs like "https://openrouter.ai/api/v1" were
getting HTML error responses because the client used the URL directly
without appending "/chat/completions".
- Normalize baseURL in NewOpenAIClient to ensure it ends with /chat/completions
- Fix modelsEndpoint() to derive /models from the normalized baseURL
- Add tests for URL normalization with various endpoint formats
The previous implementation assumed /24 subnets, which failed for
larger networks (e.g., /16 or /20). Now uses progressive subnet
matching that tries /24, /20, and /16 to handle various network sizes.
Example: If connection IP is 10.1.1.5 and a node has 10.1.2.6,
it now correctly identifies them as being on the same network.
When discovering cluster nodes, Pulse now automatically prefers IPs
on the same subnet as the initial connection. This fixes the common
issue where Pulse used internal cluster network IPs (e.g., 172.x.x.x)
instead of management network IPs (e.g., 10.x.x.x).
How it works:
1. Extract subnet from initial connection URL (assumes /24 for IPv4)
2. For each discovered node, query /nodes/{node}/network for all IPs
3. If cluster-reported IP is on a different subnet, find an IP on
the preferred subnet and set it as IPOverride
4. Manual IPOverride settings are preserved and take precedence
This eliminates the need for manual IPOverride configuration in most
multi-network Proxmox setups.
Refs #929, #1066
1. Add IPOverride field to ClusterEndpoint struct
- Allows users to specify a custom IP that takes precedence over auto-discovered IPs
- Fixes#929 and #1066 where Pulse used internal cluster IPs instead of management IPs
- Added EffectiveIP() method to cleanly handle the override logic
2. Update connection code to use EffectiveIP()
- monitor.go: Use override when building endpoint URLs
- temperature_proxy.go: Use override for proxy connections
3. Add bare Windows EXE files to GitHub releases
- Fixes#1064 where LXC/barebone installs couldn't download Windows agents
- Modified build-release.sh to copy EXEs alongside ZIPs
- Added EXEs to checksum generation
1. Use correct mutex (diagMu) in cleanupDiagnosticSnapshots to prevent
"concurrent map iteration and map write" panics (Fixes#1063)
2. Use cluster name for storage instance comparison in UpdateStorageForInstance
to prevent storage duplication in clustered Proxmox setups (Fixes#1062)
3. Fix KUBECONFIG unbound variable error in install.sh by using ${KUBECONFIG:-}
default parameter expansion (Fixes#1065)
Shared storage was duplicating across polling cycles because the ID
included the node name of whichever node reported it first. When a
different node reported first on the next cycle, a new ID was created.
This fix updates the shared storage aggregation to use a consistent ID
format (instance-cluster-storageName) that doesn't include the node name.
Closes#1049. Thanks to @siccous for the report and initial investigation.
- Add GET /api/audit endpoint for listing events with filters
- Add GET /api/audit/:id/verify endpoint for signature verification
- Add AuditLogPanel UI component with filtering and verification
- Update docs with audit API documentation
- Add localStorage utils for persisting UI state
- Update gitignore patterns
The GHCR OCI registry (ghcr.io/rcourtman/pulse-chart) is returning 403/404
errors for unauthenticated users. Updated all Helm references to use the
working GitHub Pages Helm repository at https://rcourtman.github.io/Pulse
Fixes install issues reported by customers trying to deploy via Helm.
Files updated:
- docs/KUBERNETES.md
- docs/INSTALL.md
- docs/DEPLOYMENT_MODELS.md
- docs/UPGRADE_v5.md
Allows administrators to create configuration profiles and assign them
to agents for centralized fleet management.
- Configuration profiles with customizable settings (Docker, K8s,
Proxmox monitoring, log level, reporting interval)
- Profile assignment to agents by ID
- Agent-side remote config client to fetch settings on startup
- Full CRUD API at /api/admin/profiles
- Settings UI panel in Settings → Agents → Agent Profiles
- Automatic cleanup of assignments when profiles are deleted
Implements server-side persistence for AI chat sessions, allowing users
to continue conversations across devices and browser sessions. Related
to #1059.
Backend:
- Add chat session CRUD API endpoints (GET/PUT/DELETE)
- Add persistence layer with per-user session storage
- Support session cleanup for old sessions (90 days)
- Multi-user support via auth context
Frontend:
- Rewrite aiChat store with server sync (debounced)
- Add session management UI (new conversation, switch, delete)
- Local storage as fallback/cache
- Initialize sync on app startup when AI is enabled
Documents all available token scopes, UI presets, and step-by-step
instructions for setting up kiosk mode with read-only dashboard tokens.
Related to #1055
- Add "Kiosk / Dashboard" preset in API token manager for easy token creation
- Backend returns token scopes in /api/security/status when authenticated via token
- Frontend hides Settings tab when token lacks settings:read scope
- URL-based token auth via ?token=xxx now properly reports scopes
Users can now create a monitoring:read token and use it in kiosk displays
without exposing settings or requiring cookie persistence.
Related to #1055
For RAIDZ/mirror pools, zpool list SIZE reports raw capacity (sum of
all disks), but users expect usable capacity (accounting for parity).
The dataset stats from statfs give the correct usable capacity.
Now uses dataset Total when it's smaller than zpool Size, indicating
RAIDZ/mirror overhead.
Related to #1052
- Add detailed HTTPS detection troubleshooting to REVERSE_PROXY.md
- Explain X-Forwarded-Proto header requirement for nginx/Caddy/Apache
- Add Docker Swarm troubleshooting section to UNIFIED_AGENT.md
- Document how to force Docker runtime if auto-detection fails
Based on customer feedback.
Added strategy.type option to values.yaml (default: RollingUpdate) to allow
users to configure the deployment strategy. Users with ReadWriteOnce (RWO)
persistent volumes should set this to "Recreate" to avoid Multi-Attach errors
during upgrades.
Related to #1057
When a user's reverse proxy redirects HTTP to HTTPS, Go's default HTTP
client behavior converts POST requests to GET on 301/302 redirects
(per HTTP specification). This causes the Pulse server to return 405
"Only POST is allowed" errors.
Added CheckRedirect to all agent HTTP clients (host, docker, kubernetes)
that returns a clear error message guiding users to use the correct
protocol in their --url flag instead of silently following redirects.
Related to #1058
- Major updates to README.md and docs/README.md for Pulse v5
- Added technical deep-dives for Pulse Pro (docs/PULSE_PRO.md) and AI Patrol (docs/AI.md)
- Updated Prometheus metrics documentation and Helm schema for metrics separation
- Refreshed security, installation, and deployment documentation for unified agent models
- Cleaned up legacy summary files
The storage deduplication logic only checked cluster config's Shared
flag, but this required the cluster config API call to succeed. When
the per-node storage API already returns shared=1 (as the user
verified), we should use that directly.
Now we check three sources for shared storage detection:
1. Per-node API shared flag (storage.Shared)
2. Cluster config shared flag (if available)
3. Storage type heuristics (NFS, RBD, PBS, etc.)
Related to #1049
Add contextual help icons throughout the UI to improve feature
discoverability. Users can click (?) icons to see explanations
with examples for settings they might not understand.
- HelpIcon component with click-to-open popover
- Centralized help content registry in /content/help/
- FeatureTip component for dismissible contextual tips
- Help added to: alert delay, AI endpoints, update channel
Password managers may fill form fields programmatically without
triggering input events, causing SolidJS signals to remain empty.
This fix reads values directly from the DOM on submit, ensuring
credentials filled by password managers are properly captured.
Related to #1036
The previous commit (06261627) added backend support for configurable
physical disk polling intervals but didn't include the UI to configure it.
Adds a dropdown selector (5/15/30/60 minutes) that appears when physical
disk monitoring is enabled.
Related to #1007
Shared storages were appearing multiple times (once per node) because
the deduplication logic only checked the Proxmox `Shared` flag. Many
storage types are inherently cluster-wide but don't set this flag:
- RBD (Ceph block storage)
- CephFS
- PBS (Proxmox Backup Server)
- GlusterFS
- NFS
- CIFS/SMB
- iSCSI
Now we detect shared storage based on both the Shared flag AND the
storage type. Inherently shared storage types are deduplicated and
shown once with a "cluster" node designation.
Related to #1049
Add atomic `closed` flag to Client struct and `safeSend()` helper method
to prevent race condition when sending to client channels. The race
occurred when a client disconnected while a goroutine was trying to send
initial state - the channel could be closed between the registration
check and the actual send.
All sends to client.send now go through safeSend() which checks the
closed flag first. The flag is set atomically before closing the channel
in all code paths (unregister, dispatchToClients, broadcast, shutdown).
Related to #1048
When LoadActiveAlerts skipped acknowledged alerts older than 1 hour,
it was also not populating ackState. This meant that when the same
alert (e.g., backup-age) was recreated on the next poll cycle,
preserveAlertState couldn't find any acknowledgement record and
the alert would retrigger notifications.
Now ackState is populated even for skipped old acknowledged alerts,
so if they reappear, the acknowledgement will be restored.
Related to #1043
When --docker-runtime=podman is explicitly set, the agent should try
Podman-specific sockets first before falling back to environment
defaults (which try /var/run/docker.sock).
Also adds /var/run/podman/podman.sock as a candidate socket path,
which is used by CoreOS and some Fedora configurations.
Related to #1045
macOS ships with bash 3.2 (GPLv2) which has a bug where expanding
an empty array like ${array[@]} with set -u enabled throws an
"unbound variable" error, even when the array is initialized.
Use ${arr[@]+"${arr[@]}"} pattern to safely handle empty arrays.
Related to #1046
Changes:
1. Add MAX_POLL_TIMEOUT env var for large Proxmox clusters that need
more than 3 minutes for polling (default: 3m, minimum: 30s)
2. Handle external Ceph storage gracefully - don't mark nodes unhealthy
when Proxmox returns 'binary not installed' (e.g., for Ceph not
managed by Proxmox)
Related to #965
Added FreeBSD amd64 and arm64 build targets to the release process:
- Build host-agent and unified agent binaries for FreeBSD
- Package FreeBSD tarballs in releases
- Include FreeBSD binaries in universal tarball for download endpoint
Updated agent install script with FreeBSD support:
- Fixed architecture detection (FreeBSD reports 'amd64' not 'x86_64')
- Added FreeBSD rc.d service handler with proper daemon management
- Automatic service enabling via rc.conf
This enables users to run the Pulse agent on FreeBSD-based systems
like OPNsense, pfSense, and vanilla FreeBSD.
Fixes#1041
Previously, agents linked to Proxmox nodes were hidden from the
Settings > Agents > Managed Agents table, which confused users who
couldn't find their installed agents.
Now all agents are shown in the table, with linked agents displaying
an indigo 'Linked' badge that explains they're also merged with
Proxmox nodes in the Dashboard.
Fixes#1038