Commit graph

1809 commits

Author SHA1 Message Date
rcourtman
ee83cc3f54 Add meaningful tests for host agent and exec websocket 2025-12-17 17:02:01 +00:00
rcourtman
62b5580193 fix: Prevent orphaned encrypted data when encryption key is deleted
- crypto.go: Add runtime validation to Encrypt() that verifies the key file
  still exists on disk before encrypting. If the key was deleted while Pulse
  is running, encryption now fails with a clear error instead of creating
  orphaned data that can never be decrypted.

- hot-dev.sh: Auto-generate encryption key for production data directory
  (/etc/pulse) when HOT_DEV_USE_PROD_DATA=true and key is missing. This
  prevents startup failures and ensures encrypted data can be created.

- Added test TestEncryptRefusesAfterKeyDeleted to verify the protection works.
2025-12-17 17:00:53 +00:00
rcourtman
8f746ede2a hostagent: avoid host ID collisions and prefer LAN IP 2025-12-17 16:29:59 +00:00
rcourtman
c99d1bd13b test(envdetect): cover environment detection decisions 2025-12-17 16:08:10 +00:00
rcourtman
cd3a2f14ee test: expand AI provider test coverage with HTTP mocks 2025-12-17 15:53:56 +00:00
rcourtman
07f74f5d92 test: expand cmd and agent update coverage 2025-12-17 13:28:17 +00:00
rcourtman
e9fd6c197b fix(config): avoid deadlock saving empty nodes config 2025-12-17 13:28:06 +00:00
rcourtman
941e9ed098 test: add unit tests for AI, Kubernetes agent, and clients 2025-12-17 12:47:36 +00:00
rcourtman
ad59c13b95 fix: Skip deprecated pulse-sensor-proxy for v5+ installations
The unified agent now handles temperature monitoring in v5+, making
pulse-sensor-proxy unnecessary. This commit:

1. Adds INSTALLER_MAJOR_VERSION constant to declare bundled version
2. Skips 'Temperature Monitoring Setup' prompts for v5+ installs
3. Skips sensor proxy installation entirely for v5+
4. Updates help text to mark --proxy as deprecated for v5+
5. Removes outdated sensor proxy instructions from completion message

Fixes the 'pct pull TASK ERROR: failed to open /opt/pulse/bin/pulse-sensor-proxy-linux-amd64'
error reported by users installing v5.0.0-rc.3.

Reported-by: RLSinRFV (GitHub Discussion #845)
2025-12-17 12:20:03 +00:00
rcourtman
0b717bfa44 Improve test coverage 2025-12-17 12:00:59 +00:00
rcourtman
bacf6b5fc9 feat: Add tool/function calling support to Ollama provider
Fixes issue where Ollama users get 'I'm a large language model, I can't do XYZ'
responses when trying to use the AI assistant. The problem was that the
Ollama provider was not passing tool definitions to the API.

Changes:
- Add Tools field to ollamaRequest struct
- Add ollamaTool, ollamaToolFunction, ollamaToolCall structs
- Convert tools from ChatRequest to Ollama format in Chat()
- Parse tool_calls from Ollama response
- Set StopReason to 'tool_use' when model requests tool execution
- Handle tool results in multi-turn conversations

Requires Ollama v0.3.0+ and a tool-capable model (llama3.1+, mistral-nemo, etc.)

Closes: Discussion #845 comment by misterlegend
2025-12-17 11:54:32 +00:00
rcourtman
307bb9f6cc feat: add Linux agent archives to releases and improve version mismatch detection
Release improvements:
- Add standalone Linux agent archives (amd64, arm64, armv7, armv6, 386) to releases
- Previously only Darwin/Windows had standalone agent downloads

Agent version mismatch detection:
- New agentVersion.ts utility compares agent versions against server version
- DockerHostSummaryTable now shows accurate 'outdated' warnings when
  agent version is older than the connected Pulse server
- Better tooltips explain version status and upgrade instructions
2025-12-16 22:51:10 +00:00
rcourtman
0b65d68682 fix: backup matching uses instance+VMID to prevent cross-instance collisions
Previously, SyncGuestBackupTimes matched backups to guests using only VMID.
This caused newly created containers to incorrectly show old backup times
from different containers on other Proxmox instances that happened to have
the same VMID.

Now uses composite key (instance+VMID) for PVE storage backups to ensure
proper isolation. PBS backups still use VMID matching (since they aggregate
from multiple sources) but only as a fallback.

Fixes issue where ollama LXC showed 'last backup 3 months ago' despite
being created yesterday.
2025-12-16 22:19:19 +00:00
rcourtman
26133cf0cf feat: Improve cluster endpoint error messages for users
- Add sanitizeEndpointError() to transform raw Go errors into user-friendly messages
- Transform 'context deadline exceeded' into helpful messages mentioning possible causes
- Storage timeout errors now suggest checking PBS/NFS/Ceph backend connectivity
- Connection refused, certificate errors, and auth errors get actionable hints
- Apply sanitization everywhere cluster endpoint lastError is stored
- Add comprehensive tests for all error transformations
2025-12-16 21:50:02 +00:00
rcourtman
433b5b5d3a test: add regression test for OIDC env vars with nil config (#853)
Adds TestOIDCEnvVarsWithNilConfig to catch the case where OIDC_* env
vars were silently ignored when no oidc.enc file existed. This documents
the proper pattern of initializing OIDCConfig before calling MergeFromEnv.
2025-12-16 21:02:47 +00:00
rcourtman
6bfa8a3837 fix: AI model selector dropdown overflow on small screens
Add max-height and overflow-y-auto to the model selector dropdown in
the AI chat panel to prevent it from extending past the screen when
there are many models available (e.g., with Ollama).

Fixes feedback from discussion #845
2025-12-16 20:59:42 +00:00
rcourtman
9dc365c9c6 fix: OIDC env vars ignored when no oidc.enc file exists
When OIDC_* environment variables were set but no oidc.enc config file
existed, cfg.OIDC was nil and MergeFromEnv would silently return without
applying the env vars (due to nil receiver check).

Fix: Initialize cfg.OIDC to default values before merging env vars if
it's nil. This ensures OIDC can be configured purely through environment
variables without requiring a pre-existing config file.

Related to #853
2025-12-16 20:25:56 +00:00
rcourtman
358d38f97f fix: Allow printable ASCII in alert IDs for acknowledge requests
Reverts overly strict alert ID validation that was rejecting valid
alert IDs containing special characters. Docker host IDs can contain
user-supplied data like hostnames which may include parentheses,
brackets, or other printable ASCII characters.

The previous validation only allowed alphanumeric + limited punctuation,
which caused 400 errors when acknowledging alerts from Docker hosts
with special characters in their identifiers.

Related to #852
2025-12-16 20:10:31 +00:00
rcourtman
7f4161802a Add PNG version of logo for GitHub App 2025-12-16 19:35:43 +00:00
rcourtman
a9c0a4d682 feat: configurable backup freshness thresholds for dashboard indicator
Adds FreshHours and StaleHours settings to control when the dashboard
backup indicator shows green (fresh), amber (stale), or red (critical).

- Backend: Added FreshHours/StaleHours to BackupAlertConfig (default 24/72 hours)
- Frontend: getBackupInfo() now accepts optional thresholds parameter
- Dashboard/GuestRow components use thresholds from alert config
- Settings saved/loaded with alert configuration

Closes #839
2025-12-16 16:36:08 +00:00
rcourtman
cb960a04e3 Add comprehensive AI test coverage
- Add integration tests for Ollama provider (17 tests against real API)
- Add unit tests for baseline, correlation, patterns, memory, knowledge, cost packages
- Add context formatter and builder tests
- Add factory tests for provider initialization
- Add Makefile targets: test-integration, test-all
- Clean up test theatre (removed struct field tests)

Integration tests require Ollama at OLLAMA_URL (default: 192.168.0.124:11434)
Run with: make test-integration
2025-12-16 12:33:06 +00:00
rcourtman
e41b805de2 fix: Retry-After header now uses actual limiter window
Previously the Retry-After header was hardcoded to "60" seconds
regardless of the rate limiter's actual window duration. Now uses
the limiter's configured window (e.g., 600 seconds for recovery
endpoints, 300 for exports).

Related to #579
2025-12-16 10:07:16 +00:00
rcourtman
a028f312de Auto-update Helm chart version to 5.0.0-rc.3 2025-12-16 09:52:51 +00:00
rcourtman
1971b956ae Auto-update Helm chart documentation 2025-12-16 09:52:51 +00:00
rcourtman
e7e9797319 chore: bump version to 5.0.0-rc.3 2025-12-16 09:22:50 +00:00
rcourtman
a8957a2e7b feat(ui): make AI settings page more compact and user-friendly
- Replace verbose info banner with streamlined layout
- Add collapsible 'Advanced Model Selection' accordion for Chat/Patrol models
- Make AI Patrol Settings section collapsible with inline summary badges
- Compact Cost Controls into single-row inline layout
- Reduce form spacing for tighter presentation
- Remove unused formHelpText import

Also includes:
- OpenAI provider fixes for max_tokens parameters
- Security setup CSRF and 401 fixes
- Minor UI tweaks
2025-12-16 09:20:09 +00:00
rcourtman
03182123f6 fix(sensor-proxy): remove pct pull fallback that causes PVE task errors
The installer had a fallback that tried to copy the sensor-proxy binary from
inside the Pulse container using `pct pull`. However, the paths it tried
(/opt/pulse/bin/pulse-sensor-proxy-linux-amd64) don't exist in Pulse containers.

When pct pull fails, Proxmox logs a task error visible in the UI, even though
the error is harmless and expected. This caused recurring "TASK ERROR: failed
to open /opt/pulse/bin/pulse-sensor-proxy-linux-amd64: No such file or directory"
messages every time the selfheal timer ran and couldn't download from GitHub.

The pct pull approach was never going to succeed for LXC-installed Pulse instances
since the binary doesn't exist at those paths inside the container. Removing it
eliminates the spurious error messages.

Related to #817
2025-12-16 00:02:34 +00:00
rcourtman
101fa62997 fix(security): show actual backend error instead of misleading DISABLE_AUTH message
The frontend was incorrectly assuming any 401/403 response from the
quick-setup endpoint meant the legacy DISABLE_AUTH flag was present.

This was wrong - the real causes are typically:
- Missing bootstrap token (for first-run setup)
- Invalid bootstrap token
- Authentication required for existing setups

Now the frontend properly displays the actual error message from the
backend, which includes helpful instructions for retrieving the
bootstrap token.
2025-12-15 22:30:24 +00:00
rcourtman
a3909f1b39 fix: strip provider prefix from all AI provider models and instant URL refresh
Backend fixes:
- Strip provider prefix (anthropic:, openai:, deepseek:, ollama:) in all
  provider Chat methods and constructors for robust handling
- Models are now correctly parsed regardless of caller format

Frontend fixes:
- Tool cards now persist in AI chat after approval execution by adding
  to streamEvents array
- Dashboard now listens for pulse:metadata-changed custom event
- AI Chat emits this event when set_resource_url tool completes
- Guest URL icons now update instantly when AI sets them
2025-12-15 19:18:09 +00:00
rcourtman
43d658556b feat(ai): improve AI settings first-time setup UX
- Add setup modal that appears when enabling AI without configured provider
- Modal allows selecting provider (Anthropic, OpenAI, DeepSeek, Ollama)
- Enter API key/URL and enable AI in one smooth flow
- Reorder backend to apply API keys before enabled check
- Fix Ollama to strip 'ollama:' prefix from model names
- Simplify backend error message for unconfigured providers
2025-12-15 18:59:19 +00:00
rcourtman
fe20b2c55b fix(ai): add fallback default model when Ollama model is empty
When model is not explicitly set in config or request, fall back to
llama3 to prevent 'model is required' errors from Ollama.
2025-12-15 16:59:51 +00:00
rcourtman
71783f8852 fix(ai): normalize Ollama base URL to prevent 405 errors
Users sometimes enter URLs with trailing slashes or include the /api path:
- http://host:11434/  -> would become http://host:11434//api/chat
- http://host:11434/api -> would become http://host:11434/api/api/chat

Now we strip trailing slashes and /api suffix during client initialization.

Fixes #847
2025-12-15 16:51:52 +00:00
rcourtman
0d2d71b238 Add sponsor button to repo 2025-12-15 16:27:10 +00:00
rcourtman
d43cb9fce9 fix(ci): pass explicit version to demo server update
Previously the workflow ran install.sh without --version, which caused it
to download the latest stable release instead of the target release tag.

This was causing the demo server to downgrade from RC versions to stable
when triggered via workflow_dispatch.
2025-12-15 16:11:49 +00:00
rcourtman
61da37a427 Auto-update Helm chart version to 5.0.0-rc.2 2025-12-15 14:46:45 +00:00
rcourtman
e7f85e5539 Auto-update Helm chart documentation 2025-12-15 14:46:41 +00:00
rcourtman
b6df03e989 perf(ci): use amd64-only for preflight staging images
Skip arm64 QEMU emulation in preflight tests - staging images are only
used for integration tests which run on amd64. This cuts ~20-30 minutes
off the release pipeline.

Multi-arch Docker images are still built in the final release job via
publish-docker.yml.
2025-12-15 14:27:34 +00:00
rcourtman
cb268727e4 fix(sensor-proxy): correctly skip selfheal regeneration during selfheal runs
The PULSE_SENSOR_PROXY_SELFHEAL env var is set to "1", but the check
was only looking for "true", causing selfheal to regenerate itself on
every run. This meant the cached installer would overwrite the selfheal
script with its (potentially older) version, defeating any fixes in
the selfheal script.

Now correctly checks for both "true" and "1".

Related to #849
2025-12-15 14:06:40 +00:00
rcourtman
92e8de4802 chore: bump version to 5.0.0-rc.2 2025-12-15 13:38:45 +00:00
rcourtman
2209565eae fix(sensor-proxy): skip selfheal reinstall when service is healthy
The selfheal timer was running the full installer every 5 minutes even when
the service was already running and healthy. This caused unnecessary:
- Pulse service restarts
- Config migrations
- Socket setup
- 172MB memory spikes and 15s CPU usage per run

Now the selfheal exits early after checking service health, only proceeding
to reinstall logic if the service is actually failing.

Fixes #849
2025-12-15 13:31:18 +00:00
rcourtman
b6cc57590a fix(ai): use configured provider's default model when no model set
When a user configures only Ollama (or any single provider) via the
multi-provider UI without explicitly selecting a model, GetModel() now
returns that provider's default model instead of falling back to the
legacy Provider field which defaults to "anthropic".

This fixes "API key is required for anthropic" errors when enabling AI
with only Ollama configured.

Related to #847
2025-12-15 11:18:05 +00:00
rcourtman
609aa98030 fix(sensor-proxy): skip prerelease versions in selfheal update check
The selfheal timer was downloading prerelease versions (e.g., v5.0.0-rc.1)
even when users had the stable channel selected. This happened because
fetch_latest_release_tag() didn't filter out prereleases from the GitHub
releases API response.

Now both the Python-based parser and the grep fallback skip prereleases:
- Python: checks `release.get("prerelease")` field
- Grep fallback: filters out -rc, -alpha, -beta patterns

Related to #849
2025-12-15 11:03:32 +00:00
rcourtman
06ae5295b3 fix(ai): send Ollama URL when using default localhost address
The form pre-fills the Ollama URL field with http://localhost:11434 but
compared it against that same default value to detect changes. This meant
users configuring Ollama with the default URL never actually saved it.

Also shows actual backend error messages instead of generic "Failed to update"
when toggling the AI enable switch.

Related to #847
2025-12-15 10:03:15 +00:00
rcourtman
eac510bb5e fix(ai): allow enabling AI when any provider is configured
The enable validation was using the legacy single-provider model which
checked settings.Provider and settings.APIKey. Users configuring Ollama
via the new multi-provider UI (setting ollama_base_url) couldn't enable
AI because settings.Provider defaulted to "anthropic" which required an
API key.

Now checks GetConfiguredProviders() first - if any provider is configured
(Anthropic, OpenAI, DeepSeek, or Ollama), AI can be enabled.

Related to #847
2025-12-15 09:43:17 +00:00
rcourtman
4bd9c3cf69 fix(sensor-proxy): prevent installer hang on control-plane sync
Fixes #819

The installer was hanging at 'Pending control-plane sync detected' because
systemctl start ran synchronously, waiting for the selfheal service to complete.
If the control-plane sync failed or took a long time (e.g., Proxmox node not
configured in Pulse yet), the installer would hang indefinitely.

Changed to use 'systemctl start --no-block' to run the selfheal service
asynchronously in the background. The sync will still be attempted, but the
installer will complete immediately and show the success message.
2025-12-15 08:39:06 +00:00
rcourtman
3bc2b4944b fix(sensor-proxy): add timeouts to pmxcfs operations in installer
The container config backup and pct commands could hang indefinitely
when the Proxmox cluster filesystem (pmxcfs) is slow or unresponsive.
This caused the installer to appear to hang after printing
"Configuring socket bind mount..." with no further output.

Added timeout protection to:
- Container config backup cp operation
- pct status check
- pct config verification
- Config rollback cp operation

Related to #738
2025-12-15 07:04:14 +00:00
rcourtman
854f89aa24 fix(agent): stop running agent before TrueNAS reinstall to avoid "text file busy"
On TrueNAS, the runtime binary may be in /root/bin or /var/tmp while
the install script only checked INSTALL_DIR (/data/pulse-agent).
This left the running process using the binary when the script tried
to copy a new version, causing "Text file busy" errors.

Now explicitly stop the service and kill any pulse-agent processes
before modifying binaries on TrueNAS systems.

Related to #846
2025-12-15 04:03:06 +00:00
rcourtman
5609f5a6af docs: fix Docker socket mount path for standalone sensor proxy
The standalone installer creates the socket at /mnt/pulse-proxy on the host,
not /run/pulse-sensor-proxy. Updated documentation to show the correct mount:
  /mnt/pulse-proxy:/run/pulse-sensor-proxy:ro

Related to #822
2025-12-15 02:08:15 +00:00
rcourtman
3b4c77de28 fix(ui): add CPU tooltip to Proxmox Overview. Related to #816 2025-12-15 02:06:12 +00:00
rcourtman
02c4131853 fix(agent): use /etc/machine-id in LXC containers to avoid ID collisions
LXC containers share the host's /sys/class/dmi/id/product_uuid, which
causes gopsutil to return identical HostIDs for all LXC containers on
the same physical host. This results in agent ID collisions where
multiple LXC containers appear as a single host in Pulse.

The fix detects LXC containers and prefers /etc/machine-id (which is
unique per container) over gopsutil's HostID.

Related to #773
2025-12-14 23:05:32 +00:00