The Settings page was telling systemd/bare metal users to run install.sh
for upgrades, which is wrong - install.sh is for fresh installations only
and does nothing if Pulse is already installed.
Changes:
- Updated upgrade instructions to mention built-in "Install Update" button
- Added correct manual upgrade steps (download tarball, stop service, extract, start)
- Removed misleading "run install.sh" instruction
This fixes a critical UX issue where users would run install.sh and think
nothing happened, when they should either:
1. Use the built-in automatic update feature (Install Update button)
2. Manually download and extract the new binary
Related files:
- frontend-modern/src/components/Settings/Settings.tsx:4052-4072
The "SSH Fallback" label was confusing to users. Changed to "Proxy (SSH)"
to make it clearer that the proxy is using SSH to collect temperature data
from cluster nodes.
This appears in the Capabilities column on Settings → Nodes when:
- Temperature monitoring is enabled
- Socket proxy is not available/healthy
- HTTPS proxy is not available/reachable
The "Check Proxy Nodes" button in Settings > Diagnostics was returning
403 Forbidden due to missing CSRF token. The frontend was using native
fetch() instead of apiFetch() which automatically includes CSRF tokens
for POST requests.
Fixed three endpoints in Settings.tsx:
- /api/diagnostics (GET) - for consistency
- /api/diagnostics/temperature-proxy/register-nodes (POST) - reported issue
- /api/diagnostics/docker/prepare-token (POST) - same bug
Note: Export/import config endpoints intentionally continue using native
fetch() because they need custom 401/403 handling to show the API token
modal instead of redirecting to login.
Allow homelab users to send webhooks to internal services while maintaining security defaults.
Changes:
- Add webhookAllowedPrivateCIDRs field to SystemSettings (persistent config)
- Implement CIDR parsing and validation in NotificationManager
- Convert ValidateWebhookURL to instance method to access allowlist
- Add UI controls in System Settings for configuring trusted CIDR ranges
- Maintain strict security by default (block all private IPs)
- Keep localhost, link-local, and cloud metadata services blocked regardless of allowlist
- Re-validate on both config save and webhook delivery (DNS rebinding protection)
- Add comprehensive tests for CIDR parsing and IP matching
Backend:
- UpdateAllowedPrivateCIDRs() parses comma-separated CIDRs with validation
- Support for bare IPs (auto-converts to /32 or /128)
- Thread-safe allowlist updates with RWMutex
- Logging when allowlist is updated or used
- Validation errors prevent invalid CIDRs from being saved
Frontend:
- New "Webhook Security" section in System Settings
- Input field with examples and helpful placeholder text
- Real-time unsaved changes tracking
- Loads and saves allowlist via system settings API
Security:
- Default behavior unchanged (all private IPs blocked)
- Explicit opt-in required via configuration
- Localhost (127/8) always blocked
- Link-local (169.254/16) always blocked
- Cloud metadata services always blocked
- DNS resolution checked at both save and send time
Testing:
- Tests for CIDR parsing (valid/invalid inputs)
- Tests for IP allowlist matching
- Tests for bare IP address handling
- Tests for security boundaries (localhost, link-local remain blocked)
Related to #673🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The config backup export and import functions were incorrectly parsing
the CSRF token from cookies, causing "Export requires authentication"
errors even when users were properly logged in.
Two issues were fixed:
1. Cookie parsing used `.split('=')[1]` which truncated tokens containing
`=` padding characters (common in base64 tokens). Fixed by using
`.split('=').slice(1).join('=')` to preserve the full value.
2. Missing URL decoding of the cookie value. Browsers percent-encode
cookie values, so `=` becomes `%3D`. The backend then failed to match
the encoded token hash. Fixed by adding `decodeURIComponent()`.
Both fixes mirror the pattern already used in apiClient.ts.
Users with 8-11 character passwords could not export/restore config backups
because the export encryption requires 12+ character passphrases for security,
but the password creation UI only enforced an 8-character minimum.
This created a confusing UX where users with short passwords saw validation
errors when trying to export backups, with the only solution being to use a
custom passphrase or change their password.
Root cause:
- FirstRunSetup and ChangePasswordModal allowed 8+ char passwords
- Config export/import requires 12+ char passphrases (backend validation)
- The v4.26.4 fix added frontend validation that showed the mismatch
- Users hit client-side validation before request was sent (no backend logs)
This fix raises the minimum password length to 12 characters everywhere:
- internal/auth/password.go: MinPasswordLength 8 → 12
- FirstRunSetup.tsx: validation and placeholder updated
- ChangePasswordModal.tsx: validation, minLength, and help text updated
- QuickSecuritySetup.tsx: validation and label updated
Impact:
- New users must create 12+ character passwords
- Existing users with <12 char passwords are unaffected (can't detect from hash)
- Those users will see the existing helpful error directing them to use custom
passphrase for backups
- "Use your login password" option now works for all future passwords
This aligns password requirements across the system and eliminates the
confusing mismatch between login credentials and backup encryption requirements.
Related to #646 where user confirmed backups still failed in v4.26.5
The v4.26.4 fix inadvertently broke CLI export compatibility. The frontend
attempted JSON.parse on all backup files and returned early with "Invalid
JSON file format" when parsing failed. This prevented the format detection
code from ever executing, breaking CLI-generated exports which are raw
base64 strings without a JSON wrapper.
Root cause:
- CLI exports (`pulse config export`) output raw base64 via
internal/config/export.go:128
- The fix at Settings.tsx:2030-2034 called showError() and returned
immediately on parse failure
- Format detection logic at lines 2040-2049 never executed for CLI exports
This changes the parsing flow to:
1. Try JSON.parse first (handles UI exports with {status, data} format)
2. On parse success, extract data field as before
3. On parse failure, treat entire file contents as raw base64 (CLI format)
This preserves the v4.26.4 improvements (12-char validation, better error
messages) while restoring CLI export compatibility.
Related to #646 where user confirmed v4.26.4 still failed to restore backups.
Updated the Quick Start for Docker section in TEMPERATURE_MONITORING.md to be
more user-friendly and address common setup issues:
- Added clear explanation of why the proxy is needed (containers can't access hardware)
- Provided concrete IP example instead of placeholder
- Showed full docker-compose.yml context with proper YAML structure
- Added sudo to commands where needed
- Updated docker-compose commands to v2 syntax with note about v1
- Expanded verification steps with clearer success indicators
- Added reminder to check container name in verification commands
These improvements should help users who encounter blank temperature displays
due to missing proxy installation or bind mount configuration.
Addresses two issues preventing configuration backup/restore:
1. Export passphrase validation mismatch: UI only validated 12+ char
requirement when using custom passphrase, but backend always enforced
it. Users with shorter login passwords saw unexplained failures.
- Frontend now validates all passphrases meet 12-char minimum
- Clear error message suggests custom passphrase if login password too short
2. Import data parsing failed silently: Frontend sent `exportData.data`
which was undefined for legacy/CLI backups (raw base64 strings).
Backend rejected these with no logs.
- Frontend now handles both formats: {status, data} and raw strings
- Backend logs validation failures for easier troubleshooting
Related to #646 where user reported "error after entering password" with
no container logs. These changes ensure proper validation feedback and
make the backup system resilient to different export formats.
This implements the ability for users to assign custom display names to Docker hosts,
similar to the existing functionality for Proxmox nodes. This addresses the issue where
multiple Docker hosts with identical hostnames but different IPs/domains cannot be
easily distinguished in the UI.
Backend changes:
- Add CustomDisplayName field to DockerHost model (internal/models/models.go:201)
- Update UpsertDockerHost to preserve custom display names across updates (internal/models/models.go:1110-1113)
- Add SetDockerHostCustomDisplayName method to State for updating names (internal/models/models.go:1221-1235)
- Add SetDockerHostCustomDisplayName method to Monitor (internal/monitoring/monitor.go:1070-1088)
- Add HandleSetCustomDisplayName API handler (internal/api/docker_agents.go:385-426)
- Route /api/agents/docker/hosts/{id}/display-name PUT requests (internal/api/docker_agents.go:117-120)
Frontend changes:
- Add customDisplayName field to DockerHost TypeScript interface (frontend-modern/src/types/api.ts:136)
- Add MonitoringAPI.setDockerHostDisplayName method (frontend-modern/src/api/monitoring.ts:151-187)
- Update getDisplayName function to prioritize custom names (frontend-modern/src/components/Settings/DockerAgents.tsx:84-89)
- Add inline editing UI with save/cancel buttons in Docker Agents settings (frontend-modern/src/components/Settings/DockerAgents.tsx:1349-1413)
- Update sorting to use custom display names (frontend-modern/src/components/Docker/DockerHosts.tsx:58-59)
- Update DockerHostSummaryTable to display custom names (frontend-modern/src/components/Docker/DockerHostSummaryTable.tsx:40-42, 87, 120, 254)
Users can now click the edit icon next to any Docker host name in Settings > Docker Agents
to set a custom display name. The custom name will be preserved across agent reconnections
and takes priority over the hostname reported by the agent.
Related to #623
Addresses issue #567 where selecting "Custom interval..." from the
backup polling dropdown would revert to a preset option if the current
custom minutes value happened to match a predefined interval.
The bug occurred because:
1. User selects "Custom interval..." from dropdown
2. Code sets interval based on current customMinutes value
3. If that value matches a preset (e.g., 60 min = 1 hour), the
computed select value returns the preset instead of 'custom'
4. Dropdown reverts, hiding the custom input field
Fix introduces a dedicated state variable (backupPollingUseCustom)
to explicitly track whether custom mode is active, independent of
whether the current interval value matches a preset option.
Changes:
- Add backupPollingUseCustom signal to track custom mode state
- Update backupIntervalSelectValue() to check custom flag first
- Set/clear custom flag in dropdown onChange handler
- Initialize custom flag when loading settings from API
Related to #567
Related to discussion #615
Add optional GuestURL field to PVE instances and cluster endpoints,
allowing users to specify a separate guest-accessible URL for web UI
navigation that differs from the internal management URL.
Backend changes:
- Add GuestURL field to PVEInstance and ClusterEndpoint structs
- Add GuestURL field to Node model
- Update cluster auto-discovery to preserve existing GuestURL values
- Update node creation logic to populate GuestURL from config
- Update API handlers to accept and persist GuestURL field
Frontend changes:
- Add GuestURL input field to NodeModal for configuration
- Update NodeGroupHeader and NodeSummaryTable to use GuestURL for navigation
- Add GuestURL to Node and PVENodeConfig TypeScript interfaces
When GuestURL is configured, it will be used for navigation links
instead of the Host URL, allowing users to access PVE hosts through
a reverse proxy or different domain while maintaining internal API
connections.
Related to #551
Enhanced the PMG connection test to actually validate the metrics
endpoints that Pulse uses for monitoring, rather than only checking
the version endpoint. This provides users with immediate feedback if
their PMG credentials lack the necessary permissions to collect metrics.
Backend changes:
- Test mail statistics, cluster status, and quarantine endpoints during
connection test (internal/api/config_handlers.go:1695-1714)
- Return warnings array in test response when endpoints are unavailable
- Increased timeout from 10s to 15s to accommodate multiple endpoint checks
- Added warning logs for failed endpoint checks
Frontend changes:
- Added showWarning() toast function for warning messages
- Enhanced NodeModal to display warning status with amber styling
- Added warnings list display in test results UI
- Updated Settings.tsx to show warnings from connection tests
This change helps users identify permission issues immediately rather
than discovering later that metrics aren't being collected despite a
"successful" connection.
This commit implements per-node temperature monitoring control and fixes a critical
bug where partial node updates were destroying existing configuration.
Backend changes:
- Add TemperatureMonitoringEnabled field (*bool) to PVEInstance, PBSInstance, and PMGInstance
- Update monitor.go to check per-node temperature setting with global fallback
- Convert all NodeConfigRequest boolean fields to *bool pointers
- Add nil checks in HandleUpdateNode to prevent overwriting unmodified fields
- Fix critical bug where partial updates zeroed out MonitorVMs, MonitorContainers, etc.
- Update NodeResponse, NodeFrontend, and StateSnapshot to include temperature setting
- Fix HandleAddNode and test connection handlers to use pointer-based boolean fields
Frontend changes:
- Add temperatureMonitoringEnabled to Node interface and config types
- Create per-node temperature monitoring toggle handler with optimistic updates
- Update NodeModal to wire up per-node temperature toggle
- Add isTemperatureMonitoringEnabled helper to check effective monitoring state
- Update ConfiguredNodeTables to show/hide temperature badge based on monitoring state
- Update NodeSummaryTable to conditionally show temperature column
- Pass globalTemperatureMonitoringEnabled prop through component tree
The critical bug fix ensures that when updating a single field (like temperature
monitoring), the backend only modifies that specific field instead of zeroing out
all other boolean configuration fields.
- Add Access-Control-Expose-Headers to allow frontend to read X-CSRF-Token response header
- Implement proactive CSRF token issuance on GET requests when session exists but CSRF cookie is missing
- Ensures frontend always has valid CSRF token before making POST requests
- Fixes 403 Forbidden errors when toggling system settings
This resolves CSRF validation failures that occurred when CSRF tokens expired or were missing while valid sessions existed.
This commit addresses multiple issues in the Docker/host agent removal flow:
Agent Stop Fix:
- Add systemctl stop command after agent acknowledgement to prevent systemd restart
- Previous behavior: agent disabled but systemd immediately restarted it (Restart=always)
- New behavior: agent disables itself, sends ack, then stops systemd service completely
UX Improvements:
- Add real-time elapsed time counter during removal wait
- Show progress indicators prominently (no longer hidden in dropdown)
- Display expected time range (30-60 seconds) and last heartbeat
- Auto-show timeout warning after 2 minutes with actionable "Force remove" button
- Add contextual help explaining what's happening at each stage
Security Enhancement:
- Automatically revoke API tokens when removing Docker/host agents
- Previous behavior: tokens remained valid after agent removal
- New behavior: tokens are revoked and persisted immediately on removal
- Prevents removed agents from re-authenticating with old credentials
This commit adds comprehensive token revocation tracking across the UI and enhances the agent installation script for better platform support.
Key changes:
- Added token revocation warnings in Docker hosts and host agents UI with amber-colored indicators
- Implemented automatic token revocation detection when tokens are deleted
- Enhanced install scripts with Unraid detection and manual start instructions for non-systemd platforms
- Improved service management with restart instead of start for systemd
- Added visual indicators for revoked tokens with contextual warnings
- Updated table column widths in hosts overview for better layout
Simplifies the onboarding flow by removing verbose instructions and toggles, consolidating navigation elements, and cleaning up the settings interface. Improves the macOS host agent installer with better Keychain access control and launchd service management.
Replace rounded pill badges with cleaner text and chip styles:
- Remove pill backgrounds from token/user authentication badges in node tables
- Simplify cluster node count from pill to plain text
- Change mobile navigation from pill bubbles to underlined tabs
- Convert subnet selection from rounded-full pills to squared chips
Reduces visual clutter and provides a more modern, minimal interface.
Ensure consistent icon usage between top navigation tabs and settings sidebar:
- Proxmox: Use ProxmoxIcon in both locations
- Docker: Use DockerIcon (Simple Icons whale logo) in both locations
- Hosts: Simplify HostsIcon to use lucide Monitor icon everywhere
This improves visual consistency and reduces confusion between navigation contexts.
PROPERLY fix the Docker icon in the main left sidebar navigation by updating the DockerIcon component to use lucide's Container icon instead of Simple Icons Docker logo.
Also revert incorrect changes that added Docker to the Proxmox sub-navigation tabs - Docker should not be in the Proxmox settings sub-menu.
Replace DockerIcon (Simple Icons) with lucide-solid's Container icon to maintain consistency with other navigation icons (Server, HardDrive, Mail). All icons now use the same stroke-based style and rendering pattern.
The SettingsSectionNav was only visible on the Proxmox tab. Now it also shows on the Docker settings page (/settings/docker), displaying all four tabs: Virtual Environment, Backup Server, Mail Gateway, and Docker.
When on the Docker tab, the navigation correctly highlights Docker as active.
Add Docker tab to the settings section navigation using the same DockerIcon component used in the main navigation. The icon is rendered at w-4 h-4 to match the visual weight of the lucide icons at size 16.
Now the settings navigation shows: Virtual Environment, Backup Server, Mail Gateway, and Docker.
Change from 'terminal-square' to 'square-terminal' - the actual filename in lucide-solid. The icon name convention is square-terminal not terminal-square.
Switch from penguin attempts to lucide-solid's TerminalSquare icon, which represents Linux/CLI effectively and matches the stroke-based style of the existing lucide icons. This provides:
- Clean geometry at 20x20
- Instant recognition (Linux = CLI/terminal)
- Consistent stroke weight with other lucide icons
- Better visual balance with Apple and Windows logos
Codex recommendation after multiple penguin icon failures.
Replace detailed Tux SVG with a simplified stroke-based penguin head that renders cleanly at 20x20. The new icon uses strokes instead of fills (matching lucide's style) and was specifically designed by Codex for small-size rendering.
The simplified design includes just the essential features (head outline, eyes, beak, belly) and uses currentColor to inherit the chip's color state.
Replace Monitor icon with the classic Tux penguin SVG from Simple Icons. Now all three platforms use their recognizable brand icons:
- Linux: Tux the penguin
- macOS: Apple logo
- Windows: Windows logo
All icons are from FOSS sources (Simple Icons, CC0 license).
Keep the original Apple and Windows logo SVGs which were perfect, only replace the weird-looking Linux penguin with lucide Monitor icon. Mixed approach uses lucide Monitor component for Linux and SVG elements for macOS/Windows.
Replace custom SVG icons with proper icons from lucide-solid library:
- Linux: Monitor icon
- macOS: Laptop icon
- Windows: Computer icon
These are FOSS icons from the lucide project, properly imported from the existing icon library rather than hand-coded SVG paths.
Add FOSS icons (Tux penguin for Linux, Apple for macOS, Windows logo) to platform selection buttons. Icons are displayed in colored boxes matching the active/inactive state, providing better visual identification of each OS platform.
Icons sourced from Simple Icons (CC0 license).
Remove duplicate "Host Monitoring" SectionHeader since the page already has a descriptive header at the top. Also clean up unused cardTitle() and cardDescription() functions.
Remove bloated multi-step wizard pattern and localStorage token storage in favor of a clean inline flow:
- Remove token generation modal and multi-step confirmation UI
- Remove localStorage persistence (useScopedTokenManager hook)
- Remove showTokenReveal popup - token goes directly into command
- Remove summary stat cards and redundant info cards
- Add simple inline input for optional token naming
- Token generation now instantly inserts into install command
The new flow: enter optional name → generate → copy command. No modals, no stored tokens, no extra steps.