feat: add --kube-include-all-deployments flag for Kubernetes agent

Adds IncludeAllDeployments option to show all deployments, not just
problem ones (where replicas don't match desired). This provides parity
with the existing --kube-include-all-pods flag.

- Add IncludeAllDeployments to kubernetesagent.Config
- Add --kube-include-all-deployments flag and PULSE_KUBE_INCLUDE_ALL_DEPLOYMENTS env var
- Update collectDeployments to respect the new flag
- Add test for IncludeAllDeployments functionality
- Update UNIFIED_AGENT.md documentation

Addresses feedback from PR #855
This commit is contained in:
rcourtman 2025-12-18 20:58:30 +00:00
parent bd7d2c22f7
commit ae70d9e423
48 changed files with 1903 additions and 686 deletions

View file

@ -32,9 +32,9 @@ Designed for homelabs, sysadmins, and MSPs who need a "single pane of glass" wit
### Pulse AI *(New in 5.0)* ### Pulse AI *(New in 5.0)*
- **Chat Assistant**: Ask questions about your infrastructure in natural language - **Chat Assistant**: Ask questions about your infrastructure in natural language
- **Patrol Mode**: Automated health checks with proactive issue detection - **Patrol**: Background health checks that generate findings on a schedule
- **Auto-Fix**: Automatically resolve common issues with AI-guided remediation - **Alert Analysis**: Optional AI analysis when alerts fire
- **Predictive Intelligence**: Forecast problems before they happen - **Cost Tracking**: Track usage and costs per provider/model
### Multi-Platform ### Multi-Platform
- **Proxmox VE/PBS/PMG**: Full monitoring and management - **Proxmox VE/PBS/PMG**: Full monitoring and management

View file

@ -41,14 +41,20 @@ environment where `PULSE_DOCKER=true`/`/.dockerenv` is detected.
#### Migration Path (Production) #### Migration Path (Production)
1. **Install the sensor proxy on the Proxmox host that runs Pulse** Preferred option (no SSH keys, no proxy wiring):
```bash
curl -fsSL https://github.com/rcourtman/Pulse/releases/latest/download/install-sensor-proxy.sh | \ 1. Install the unified agent (`pulse-agent`) on each Proxmox host with Proxmox integration enabled.
bash -s -- --ctid <pulse-lxc-id> --pulse-server https://pulse.example.com:7655 - Use the UI to generate an install command in **Settings → Agents**, or run:
``` ```bash
- For Docker or standalone hosts where Pulse cannot mount the socket (or when collecting temps from *other* nodes), run the installer with `--standalone --http-mode` so each host exposes its own HTTPS endpoint on port 8443. curl -fsSL http://pulse.example.com:7655/install.sh | \
2. **Let the installer manage the hardened systemd unit** it creates the dedicated `pulse-sensor-proxy` user, installs TLS material, and restarts the service safely. sudo bash -s -- --url http://pulse.example.com:7655 --token <api-token> --enable-proxmox
3. **Restart the Pulse container** so it binds to `/run/pulse-sensor-proxy` (for local temps) and registers the HTTPS proxy (for remote temps). The backend automatically prefers the HTTPS proxy, then the socket, and never stores SSH keys in the container. ```
Deprecated option (existing installs only):
- `pulse-sensor-proxy` is deprecated in Pulse v5 and is not recommended for new deployments.
- Existing installs continue to work during the migration window, but plan to move to `pulse-agent --enable-proxmox`.
- Canonical temperature docs: `docs/TEMPERATURE_MONITORING.md`
#### Removing Old SSH Keys #### Removing Old SSH Keys
@ -68,15 +74,15 @@ docker exec pulse rm -rf /home/pulse/.ssh/id_ed25519*
┌─────────────────────────────────────┐ ┌─────────────────────────────────────┐
│ Proxmox Host │ │ Proxmox Host │
│ ┌───────────────────────────────┐ │ │ ┌───────────────────────────────┐ │
│ │ pulse-sensor-proxy (svc user)│ │ │ │ pulse-agent │ │
│ │ · Runs sensors -j │ │ │ │ · Reads sensors locally │ │
│ │ · Exposes Unix socket + TLS │ │ │ │ · Sends metrics via HTTPS │ │
│ └───────────────────────────────┘ │ │ └───────────────────────────────┘ │
│ │ │ │ │ │
│ │ /run/pulse-sensor-proxy.sock │ │ HTTPS + API token │
│ │ │ │ │ │
│ ┌─────────▼─────────────────────┐ │ │ ┌─────────▼─────────────────────┐ │
│ │ Pulse container (bind mount) │ │ │ │ Pulse (Docker/LXC container) │ │
│ │ · No SSH keys │ │ │ │ · No SSH keys │ │
│ │ · No host root privileges │ │ │ │ · No host root privileges │ │
│ └───────────────────────────────┘ │ │ └───────────────────────────────┘ │
@ -95,14 +101,17 @@ rotate keys regularly.
```bash ```bash
# Detect vulnerable containers # Detect vulnerable containers
ls /home/pulse/.ssh/id_ed25519* 2>/dev/null && echo "⚠️ SSH keys present" ls /home/pulse/.ssh/id_ed25519* 2>/dev/null && echo "⚠️ SSH keys present"
# Check container logs for proxy detection
docker logs pulse | grep -i "temperature proxy detected"
# Verify the host service
systemctl status pulse-sensor-proxy
``` ```
Verify temperature collection is agent-based:
- UI: **Settings → Agents** shows each Proxmox host connected and reporting.
- On each Proxmox host:
```bash
systemctl status pulse-agent
journalctl -u pulse-agent -n 200 --no-pager
```
**Documentation:** https://github.com/rcourtman/Pulse/blob/main/SECURITY.md#critical-security-notice-for-container-deployments **Documentation:** https://github.com/rcourtman/Pulse/blob/main/SECURITY.md#critical-security-notice-for-container-deployments
**Issues:** https://github.com/rcourtman/pulse/issues **Issues:** https://github.com/rcourtman/pulse/issues
**Private disclosures:** security@pulseapp.io **Private disclosures:** security@pulseapp.io
@ -397,7 +406,7 @@ curl -H "Authorization: Bearer your-original-token" http://localhost:7655/api/ex
### Runtime Logging Configuration ### Runtime Logging Configuration
**New in v4.24.0:** Adjust logging settings dynamically without restarting Pulse. Pulse supports configurable logging (level, format, optional file output, rotation) via environment variables.
#### Security Benefits #### Security Benefits
- Enable debug logging temporarily for incident investigation - Enable debug logging temporarily for incident investigation
@ -407,13 +416,7 @@ curl -H "Authorization: Bearer your-original-token" http://localhost:7655/api/ex
#### Configuration Options #### Configuration Options
**Via UI:** **Via environment variables:**
Navigate to **Settings → System → Logging**:
- **Log Level**: `debug`, `info`, `warn`, `error`
- **Log Format**: `json` (for log aggregation), `text` (human-readable)
- **File Rotation**: size limits, retention policies
**Via Environment Variables:**
```bash ```bash
# Systemd # Systemd
sudo systemctl edit pulse sudo systemctl edit pulse
@ -421,16 +424,16 @@ sudo systemctl edit pulse
Environment="LOG_LEVEL=info" Environment="LOG_LEVEL=info"
Environment="LOG_FORMAT=json" Environment="LOG_FORMAT=json"
Environment="LOG_MAX_SIZE=100" # MB per log file Environment="LOG_MAX_SIZE=100" # MB per log file
Environment="LOG_MAX_BACKUPS=10" # Number of rotated logs to keep
Environment="LOG_MAX_AGE=30" # Days to retain logs Environment="LOG_MAX_AGE=30" # Days to retain logs
Environment="LOG_COMPRESS=true" # Compress rotated logs
# Docker # Docker
docker run \ docker run \
-e LOG_LEVEL=info \ -e LOG_LEVEL=info \
-e LOG_FORMAT=json \ -e LOG_FORMAT=json \
-e LOG_MAX_SIZE=100 \ -e LOG_MAX_SIZE=100 \
-e LOG_MAX_BACKUPS=10 \
-e LOG_MAX_AGE=30 \ -e LOG_MAX_AGE=30 \
-e LOG_COMPRESS=true \
rcourtman/pulse:latest rcourtman/pulse:latest
``` ```
@ -438,7 +441,7 @@ docker run \
- Debug logs may contain sensitive data—enable only when needed - Debug logs may contain sensitive data—enable only when needed
- JSON format recommended for security monitoring and SIEM - JSON format recommended for security monitoring and SIEM
- Adjust retention based on compliance requirements - Adjust retention based on compliance requirements
- Changes are logged to audit trail - Changes take effect on restart
## CORS (Cross-Origin Resource Sharing) ## CORS (Cross-Origin Resource Sharing)
@ -499,8 +502,7 @@ curl -s http://localhost:7655/api/monitoring/scheduler/health | jq
- **Backoff Delays**: Increased backoff may indicate rate limiting or errors - **Backoff Delays**: Increased backoff may indicate rate limiting or errors
- **Error Rates**: Track failed API calls and authentication attempts - **Error Rates**: Track failed API calls and authentication attempts
**Dashboard Access:** There is currently no dedicated scheduler-health UI in v5. Use the API endpoint above (or export diagnostics from **Settings → Diagnostics**) when troubleshooting.
Navigate to **Settings → System → Monitoring** for visual representation of scheduler health.
## Security Best Practices ## Security Best Practices

View file

@ -205,10 +205,11 @@ func main() {
Logger: &logger, Logger: &logger,
KubeconfigPath: cfg.KubeconfigPath, KubeconfigPath: cfg.KubeconfigPath,
KubeContext: cfg.KubeContext, KubeContext: cfg.KubeContext,
IncludeNamespaces: cfg.KubeIncludeNamespaces, IncludeNamespaces: cfg.KubeIncludeNamespaces,
ExcludeNamespaces: cfg.KubeExcludeNamespaces, ExcludeNamespaces: cfg.KubeExcludeNamespaces,
IncludeAllPods: cfg.KubeIncludeAllPods, IncludeAllPods: cfg.KubeIncludeAllPods,
MaxPods: cfg.KubeMaxPods, IncludeAllDeployments: cfg.KubeIncludeAllDeployments,
MaxPods: cfg.KubeMaxPods,
} }
agent, err := kubernetesagent.New(kubeCfg) agent, err := kubernetesagent.New(kubeCfg)
@ -336,10 +337,11 @@ type Config struct {
// Kubernetes // Kubernetes
KubeconfigPath string KubeconfigPath string
KubeContext string KubeContext string
KubeIncludeNamespaces []string KubeIncludeNamespaces []string
KubeExcludeNamespaces []string KubeExcludeNamespaces []string
KubeIncludeAllPods bool KubeIncludeAllPods bool
KubeMaxPods int KubeIncludeAllDeployments bool
KubeMaxPods int
} }
func loadConfig() Config { func loadConfig() Config {
@ -364,6 +366,7 @@ func loadConfig() Config {
envKubeIncludeNamespaces := utils.GetenvTrim("PULSE_KUBE_INCLUDE_NAMESPACES") envKubeIncludeNamespaces := utils.GetenvTrim("PULSE_KUBE_INCLUDE_NAMESPACES")
envKubeExcludeNamespaces := utils.GetenvTrim("PULSE_KUBE_EXCLUDE_NAMESPACES") envKubeExcludeNamespaces := utils.GetenvTrim("PULSE_KUBE_EXCLUDE_NAMESPACES")
envKubeIncludeAllPods := utils.GetenvTrim("PULSE_KUBE_INCLUDE_ALL_PODS") envKubeIncludeAllPods := utils.GetenvTrim("PULSE_KUBE_INCLUDE_ALL_PODS")
envKubeIncludeAllDeployments := utils.GetenvTrim("PULSE_KUBE_INCLUDE_ALL_DEPLOYMENTS")
envKubeMaxPods := utils.GetenvTrim("PULSE_KUBE_MAX_PODS") envKubeMaxPods := utils.GetenvTrim("PULSE_KUBE_MAX_PODS")
// Defaults // Defaults
@ -418,6 +421,7 @@ func loadConfig() Config {
kubeconfigFlag := flag.String("kubeconfig", envKubeconfig, "Path to kubeconfig (optional; uses in-cluster config if available)") kubeconfigFlag := flag.String("kubeconfig", envKubeconfig, "Path to kubeconfig (optional; uses in-cluster config if available)")
kubeContextFlag := flag.String("kube-context", envKubeContext, "Kubeconfig context (optional)") kubeContextFlag := flag.String("kube-context", envKubeContext, "Kubeconfig context (optional)")
kubeIncludeAllPodsFlag := flag.Bool("kube-include-all-pods", utils.ParseBool(envKubeIncludeAllPods), "Include all non-succeeded pods (may be large)") kubeIncludeAllPodsFlag := flag.Bool("kube-include-all-pods", utils.ParseBool(envKubeIncludeAllPods), "Include all non-succeeded pods (may be large)")
kubeIncludeAllDeploymentsFlag := flag.Bool("kube-include-all-deployments", utils.ParseBool(envKubeIncludeAllDeployments), "Include all deployments, not just problem ones")
kubeMaxPodsFlag := flag.Int("kube-max-pods", defaultInt(envKubeMaxPods, 200), "Max pods included in report") kubeMaxPodsFlag := flag.Int("kube-max-pods", defaultInt(envKubeMaxPods, 200), "Max pods included in report")
showVersion := flag.Bool("version", false, "Print the agent version and exit") showVersion := flag.Bool("version", false, "Print the agent version and exit")
@ -474,10 +478,11 @@ func loadConfig() Config {
HealthAddr: strings.TrimSpace(*healthAddrFlag), HealthAddr: strings.TrimSpace(*healthAddrFlag),
KubeconfigPath: strings.TrimSpace(*kubeconfigFlag), KubeconfigPath: strings.TrimSpace(*kubeconfigFlag),
KubeContext: strings.TrimSpace(*kubeContextFlag), KubeContext: strings.TrimSpace(*kubeContextFlag),
KubeIncludeNamespaces: kubeIncludeNamespaces, KubeIncludeNamespaces: kubeIncludeNamespaces,
KubeExcludeNamespaces: kubeExcludeNamespaces, KubeExcludeNamespaces: kubeExcludeNamespaces,
KubeIncludeAllPods: *kubeIncludeAllPodsFlag, KubeIncludeAllPods: *kubeIncludeAllPodsFlag,
KubeMaxPods: *kubeMaxPodsFlag, KubeIncludeAllDeployments: *kubeIncludeAllDeploymentsFlag,
KubeMaxPods: *kubeMaxPodsFlag,
} }
} }

View file

@ -1,9 +1,9 @@
# pulse-sensor-proxy # pulse-sensor-proxy
> **⚠️ Deprecated:** The sensor-proxy is deprecated in favor of the unified Pulse agent. > **Deprecated in v5:** `pulse-sensor-proxy` is deprecated and not recommended for new deployments.
> For new installations, use `install.sh --enable-proxmox` instead. The agent provides > Temperature monitoring should be done via the unified agent (`pulse-agent --enable-proxmox`).
> temperature monitoring plus additional features (AI, automatic Proxmox API setup). > This README is retained for existing installations during the migration window.
> See [TEMPERATURE_MONITORING.md](/docs/security/TEMPERATURE_MONITORING.md) for details. > See `docs/TEMPERATURE_MONITORING.md`.
The sensor proxy keeps SSH identities and temperature polling logic on the The sensor proxy keeps SSH identities and temperature polling logic on the
Proxmox host while presenting a small RPC surface (Unix socket or HTTPS) to the Proxmox host while presenting a small RPC surface (Unix socket or HTTPS) to the
@ -16,7 +16,7 @@ capabilities, and produces append-only audit logs.
| --- | --- | | --- | --- |
| **Recommended (automated)** | `curl -fsSL https://github.com/rcourtman/Pulse/releases/latest/download/install-sensor-proxy.sh \| bash` | | **Recommended (automated)** | `curl -fsSL https://github.com/rcourtman/Pulse/releases/latest/download/install-sensor-proxy.sh \| bash` |
| **Manual build** | `go build ./cmd/pulse-sensor-proxy` and `sudo install -m 0755 pulse-sensor-proxy /usr/local/bin/` | | **Manual build** | `go build ./cmd/pulse-sensor-proxy` and `sudo install -m 0755 pulse-sensor-proxy /usr/local/bin/` |
| **Prebuilt artifact** | Copy the binary from `/opt/pulse/bin/pulse-sensor-proxy-*` inside the Pulse Docker image or download via `/download/pulse-sensor-proxy?platform=linux&arch=amd64`. | | **Prebuilt artifact** | Download `pulse-sensor-proxy-<platform>-<arch>` from GitHub Releases, or copy it from `/opt/pulse/bin/` inside the Pulse Docker image. |
The installer script provisions: The installer script provisions:
@ -176,7 +176,7 @@ Set alerts on:
| Symptom | Guidance | | Symptom | Guidance |
| --- | --- | | --- | --- |
| Service fails to start with "Config validation failed" | Run `pulse-sensor-proxy config validate` to see specific errors. Check for duplicate keys or malformed YAML. | | Service fails to start with "Config validation failed" | Run `pulse-sensor-proxy config validate` to see specific errors. Check for duplicate keys or malformed YAML. |
| Config corruption detected during startup | Older versions had dual code paths. Update to v4.31.1+ and reinstall proxy. The migration runs automatically. | | Config corruption detected during startup | Older versions had dual code paths. Update to the latest release and reinstall the proxy. The migration runs automatically. |
| Temperature monitoring stops working after config change | Validate config first with `pulse-sensor-proxy config validate`, then restart service: `systemctl restart pulse-sensor-proxy`. | | Temperature monitoring stops working after config change | Validate config first with `pulse-sensor-proxy config validate`, then restart service: `systemctl restart pulse-sensor-proxy`. |
| `Cannot open audit log file` | Check permissions on `/var/log/pulse/sensor-proxy`. Remove `chattr +a` only during rotation. | | `Cannot open audit log file` | Check permissions on `/var/log/pulse/sensor-proxy`. Remove `chattr +a` only during rotation. |
| `connection denied` in audit log | UID/GID not listed in `allowed_peers`. Verify Pulse container UID mapping. | | `connection denied` in audit log | UID/GID not listed in `allowed_peers`. Verify Pulse container UID mapping. |

View file

@ -2,7 +2,9 @@
![Version: 5.0.0-rc.4](https://img.shields.io/badge/Version-5.0.0--rc.4-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 5.0.0-rc.4](https://img.shields.io/badge/AppVersion-5.0.0--rc.4-informational?style=flat-square) ![Version: 5.0.0-rc.4](https://img.shields.io/badge/Version-5.0.0--rc.4-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 5.0.0-rc.4](https://img.shields.io/badge/AppVersion-5.0.0--rc.4-informational?style=flat-square)
Helm chart for deploying the Pulse hub and optional Docker monitoring agent. Helm chart for deploying the Pulse server and an optional legacy Docker monitoring agent.
Note: the `agent.*` values currently deploy `pulse-docker-agent`, which is deprecated in favor of the unified agent (`pulse-agent`) where possible.
**Homepage:** <https://github.com/rcourtman/Pulse> **Homepage:** <https://github.com/rcourtman/Pulse>

View file

@ -98,6 +98,8 @@ server:
failureThreshold: 3 failureThreshold: 3
agent: agent:
# Legacy: this deploys the deprecated `pulse-docker-agent`.
# For new deployments, prefer installing the unified agent (`pulse-agent`) on the hosts you want to monitor.
enabled: false enabled: false
kind: DaemonSet # Supported: DaemonSet | Deployment kind: DaemonSet # Supported: DaemonSet | Deployment
replicaCount: 1 replicaCount: 1

View file

@ -1,123 +1,64 @@
# Pulse AI # Pulse AI
Pulse AI is an intelligent monitoring assistant that helps you understand and manage your infrastructure through natural language conversations, proactive monitoring, and automated issue resolution. Pulse AI adds an optional assistant for troubleshooting, summarization, and proactive monitoring. It is **off by default** and can be enabled per instance.
## Features ## What Pulse AI Can Do
### Chat Assistant - **Interactive chat**: Ask questions about current cluster state and recent health signals.
Ask questions about your infrastructure in plain English: - **Patrol**: Background checks that generate findings on a schedule.
- "What's using the most CPU right now?" - **Alert analysis**: Optional analysis when alerts fire (token-efficient).
- "Show me VMs with high memory usage" - **Command proposals and execution**: When enabled, Pulse can propose commands and (optionally) execute them via connected agents.
- "Why is my backup failing?" - **Cost tracking**: Tracks usage and supports a monthly budget target.
- "Summarize the health of my cluster"
### Patrol Mode
Automated monitoring that runs on a schedule to:
- Analyze resource utilization patterns
- Detect potential issues before they cause problems
- Generate actionable recommendations
- Track trends over time
### Auto-Fix
Automatically resolve common issues:
- Restart stuck services
- Clear disk space
- Restart unresponsive containers
- Apply recommended optimizations
### Predictive Intelligence
- Identify resources trending toward problems
- Forecast disk space exhaustion
- Detect unusual patterns
- Alert on anomalies
## Configuration ## Configuration
### Enable Pulse AI Configure in the UI:
1. Navigate to **Settings → AI** - **Settings → AI**
2. Toggle **Enable Pulse AI**
3. Configure your AI provider AI settings are stored encrypted at rest in `ai.enc` under the Pulse config directory (`/etc/pulse` for systemd installs, `/data` for Docker/Kubernetes).
### Supported Providers ### Supported Providers
| Provider | Models | Notes | Pulse supports multiple providers configured independently:
|----------|--------|-------|
| **OpenAI** | GPT-4, GPT-4 Turbo, GPT-3.5 | Recommended for best results |
| **Anthropic** | Claude 3 Opus, Sonnet, Haiku | Excellent for complex analysis |
| **Ollama** | Llama 3, Mistral, etc. | Self-hosted, privacy-focused |
| **OpenRouter** | Multiple models | Pay-per-use routing |
| **Custom** | Any OpenAI-compatible API | For enterprise deployments |
### API Key Setup - **Anthropic** (API key or OAuth)
- **OpenAI**
- **DeepSeek**
- **Google Gemini**
- **Ollama** (self-hosted)
- **OpenAI-compatible base URL** (for providers that implement the OpenAI API shape)
```bash ### Models
# Environment variable (recommended for production)
export PULSE_AI_PROVIDER=openai
export PULSE_AI_API_KEY=sk-...
# Or configure via Settings UI Pulse uses model identifiers in the form:
```
### Patrol Configuration - `provider:model-name`
| Setting | Default | Description | You can set separate models for:
|---------|---------|-------------|
| Patrol Enabled | Off | Run automated checks |
| Patrol Interval | 30 minutes | How often to run patrol |
| Auto-Fix Enabled | Off | Allow automatic remediation |
| Auto-Fix Model | Same as chat | Model for auto-fix decisions |
## Usage - Chat (`chat_model`)
- Patrol (`patrol_model`)
- Auto-fix remediation (`auto_fix_model`)
### Chat Interface ### Testing and Model Discovery
Access the AI chat from the bottom-right corner of any page. Type your question and press Enter.
**Example queries:** - Test provider connectivity: `POST /api/ai/test` and `POST /api/ai/test/{provider}`
``` - List available models (queried live from the provider): `GET /api/ai/models`
"What VMs are using more than 80% memory?"
"Show me the status of all backups"
"Why is node pve1 showing high load?"
"Compare resource usage between this week and last week"
```
### Context Selection ## Safety Controls
Click on any resource (VM, container, host) to add it to the AI context. This helps the AI provide more specific answers.
### Patrol Reports Pulse includes settings that control how “active” AI features are:
When Patrol is enabled, Pulse AI will:
1. Run periodic health checks
2. Generate findings (issues, warnings, info)
3. Offer to auto-fix issues (if enabled)
4. Track trends over time
## Cost Tracking - **Autonomous mode** (`autonomous_mode`): when enabled, AI may execute actions without a separate approval step in the UI.
- **Patrol auto-fix** (`patrol_auto_fix`): allows patrol findings to trigger remediation attempts.
- **Alert-triggered analysis** (`alert_triggered_analysis`): limits AI to analyzing specific events when alerts occur.
Track AI API usage in **Settings → AI → Cost Dashboard**: If you enable execution features, ensure agent tokens and scopes are appropriately restricted and that audit logging is enabled.
- Daily/monthly usage statistics
- Cost breakdown by feature
- Usage trends over time
## Privacy & Security
- **Data stays local**: Only resource metadata is sent to AI providers
- **No training**: Your data is never used for model training
- **Audit logging**: All AI interactions are logged
- **Self-hosted option**: Use Ollama for complete data privacy
## Troubleshooting ## Troubleshooting
### AI not responding - **AI not responding**: verify provider credentials in **Settings → AI** and confirm `GET /api/ai/models` works.
1. Check API key is configured correctly - **OAuth issues (Anthropic)**: verify the OAuth flow is completing and that Pulse can reach the callback endpoint.
2. Verify network connectivity to AI provider - **No execution capability**: confirm at least one compatible agent is connected and that the instance has execution enabled.
3. Check Settings → AI for error messages
### Patrol not running
1. Ensure Patrol is enabled in Settings
2. Check system resource availability
3. Review logs: `journalctl -u pulse -f`
### Auto-fix not working
1. Enable Auto-Fix in Settings → AI
2. Verify the connected agents have execute permissions
3. Check the Auto-Fix model is configured

View file

@ -115,8 +115,12 @@ Triggers a test alert to all configured channels.
## ⚙️ System Settings ## ⚙️ System Settings
### Get Settings ### Get Settings
`GET /api/config/system` `GET /api/system/settings`
Retrieve current system configuration. Retrieve current system settings.
### Update Settings
`POST /api/system/settings/update`
Update system settings. Requires admin + `settings:write`.
### Toggle Mock Mode ### Toggle Mock Mode
`POST /api/system/mock-mode` `POST /api/system/mock-mode`
@ -140,34 +144,47 @@ Initiate OIDC login flow.
--- ---
## 🤖 Pulse AI *(New in 5.0)* ## 🤖 Pulse AI *(v5)*
### Get AI Settings ### Get AI Settings
`GET /api/settings/ai` `GET /api/settings/ai`
Returns current AI configuration (providers, models, patrol status). Returns current AI configuration (providers, models, patrol status). Requires admin + `settings:read`.
### Update AI Settings ### Update AI Settings
`PUT /api/settings/ai` `PUT /api/settings/ai/update` (or `POST /api/settings/ai/update`)
Configure AI providers, API keys, and preferences. Configure AI providers, API keys, and preferences. Requires admin + `settings:write`.
### Chat ### List Models
`POST /api/ai/chat` `GET /api/ai/models`
Send a message to the AI assistant. Lists models available to the configured providers (queried live from provider APIs).
```json
{ "message": "What VMs are using the most CPU?", "context": ["vm-100", "vm-101"] }
```
### Patrol Status ### Execute (Chat + Tools)
`GET /api/ai/patrol/status` `POST /api/ai/execute`
Get current patrol status and recent findings. Runs an AI request which may return tool calls, findings, or suggested actions.
### Patrol Findings ### Execute (Streaming)
`GET /api/ai/patrol/findings` `POST /api/ai/execute/stream`
List all patrol findings with severity and recommendations. Streaming variant of execute (used by the UI for incremental responses).
### Patrol
- `GET /api/ai/patrol/status`
- `GET /api/ai/patrol/findings`
- `GET /api/ai/patrol/history`
- `POST /api/ai/patrol/run` (admin)
### Cost Tracking ### Cost Tracking
`GET /api/ai/cost?period=30d` `GET /api/ai/cost/summary`
Get AI usage statistics and costs. Get AI usage statistics (includes retention window details).
## 📈 Metrics Store (v5)
### Store Stats
`GET /api/metrics-store/stats`
Returns stats for the persistent metrics store (SQLite-backed).
### History
`GET /api/metrics-store/history`
Returns historical metric series for a resource and time range.
--- ---
@ -181,6 +198,10 @@ The unified agent combines host, Docker, and Kubernetes monitoring. Use `--enabl
See [UNIFIED_AGENT.md](UNIFIED_AGENT.md) for installation instructions. See [UNIFIED_AGENT.md](UNIFIED_AGENT.md) for installation instructions.
### Unified Agent Installer Script
`GET /install.sh`
Serves the universal `install.sh` used to install `pulse-agent` on target machines.
### Legacy Agents (Deprecated) ### Legacy Agents (Deprecated)
`GET /download/pulse-host-agent` - *Deprecated, use pulse-agent* `GET /download/pulse-host-agent` - *Deprecated, use pulse-agent*
`GET /download/pulse-docker-agent` - *Deprecated, use pulse-agent --enable-docker* `GET /download/pulse-docker-agent` - *Deprecated, use pulse-agent --enable-docker*

View file

@ -15,7 +15,7 @@ Pulse 5.0 introduces one-click updates for supported deployment types, making it
### When an Update is Available ### When an Update is Available
1. Navigate to **Settings → System Updates** 1. Navigate to **Settings → System Updates**
2. If an update is available, you'll see an **"Install Update"** button 2. If an update is available, you'll see an **"Install Update"** button
3. Click the button to open the confirmation dialog 3. Click the button to open the confirmation dialog
4. Review the update details: 4. Review the update details:
@ -44,7 +44,7 @@ A real-time progress modal shows:
### Update Preferences ### Update Preferences
In **Settings → System Updates**: In **Settings → System Updates**:
| Setting | Description | | Setting | Description |
|---------|-------------| |---------|-------------|
@ -67,7 +67,7 @@ PULSE_UPDATE_CHANNEL=rc
```bash ```bash
# Pull latest image # Pull latest image
docker pull ghcr.io/rcourtman/pulse:latest docker pull rcourtman/pulse:latest
# Restart container # Restart container
docker-compose down && docker-compose up -d docker-compose down && docker-compose up -d
@ -76,21 +76,19 @@ docker-compose down && docker-compose up -d
### ProxmoxVE LXC (Manual) ### ProxmoxVE LXC (Manual)
```bash ```bash
# Inside the container curl -fsSL https://github.com/rcourtman/Pulse/releases/latest/download/install.sh | bash
curl -fsSL https://raw.githubusercontent.com/rcourtman/Pulse/main/scripts/install.sh | bash
``` ```
### Systemd Service (Manual) ### Systemd Service (Manual)
```bash ```bash
# Download new release curl -fsSL https://github.com/rcourtman/Pulse/releases/latest/download/install.sh | bash
curl -fsSL https://raw.githubusercontent.com/rcourtman/Pulse/main/scripts/install.sh | bash
``` ```
### Source Build ### Source Build
```bash ```bash
cd /opt/pulse cd /path/to/pulse
git pull git pull
make build make build
sudo systemctl restart pulse sudo systemctl restart pulse
@ -107,17 +105,13 @@ Pulse creates a backup before updating. If the update fails:
3. Error details are logged 3. Error details are logged
### Manual Rollback ### Manual Rollback
```bash If rollback is supported for your deployment, use the **Rollback** action from the update history in **Settings → System → Updates**.
# Backups are stored in /etc/pulse/backups/
ls /etc/pulse/backups/
# Restore a specific backup Backups are stored as `backup-<timestamp>/` folders inside the Pulse data directory (`/etc/pulse` or `/data`).
sudo /opt/pulse/scripts/restore-backup.sh /etc/pulse/backups/pulse-backup-20250101.tar.gz
```
## Update History ## Update History
View past updates in **Settings → System Updates → Update History**: View past updates in **Settings → System Updates → Update History**:
- Previous versions installed - Previous versions installed
- Update timestamps - Update timestamps
- Success/failure status - Success/failure status

View file

@ -8,6 +8,13 @@ Pulse uses a split-configuration model to ensure security and flexibility.
| `system.json` | General Settings | 📝 Standard | | `system.json` | General Settings | 📝 Standard |
| `nodes.enc` | Node Credentials | 🔒 **Encrypted** (AES-256-GCM) | | `nodes.enc` | Node Credentials | 🔒 **Encrypted** (AES-256-GCM) |
| `alerts.json` | Alert Rules | 📝 Standard | | `alerts.json` | Alert Rules | 📝 Standard |
| `email.enc` | SMTP settings | 🔒 **Encrypted** |
| `webhooks.enc` | Webhook URLs + headers | 🔒 **Encrypted** |
| `apprise.enc` | Apprise notification config | 🔒 **Encrypted** |
| `oidc.enc` | OIDC provider config | 🔒 **Encrypted** |
| `api_tokens.json` | API token records (hashed) | 🔒 **Sensitive** |
| `ai.enc` | AI settings and credentials | 🔒 **Encrypted** |
| `metrics.db` | Persistent metrics history (SQLite) | 📝 Standard |
All files are located in `/etc/pulse/` (Systemd) or `/data/` (Docker/Kubernetes). All files are located in `/etc/pulse/` (Systemd) or `/data/` (Docker/Kubernetes).
@ -67,7 +74,7 @@ Controls runtime behavior like ports, logging, and polling intervals. Most of th
"frontendPort": 7655, // Public port "frontendPort": 7655, // Public port
"logLevel": "info", // debug, info, warn, error "logLevel": "info", // debug, info, warn, error
"autoUpdateEnabled": false, // Enable auto-update checks "autoUpdateEnabled": false, // Enable auto-update checks
"adaptivePollingEnabled": true // Smart polling for large clusters "adaptivePollingEnabled": false // Smart polling for large clusters
} }
``` ```
@ -97,11 +104,22 @@ Environment variables take precedence over `system.json`.
| `PMG_POLLING_INTERVAL` | PMG metrics polling frequency | `60s` | | `PMG_POLLING_INTERVAL` | PMG metrics polling frequency | `60s` |
| `ENABLE_BACKUP_POLLING` | Enable backup job monitoring | `true` | | `ENABLE_BACKUP_POLLING` | Enable backup job monitoring | `true` |
| `BACKUP_POLLING_INTERVAL` | Backup polling frequency | `0` (Auto) | | `BACKUP_POLLING_INTERVAL` | Backup polling frequency | `0` (Auto) |
| `ENABLE_TEMPERATURE_MONITORING` | Enable SSH temperature checks | `true` | | `ENABLE_TEMPERATURE_MONITORING` | Enable temperature monitoring (where supported) | `true` |
| `SSH_PORT` | SSH port for temperature checks | `22` | | `SSH_PORT` | SSH port for legacy SSH-based temperature collection | `22` |
| `ADAPTIVE_POLLING_ENABLED` | Enable smart polling for large clusters | `false` | | `ADAPTIVE_POLLING_ENABLED` | Enable smart polling for large clusters | `false` |
| `WEBHOOK_BATCH_DELAY` | Delay before sending batched webhooks | `10s` | | `WEBHOOK_BATCH_DELAY` | Delay before sending batched webhooks | `10s` |
### Metrics Retention (Tiered)
Persistent metrics history uses tiered retention windows. These values are stored in `system.json` and can be adjusted for storage vs history depth:
- `metricsRetentionRawHours`
- `metricsRetentionMinuteHours`
- `metricsRetentionHourlyDays`
- `metricsRetentionDailyDays`
See [METRICS_HISTORY.md](METRICS_HISTORY.md) for details.
--- ---
## 🔔 Alerts (`alerts.json`) ## 🔔 Alerts (`alerts.json`)

62
docs/DEPLOYMENT_MODELS.md Normal file
View file

@ -0,0 +1,62 @@
# Deployment Models
Pulse supports multiple deployment models. This page clarifies what differs between them and where “truth” lives (paths, updates, and operational constraints).
## Summary
| Model | Recommended for | Data/config path | Updates |
| --- | --- | --- | --- |
| Proxmox VE LXC (installer) | Proxmox-first deployments | `/etc/pulse` | In-app updates supported |
| systemd (bare metal / VM) | Traditional Linux hosts | `/etc/pulse` | In-app updates supported |
| Docker | Quick evaluation and container stacks | `/data` (bind mount / volume) | Image pull + restart |
| Kubernetes (Helm) | Cluster operators | `/data` (PVC) | Helm upgrade |
## Common Ports
- UI/API: `7655/tcp`
- Prometheus metrics: `9091/tcp` (`/metrics` on a separate listener)
Docker and Kubernetes do not publish `9091` unless you explicitly expose it.
## Where Configuration Lives
Pulse uses a split config model:
- **Local auth and secrets**: `.env` (not editable in the UI)
- **System settings**: `system.json` (editable in the UI unless locked by env)
- **Nodes and credentials**: `nodes.enc` (encrypted)
- **AI config**: `ai.enc` (encrypted)
- **Metrics history**: `metrics.db` (SQLite)
Path mapping:
- systemd/LXC: `/etc/pulse/*`
- Docker/Helm: `/data/*`
## Updates by Model
### systemd and Proxmox LXC
Use the UI:
- **Settings → System → Updates**
These deployments can apply updates by downloading a release and swapping binaries/config safely with backups and history.
### Docker
Pull a new image and restart:
```bash
docker pull rcourtman/pulse:latest
docker compose up -d
```
### Kubernetes (Helm)
Upgrade the chart (OCI):
```bash
helm upgrade pulse oci://ghcr.io/rcourtman/pulse-chart -n pulse
```

View file

@ -105,7 +105,9 @@ docker compose up -d
```bash ```bash
docker exec pulse rm /data/.env docker exec pulse rm /data/.env
docker restart pulse docker restart pulse
# Access UI to run setup wizard again # Access UI again. Pulse will require a bootstrap token for setup.
# Get it with:
docker exec pulse /app/pulse bootstrap-token
``` ```
- **Logs** - **Logs**

317
docs/DOCS_AUDIT_V5.md Normal file
View file

@ -0,0 +1,317 @@
# Pulse v5 Documentation Audit (pre-stable)
This is a working audit of Pulse documentation as of `VERSION=5.0.0-rc.4`, focused on release readiness for a v5 stable cut.
## Status (updated 2025-12-18)
Most of the issues identified in this audit have been addressed in-repo:
- Updated install recommendation and bootstrap-token guidance across entrypoints (`README.md`, `docs/INSTALL.md`, `docs/FAQ.md`, `docs/TROUBLESHOOTING.md`, `docs/DOCKER.md`)
- Rewritten AI and API docs to match the current v5 implementation (`docs/AI.md`, `docs/API.md`)
- Rewritten metrics history docs to match SQLite store + tiered retention (`docs/METRICS_HISTORY.md`)
- Fixed adaptive polling defaults and rollout paths (`docs/monitoring/ADAPTIVE_POLLING.md`, `docs/operations/ADAPTIVE_POLLING_ROLLOUT.md`)
- Reduced temperature monitoring contradictions by making the agent the recommended path and scoping sensor-proxy as a legacy/alternative (`docs/TEMPERATURE_MONITORING.md`, `docs/security/TEMPERATURE_MONITORING.md`, `SECURITY.md`, sensor-proxy docs)
- Updated Helm/Kubernetes docs to prefer OCI distribution and flag the legacy agent block (`docs/KUBERNETES.md`, `deploy/helm/pulse/README.md`, `deploy/helm/pulse/values.yaml`)
- Added missing “operator clarity” docs (`docs/DEPLOYMENT_MODELS.md`, `docs/UPGRADE_v5.md`)
- Link validation run: no broken relative `.md` links found at time of update
## Goals
- Identify docs that are **stale**, **contradictory**, or **redundant**
- Identify **missing docs** needed for a v5 stable release
- Produce an actionable “what to change, where” checklist
## Highest-Priority Fixes (release-blockers)
### 1) Temperature monitoring guidance is contradictory
There are multiple competing “truths” about how temperature monitoring works in v5:
- `SECURITY.md` describes container deployments as requiring `pulse-sensor-proxy` and explicitly blocks SSH-based temps in containers.
- Multiple docs under `docs/security/` and `cmd/pulse-sensor-proxy/README.md` claim `pulse-sensor-proxy` is deprecated in favor of the unified agent.
- `docs/TEMPERATURE_MONITORING.md` is an extensive sensor-proxy-first guide and reads as “current”, but conflicts with the “deprecated” banner elsewhere.
- The backend still has extensive support and UX flows for sensor proxy install/register (`/api/install/install-sensor-proxy.sh`, temperature proxy diagnostics, container SSH blocking guidance).
Action:
- Decide the **canonical** v5 story:
- **Option A (agent-first)**: “Install `pulse-agent --enable-proxmox` on each Proxmox host for temperatures and management. `pulse-sensor-proxy` is legacy or edge-case only.”
- **Option B (proxy-first for containers)**: “If Pulse runs in Docker/LXC, temperatures require `pulse-sensor-proxy` (socket/HTTPS). The agent is optional for other features.”
- Update all docs to align with the chosen story, and ensure `SECURITY.md` reflects it unambiguously.
Status:
- Docs updated to be agent-first, with `pulse-sensor-proxy` treated as a legacy/alternative option.
- Remaining work is primarily product positioning and long-term deprecation decisions, not broken documentation.
Files involved:
- `SECURITY.md`
- `docs/TEMPERATURE_MONITORING.md`
- `docs/security/TEMPERATURE_MONITORING.md`
- `docs/security/SENSOR_PROXY_HARDENING.md`
- `docs/security/SENSOR_PROXY_NETWORK.md`
- `docs/security/SENSOR_PROXY_APPARMOR.md`
- `docs/operations/SENSOR_PROXY_CONFIG.md`
- `docs/operations/SENSOR_PROXY_LOGS.md`
- `cmd/pulse-sensor-proxy/README.md`
### 2) AI docs do not match the actual v5 API and configuration model
`docs/AI.md` and the AI section in `docs/API.md` appear written for an older/alternate API surface:
- `docs/AI.md` documents `PULSE_AI_PROVIDER` and `PULSE_AI_API_KEY` env vars, but the current implementation persists encrypted AI config in `ai.enc` and supports multi-provider credentials (Anthropic/OpenAI/DeepSeek/Gemini/Ollama) plus Anthropic OAuth.
- `docs/API.md` references endpoints like `POST /api/ai/chat` and `PUT /api/settings/ai` that do not match the router (current endpoints include `/api/ai/execute`, `/api/ai/models`, `/api/settings/ai/update`, OAuth endpoints, patrol stream, cost summary).
Action:
- Rewrite AI docs to match current behavior:
- Providers actually supported
- How keys/tokens are stored (encrypted) and what the UI exposes
- Anthropic OAuth flow and security implications
- Patrol and command execution (“autonomous mode”) safety controls
- Correct API endpoints and auth requirements
Files involved:
- `docs/AI.md`
- `docs/API.md`
- `internal/config/ai.go` (source of truth for config fields)
- `internal/api/router.go` (source of truth for endpoints)
Status:
- `docs/AI.md` rewritten to match multi-provider + encrypted config.
- `docs/API.md` AI endpoints updated to match router.
### 3) Installation “recommended path” is inconsistent across docs
- `README.md` recommends “Proxmox LXC (Recommended)” via GitHub `install.sh`.
- `docs/INSTALL.md` and `docs/FAQ.md` currently present Docker as the easiest/recommended path.
Action:
- Pick one recommendation hierarchy and make it consistent:
- If Proxmox LXC is the primary path, it should be the top section in `docs/INSTALL.md` and the FAQ answer should reflect it.
Files involved:
- `README.md`
- `docs/INSTALL.md`
- `docs/FAQ.md`
Status:
- Install docs now consistently present Proxmox VE LXC installer as the recommended path and include bootstrap-token retrieval.
### 4) Kubernetes/Helm docs and chart docs are out of date for v5
- `docs/KUBERNETES.md` references a chart repo URL and “Docker Agent sidecar”.
- `deploy/helm/pulse/README.md` describes “optional Docker monitoring agent” and defaults to `ghcr.io/rcourtman/pulse-docker-agent`.
Action:
- Update Helm docs to match the v5 agent direction:
- If `pulse-docker-agent` is deprecated, the chart should not reference it as primary.
- Align chart distribution instructions (Helm repo vs OCI).
Files involved:
- `docs/KUBERNETES.md`
- `deploy/helm/pulse/README.md`
- `deploy/helm/pulse/values.yaml`
- `deploy/helm/pulse/templates/*`
Status:
- `docs/KUBERNETES.md` updated to prefer OCI chart installs and flag the legacy agent block.
- `deploy/helm/pulse/README.md` and `deploy/helm/pulse/values.yaml` now label the agent workload as legacy.
## Redundant / Duplicated Docs (needs consolidation)
### Auto-update docs: two competing sources
- `docs/AUTO_UPDATE.md` describes “Settings → System Updates” and includes docker image instructions that differ from other docs.
- `docs/operations/AUTO_UPDATE.md` documents systemd timers and edits `/var/lib/pulse/system.json` which appears stale for current config defaults (`/etc/pulse/system.json`).
Action:
- Choose one canonical page (likely `docs/AUTO_UPDATE.md`) and:
- Move operational/timer details into it (or link to a clearly “advanced ops” page)
- Fix stale paths and service names
- Remove or clearly label the non-canonical duplicate
Files involved:
- `docs/AUTO_UPDATE.md`
- `docs/operations/AUTO_UPDATE.md`
Status:
- Both documents updated to current UI naming and paths; optional future work is to consolidate into a single canonical page.
### Temperature monitoring docs: two sources with different “truth”
- `docs/TEMPERATURE_MONITORING.md` (sensor proxy focused, extensive)
- `docs/security/TEMPERATURE_MONITORING.md` (agent recommended, proxy “legacy”)
Action:
- Collapse into one canonical document with a clear decision tree, then:
- Keep the other as a short redirect page, or delete it.
Files involved:
- `docs/TEMPERATURE_MONITORING.md`
- `docs/security/TEMPERATURE_MONITORING.md`
Status:
- `docs/TEMPERATURE_MONITORING.md` is now the canonical deep-dive, and `docs/security/TEMPERATURE_MONITORING.md` is a security/overview page.
### Adaptive polling docs disagree with defaults and file paths
- `docs/monitoring/ADAPTIVE_POLLING.md` claims adaptive polling is enabled by default and says env default is `true`.
- Code defaults `AdaptivePollingEnabled=false` and `docs/operations/ADAPTIVE_POLLING_ROLLOUT.md` references `/var/lib/pulse/system.json`.
Action:
- Make one canonical doc, fix defaults and paths, and ensure UI path matches current navigation.
Files involved:
- `docs/monitoring/ADAPTIVE_POLLING.md`
- `docs/operations/ADAPTIVE_POLLING_ROLLOUT.md`
Status:
- Defaults and paths updated to match current behavior.
## Stale / Incorrect Content (targeted findings)
### `docs/API.md`
Issues:
- AI endpoints mismatch current router paths (examples: `POST /api/ai/chat` vs current `/api/ai/execute`; settings update path differs).
- “complete REST API documentation” claim is optimistic. Its a curated subset plus a “check router.go” note.
Action:
- Update AI section to match `internal/api/router.go`.
- Consider splitting into:
- “Stable/public API” (guaranteed)
- “Internal/subject to change” (documented but not stable)
### `docs/METRICS_HISTORY.md`
Issues:
- Documents `PULSE_METRICS_*_RETENTION_DAYS` env vars that do not appear to exist in the server config.
- Claims metrics are stored under `/etc/pulse/data/metrics/`, but the metrics store is SQLite (`metrics.db`) under the configured data directory.
Action:
- Rewrite this doc to match the tiered retention model and actual storage format/location.
### `docs/FAQ.md`
Issues:
- Recommends Docker as easiest install, conflicts with repo README.
- Password reset guidance does not mention the bootstrap token requirement that can appear after removing `.env`.
- Mentions `METRICS_RETENTION_DAYS` which does not appear to be a current server config knob (v5 uses tiered retention settings).
Action:
- Align install recommendation with v5 positioning.
- Update auth reset steps to include bootstrap token retrieval where applicable.
- Replace metrics retention knob guidance with current retention model and UI location.
### `docs/TROUBLESHOOTING.md` and `docs/DOCKER.md`
Issues:
- “Forgot password” flow implies you can just rerun the setup wizard after deleting `.env`, but first-time setup can require the bootstrap token.
Action:
- Update password reset steps and link to the bootstrap token section in `docs/INSTALL.md`.
### `docs/RELEASE_NOTES.md`
Issues:
- Entire document is v4.x release notes.
Action:
- Replace with v5 release notes (or move to `docs/releases/` and add v5.0.0 as the top section).
- For the v5 stable cut, include: breaking changes, migration notes, and versioned “what changed since v4”.
### `cmd/pulse-sensor-proxy/README.md`
Issues:
- Mentions downloading via `/download/pulse-sensor-proxy` but the server router does not expose this endpoint.
- “Deprecated” banner conflicts with current server behavior and security guidance.
Action:
- Either bring it in line with the chosen v5 temperature story, or clearly scope it as legacy.
### Broken local link
- `docs/TEMPERATURE_MONITORING.md` contains an absolute link to `/opt/pulse/cmd/pulse-sensor-proxy/README.md` which does not work in GitHub.
Action:
- Replace with a repo-relative link (or link to the canonical temperature doc).
### Widespread “runtime path” drift (`/opt/pulse/...`)
Several user-facing docs mix:
- repository paths (`/opt/pulse/...`) used in this dev workspace, and
- runtime paths used in real installs (`/etc/pulse`, `/data`, `/var/log/pulse`, systemd units).
This creates confusion and broken copy-paste commands.
Examples to review:
- `docs/ZFS_MONITORING.md` references `/opt/pulse/.env` and `/opt/pulse/pulse.log`.
- `docs/operations/*` references `/var/lib/pulse/system.json` rather than `/etc/pulse/system.json`.
Action:
- Adopt a consistent convention across docs:
- **Runtime**: `/etc/pulse` (systemd/LXC), `/data` (Docker/Helm)
- **Repo/dev**: `/opt/pulse` only in development docs
- **Logs**: `journalctl -u pulse` (systemd) and `docker logs` (Docker), plus `/var/log/pulse/*` only if actually used in production images.
## Missing Docs for a v5 Stable Release (recommended additions)
### v5 upgrade guide (v4 → v5)
Add a single canonical page covering:
- “What changes in v5” in operator terms
- Any breaking changes and required actions
- Post-upgrade verification checklist (health endpoint, scheduler health, agents connected, temps, notifications)
- Rollback guidance for each deployment model (Docker, systemd/LXC, Helm)
Suggested path:
- `docs/UPGRADE_v5.md` (or `docs/MIGRATION_v5.md`)
### “Deployment model matrix”
Many docs implicitly assume a deployment type. Add a short matrix page that answers:
- What works on Docker vs Proxmox LXC vs systemd vs Helm
- How updates work per model
- Where config lives per model
- What “recommended” means (and why)
Suggested path:
- `docs/DEPLOYMENT_MODELS.md`
### AI safety and permissions
If v5 ships AI “execute/run-command” features:
- Document default safety posture
- What autonomous mode does
- Required scopes/roles
- Audit logging expectations
- Clear warning section for production
Suggested path:
- Expand `docs/AI.md` with a “Safety” section, or add `docs/AI_SAFETY.md`.
## Quick “Status” Inventory (what to touch for v5)
This is a fast triage list to help plan the doc refresh. Treat anything marked “Review” as “verify against current behavior”.
- Rewrite: `docs/AI.md`
- Rewrite: `docs/METRICS_HISTORY.md`
- Rewrite: `docs/RELEASE_NOTES.md` (or replace with v5 release notes)
- Update + align: `docs/INSTALL.md`, `docs/FAQ.md`, `docs/TROUBLESHOOTING.md`
- Update: `docs/API.md` (especially AI endpoints)
- Decide canonical + consolidate:
- `docs/TEMPERATURE_MONITORING.md` vs `docs/security/TEMPERATURE_MONITORING.md`
- `docs/AUTO_UPDATE.md` vs `docs/operations/AUTO_UPDATE.md`
- `docs/monitoring/ADAPTIVE_POLLING.md` vs `docs/operations/ADAPTIVE_POLLING_ROLLOUT.md`
- Review (Helm): `docs/KUBERNETES.md`, `deploy/helm/pulse/README.md`
- Review (paths): `docs/ZFS_MONITORING.md` (and any other doc that uses `/opt/pulse/...` in user instructions)
## Suggested “Doc Refresh” Execution Order
1. Decide v5 canonical stories (agent vs proxy for temps, AI capabilities, Helm strategy).
2. Update the primary entrypoints:
- `README.md`
- `docs/README.md`
- `docs/INSTALL.md`
3. Fix contradictions and remove duplicates (temperature, auto-update, adaptive polling).
4. Update `docs/API.md` to reflect current endpoints (especially AI).
5. Add v5 upgrade guide and deployment matrix.
6. Sweep FAQ + troubleshooting for the new canonical flows.

View file

@ -3,7 +3,14 @@
## 🛠️ Installation & Setup ## 🛠️ Installation & Setup
### What's the easiest way to install? ### What's the easiest way to install?
Use Docker: If you run Proxmox VE, use the official LXC installer (recommended):
```bash
curl -fsSL https://github.com/rcourtman/Pulse/releases/latest/download/install.sh | bash
```
If you prefer Docker:
```bash ```bash
docker run -d --name pulse -p 7655:7655 -v pulse_data:/data rcourtman/pulse:latest docker run -d --name pulse -p 7655:7655 -v pulse_data:/data rcourtman/pulse:latest
``` ```
@ -11,8 +18,12 @@ docker run -d --name pulse -p 7655:7655 -v pulse_data:/data rcourtman/pulse:late
See [INSTALL.md](INSTALL.md) for all options (Docker Compose, Kubernetes, systemd). See [INSTALL.md](INSTALL.md) for all options (Docker Compose, Kubernetes, systemd).
### How do I add a node? ### How do I add a node?
**Auto-discovery (Recommended)**: Go to **Settings → Nodes**, find your node in the "Discovered" list, click "Setup Script", and run the provided command on your Proxmox host. Go to **Settings → Proxmox**.
**Manual**: Go to **Settings → Nodes → Add Node** and enter the credentials manually.
- **Recommended (Agent setup)**: choose **Setup mode: Agent** and run the generated install command on the Proxmox host.
- **Manual**: choose **Setup mode: Manual** and enter the credentials (password or API token) for the Proxmox API.
If you want Pulse to find servers automatically, enable discovery in **Settings → System → Network** and then return to **Settings → Proxmox** to review discovered servers.
### How do I change the port? ### How do I change the port?
- **Systemd**: `sudo systemctl edit pulse`, add `Environment="FRONTEND_PORT=8080"`, restart. - **Systemd**: `sudo systemctl edit pulse`, add `Environment="FRONTEND_PORT=8080"`, restart.
@ -36,9 +47,12 @@ Yes! If Pulse detects Ceph storage, it automatically queries cluster health, OSD
Yes. Go to **Alerts → Thresholds** and set any value to `-1` to disable it. You can do this globally or per-resource (VM/Node). Yes. Go to **Alerts → Thresholds** and set any value to `-1` to disable it. You can do this globally or per-resource (VM/Node).
### How do I monitor temperature? ### How do I monitor temperature?
Pulse uses a secure sensor proxy. Install the unified agent on your Proxmox hosts with Proxmox integration enabled:
1. Install `lm-sensors` on your host (`apt install lm-sensors && sensors-detect`).
2. Run the Pulse setup script on the node again to install the sensor proxy. 1. Install `lm-sensors` on the host (`apt install lm-sensors && sensors-detect`)
2. Install `pulse-agent` with `--enable-proxmox`
`pulse-sensor-proxy` is deprecated in v5 and is not recommended for new deployments.
See [Temperature Monitoring](TEMPERATURE_MONITORING.md). See [Temperature Monitoring](TEMPERATURE_MONITORING.md).
--- ---
@ -50,16 +64,22 @@ See [Temperature Monitoring](TEMPERATURE_MONITORING.md).
```bash ```bash
docker exec pulse rm /data/.env docker exec pulse rm /data/.env
docker restart pulse docker restart pulse
# Access UI to run setup wizard again # Access UI again. Pulse will require a bootstrap token for setup.
# Get it with:
docker exec pulse /app/pulse bootstrap-token
``` ```
**Systemd**: **Systemd**:
Delete `/etc/pulse/.env` and restart the service. Delete `/etc/pulse/.env` and restart the service. Pulse will require a bootstrap token for setup:
```bash
sudo pulse bootstrap-token
```
### How do I enable HTTPS? ### How do I enable HTTPS?
Set `HTTPS_ENABLED=true` and provide `TLS_CERT_FILE` and `TLS_KEY_FILE` environment variables. See [Configuration](CONFIGURATION.md#https--tls). Set `HTTPS_ENABLED=true` and provide `TLS_CERT_FILE` and `TLS_KEY_FILE` environment variables. See [Configuration](CONFIGURATION.md#https--tls).
### Can I use Single Sign-On (SSO)? ### Can I use Single Sign-On (SSO)?
Yes. Pulse supports OIDC (Settings → Security → OIDC) and Proxy Auth (Authentik, Authelia). See [Proxy Auth Guide](PROXY_AUTH.md). Yes. Pulse supports OIDC in **Settings → Security → Single Sign-On** and Proxy Auth (Authentik, Authelia). See [Proxy Auth Guide](PROXY_AUTH.md) and [OIDC](OIDC.md).
--- ---
@ -67,7 +87,7 @@ Yes. Pulse supports OIDC (Settings → Security → OIDC) and Proxy Auth (Authen
### No data showing? ### No data showing?
- Check Proxmox API is reachable (port 8006). - Check Proxmox API is reachable (port 8006).
- Verify credentials in **Settings → Nodes**. - Verify credentials in **Settings → Proxmox**.
- Check logs: `journalctl -u pulse -f` or `docker logs -f pulse`. - Check logs: `journalctl -u pulse -f` or `docker logs -f pulse`.
### Connection refused? ### Connection refused?
@ -78,4 +98,4 @@ Yes. Pulse supports OIDC (Settings → Security → OIDC) and Proxy Auth (Authen
Set `ALLOWED_ORIGINS=https://your-domain.com` environment variable if accessing Pulse from a different domain. Set `ALLOWED_ORIGINS=https://your-domain.com` environment variable if accessing Pulse from a different domain.
### High memory usage? ### High memory usage?
Reduce `METRICS_RETENTION_DAYS` (default 7) via environment variable if running on very constrained hardware. If you are storing long history windows, reduce metrics retention (see [METRICS_HISTORY.md](METRICS_HISTORY.md)). Also confirm your polling intervals match your environment size.

View file

@ -4,6 +4,15 @@ Pulse offers flexible installation options from Docker to enterprise-ready Kuber
## 🚀 Quick Start (Recommended) ## 🚀 Quick Start (Recommended)
### Proxmox VE (LXC installer)
If you run Proxmox VE, the easiest and most “Pulse-native” deployment is the official installer which creates and configures a lightweight LXC container.
Run this on your Proxmox host:
```bash
curl -fsSL https://github.com/rcourtman/Pulse/releases/latest/download/install.sh | bash
```
### Docker ### Docker
Ideal for containerized environments or testing. Ideal for containerized environments or testing.
@ -46,22 +55,27 @@ volumes:
Deploy to your cluster using our Helm chart. Deploy to your cluster using our Helm chart.
```bash ```bash
helm repo add pulse https://rcourtman.github.io/Pulse/ helm upgrade --install pulse oci://ghcr.io/rcourtman/pulse-chart \
helm repo update
helm install pulse pulse/pulse \
--namespace pulse \ --namespace pulse \
--create-namespace --create-namespace
``` ```
See [KUBERNETES.md](KUBERNETES.md) for ingress and persistence configuration. See [KUBERNETES.md](KUBERNETES.md) for ingress and persistence configuration.
### 2. Bare Metal / Systemd ### 2. Bare Metal / Systemd
For bare-metal Linux servers, download the release binary directly. For Linux servers (VM or bare metal), use the official installer:
```bash ```bash
# Download and extract curl -fsSL https://github.com/rcourtman/Pulse/releases/latest/download/install.sh | sudo bash
curl -fsSL https://github.com/rcourtman/Pulse/releases/latest/download/pulse-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m).tar.gz | tar xz ```
sudo mv pulse /usr/local/bin/
sudo chmod +x /usr/local/bin/pulse <details>
<summary><strong>Manual systemd install (advanced)</strong></summary>
```bash
# Download the correct tarball from GitHub Releases and extract it
# https://github.com/rcourtman/Pulse/releases
sudo install -m 0755 pulse /usr/local/bin/pulse
# Create systemd service # Create systemd service
sudo tee /etc/systemd/system/pulse.service > /dev/null << 'EOF' sudo tee /etc/systemd/system/pulse.service > /dev/null << 'EOF'
@ -85,6 +99,7 @@ sudo mkdir -p /etc/pulse
sudo systemctl daemon-reload sudo systemctl daemon-reload
sudo systemctl enable --now pulse sudo systemctl enable --now pulse
``` ```
</details>
--- ---
@ -96,16 +111,16 @@ Pulse is secure by default. On first launch, you must retrieve a **Bootstrap Tok
| Platform | Command | | Platform | Command |
|----------|---------| |----------|---------|
| **Docker** | `docker exec pulse cat /data/.bootstrap_token` | | **Docker** | `docker exec pulse cat /data/.bootstrap_token` or `docker exec pulse /app/pulse bootstrap-token` |
| **Kubernetes** | `kubectl exec -it <pod> -- cat /data/.bootstrap_token` | | **Kubernetes** | `kubectl exec -it <pod> -- cat /data/.bootstrap_token` or `kubectl exec -it <pod> -- /app/pulse bootstrap-token` |
| **Systemd** | `sudo cat /etc/pulse/.bootstrap_token` | | **Systemd** | `sudo cat /etc/pulse/.bootstrap_token` or `sudo pulse bootstrap-token` |
### Step 2: Create Admin Account ### Step 2: Create Admin Account
1. Open `http://<your-ip>:7655` 1. Open `http://<your-ip>:7655`
2. Paste the **Bootstrap Token**. 2. Paste the **Bootstrap Token**.
3. Create your **Admin Username** and **Password**. 3. Create your **Admin Username** and **Password**.
> **Note**: If you configure `PULSE_AUTH_USER` and `PULSE_AUTH_PASS` via environment variables, this step is skipped. > **Note**: If you configure authentication via environment variables (`PULSE_AUTH_USER`/`PULSE_AUTH_PASS` and/or `API_TOKENS`), the bootstrap token is automatically removed and this step is skipped.
--- ---
@ -114,7 +129,7 @@ Pulse is secure by default. On first launch, you must retrieve a **Bootstrap Tok
### Automatic Updates (Systemd only) ### Automatic Updates (Systemd only)
Pulse can self-update to the latest stable version. Pulse can self-update to the latest stable version.
**Enable via UI**: Settings → System → Automatic Updates **Enable via UI**: Settings → System → Updates
### Manual Update ### Manual Update
| Platform | Command | | Platform | Command |

View file

@ -4,25 +4,21 @@ Deploy Pulse to Kubernetes using the official Helm chart.
## 🚀 Installation ## 🚀 Installation
1. **Add Repo** 1. **Install (OCI chart, recommended)**
```bash ```bash
helm repo add pulse https://rcourtman.github.io/Pulse/ helm upgrade --install pulse oci://ghcr.io/rcourtman/pulse-chart \
helm repo update
```
2. **Install**
```bash
helm install pulse pulse/pulse \
--namespace pulse \ --namespace pulse \
--create-namespace --create-namespace
``` ```
3. **Access** 2. **Access**
```bash ```bash
kubectl -n pulse port-forward svc/pulse 7655:7655 kubectl -n pulse port-forward svc/pulse 7655:7655
``` ```
Open `http://localhost:7655` to complete setup. Open `http://localhost:7655` to complete setup.
> If you installed using a Helm repository URL previously, you can keep using it. OCI is the preferred distribution format going forward.
--- ---
## ⚙️ Configuration ## ⚙️ Configuration
@ -35,7 +31,9 @@ Configure via `values.yaml` or `--set` flags.
| `ingress.enabled` | Enable Ingress | `false` | | `ingress.enabled` | Enable Ingress | `false` |
| `persistence.enabled` | Enable PVC for /data | `true` | | `persistence.enabled` | Enable PVC for /data | `true` |
| `persistence.size` | PVC Size | `8Gi` | | `persistence.size` | PVC Size | `8Gi` |
| `agent.enabled` | Enable Docker Agent sidecar | `false` | | `agent.enabled` | Enable legacy docker agent workload | `false` |
> Note: the `agent.*` block is legacy and currently references `pulse-docker-agent`. For new deployments, prefer the unified agent (`pulse-agent`) where possible.
### Example `values.yaml` ### Example `values.yaml`
@ -59,7 +57,7 @@ server:
API_TOKENS: "my-token" API_TOKENS: "my-token"
agent: agent:
enabled: true enabled: false
secretEnv: secretEnv:
create: true create: true
data: data:
@ -76,8 +74,7 @@ helm upgrade --install pulse pulse/pulse -n pulse -f values.yaml
## 🔄 Upgrades ## 🔄 Upgrades
```bash ```bash
helm repo update helm upgrade pulse oci://ghcr.io/rcourtman/pulse-chart -n pulse
helm upgrade pulse pulse/pulse -n pulse
``` ```
**Rollback**: **Rollback**:

View file

@ -19,32 +19,26 @@ Pulse 5.0 adds support for monitoring Proxmox Mail Gateway instances alongside y
4. Enter connection details: 4. Enter connection details:
- Host: Your PMG IP or hostname - Host: Your PMG IP or hostname
- Port: 8006 (default) - Port: 8006 (default)
- API Token ID: e.g., `root@pam!pulse` - API Token ID: e.g., `root@pmg!pulse` (format: `<user>@<realm>!<token-id>`)
- API Token Secret: Your token secret - API Token Secret: Your token secret (shown once when you create the token)
### Via Discovery ### Via Discovery
Pulse can automatically discover PMG instances on your network: Pulse can automatically discover PMG instances on your network:
1. Go to **Settings → Discovery** 1. Enable discovery in **Settings → System → Network**
2. Enable network discovery 2. Go to **Settings → Proxmox**
3. PMG instances on port 8006 will be detected 3. PMG instances on port 8006 are detected and shown in the Proxmox discovery panels
4. Click to add discovered instances 4. Click a discovered PMG server to add it
## API Token Setup on PMG ## API Token Setup on PMG
Create an API token on your PMG server: Create an API token on your PMG server (recommended). The easiest method is via the PMG web UI:
```bash - Create a token for a user (for example `root@pmg`)
# SSH to your PMG server - Copy the token secret when it is displayed (it is typically shown once)
pveum user token add root@pam pulse --privsep 0
# Note the token secret - it's only shown once! If you see 403/permission errors, start by testing with a token for an admin user to confirm connectivity, then tighten permissions once you know which PMG endpoints your instance requires.
```
Required permissions:
- `Sys.Audit` - Read system status
- `Datastore.Audit` - Read mail statistics
## Dashboard ## Dashboard

View file

@ -1,87 +1,74 @@
# Metrics History # Metrics History (Persistent)
Pulse 5.0 introduces persistent metrics history, allowing you to view historical resource usage data and trends over time. Pulse persists metrics history to disk so trend views and sparklines survive restarts.
## Features ## Storage Location
- **Persistent Storage**: Metrics are saved to disk and survive restarts Metrics history is stored in a SQLite database named `metrics.db` under the Pulse data directory:
- **Configurable Retention**: Set how long to keep different metric types
- **Trend Analysis**: View resource usage patterns over time
- **Spark Lines**: See at-a-glance trends in the dashboard
## Configuration - **systemd/LXC installs**: typically `/etc/pulse/metrics.db`
- **Docker/Kubernetes installs**: typically `/data/metrics.db`
### Retention Settings ## Retention Model (Tiered)
Configure retention periods in **Settings → General → Metrics History**: Pulse keeps multiple resolutions of the same data, which allows longer history without storing raw samples forever:
| Metric Type | Default | Description | - **Raw** (high-resolution, short window)
|-------------|---------|-------------| - **Minute aggregates**
| **Host Metrics** | 7 days | CPU, memory, disk for hypervisors | - **Hourly aggregates**
| **Guest Metrics** | 7 days | VM and container metrics | - **Daily aggregates**
| **Container Metrics** | 3 days | Docker/Podman container stats |
| **Aggregate Metrics** | 30 days | Cluster-wide summaries |
### Environment Variables Default retention values (subject to change) are:
```bash - Raw: 2 hours
# Override via environment - Minute: 24 hours
PULSE_METRICS_HOST_RETENTION_DAYS=14 - Hourly: 7 days
PULSE_METRICS_GUEST_RETENTION_DAYS=14 - Daily: 90 days
PULSE_METRICS_CONTAINER_RETENTION_DAYS=7
PULSE_METRICS_AGGREGATE_RETENTION_DAYS=60 ## Advanced: Retention Tuning
Tiered retention is stored in `system.json` in the Pulse data directory:
- **systemd/LXC installs**: typically `/etc/pulse/system.json`
- **Docker/Kubernetes installs**: typically `/data/system.json`
Keys:
```json
{
"metricsRetentionRawHours": 2,
"metricsRetentionMinuteHours": 24,
"metricsRetentionHourlyDays": 7,
"metricsRetentionDailyDays": 90
}
``` ```
## Storage After changing these values, restart Pulse.
Metrics are stored in `/etc/pulse/data/metrics/` (or your configured data directory).
### Disk Usage
Approximate storage requirements:
- ~1 KB per resource per hour
- 10 hosts × 50 guests × 7 days ≈ 8 MB
### Database Maintenance
Pulse automatically:
- Compacts old data
- Prunes metrics beyond retention period
- Optimizes storage during low-usage periods
## API Access ## API Access
Query historical metrics via the API: Pulse exposes the persistent metrics store via:
- `GET /api/metrics-store/stats`
- `GET /api/metrics-store/history`
### History Query Parameters
`GET /api/metrics-store/history` supports:
- `resourceType` (required): `node`, `guest`, `storage`, `docker`, `dockerHost`
- `resourceId` (required): resource identifier
- `metric` (optional): `cpu`, `memory`, `disk`, etc. Omit to return all metrics for the resource.
- `range` (optional): `1h`, `6h`, `12h`, `24h`, `7d`, `30d`, `90d` (default `24h`)
Example:
```bash ```bash
# Get metrics for a specific resource
curl -H "X-API-Token: $TOKEN" \ curl -H "X-API-Token: $TOKEN" \
"http://localhost:7655/api/metrics/history?resource=vm-100&hours=24" "http://localhost:7655/api/metrics-store/history?resourceType=guest&resourceId=vm-100&range=7d&metric=cpu"
# Get aggregated cluster metrics
curl -H "X-API-Token: $TOKEN" \
"http://localhost:7655/api/metrics/history?type=aggregate&days=7"
``` ```
## Visualization
### Dashboard Sparklines
The dashboard shows 24-hour trend sparklines for each resource, updating in real-time.
### Detailed Charts
Click on any resource to see detailed historical charts with:
- Selectable time ranges (1h, 6h, 24h, 7d, 30d)
- Multiple metric overlays (CPU, memory, disk, network)
- Zoom and pan controls
## Troubleshooting ## Troubleshooting
### Metrics not persisting - **No sparklines / empty history**: confirm the instance can write to the data directory and that `metrics.db` exists.
1. Check data directory permissions - **Large disk usage**: reduce polling frequency first. If you need tighter retention, adjust the tiered retention settings in `system.json` (advanced) and restart Pulse.
2. Verify disk space availability
3. Check logs: `journalctl -u pulse | grep metrics`
### High disk usage
1. Reduce retention periods in Settings
2. Exclude low-value resources from history
3. Run manual cleanup: Settings → General → Clear Old Metrics

View file

@ -1,34 +1,36 @@
# 🚚 Migrating Pulse # 🚚 Migrating Pulse
**Updated for Pulse v4.24.0+** This guide covers migrating Pulse to a new host using the built-in encrypted export/import workflow.
## 🚀 Quick Migration Guide ## 🚀 Quick Migration Guide
### ❌ DON'T: Copy Files ### ❌ DON'T: Copy Files
Never copy `/etc/pulse` or `/var/lib/pulse` manually. Encryption keys and credentials will break. Never copy `/etc/pulse` (or `/data` in Docker/Kubernetes) manually. Encryption keys and credentials can break.
### ✅ DO: Use Export/Import ### ✅ DO: Use Export/Import
#### 1. Export (Old Server) #### 1. Export (Old Server)
1. Go to **Settings → Configuration Management**. 1. Go to **Settings → System → Backups**.
2. Click **Export Configuration**. 2. Click **Create Backup**.
3. Enter a strong passphrase and save the `.enc` file. 3. Enter a strong passphrase and download the encrypted backup.
#### 2. Import (New Server) #### 2. Import (New Server)
1. Install a fresh Pulse instance. 1. Install a fresh Pulse instance.
2. Go to **Settings → Configuration Management**. 2. Go to **Settings → System → Backups**.
3. Click **Import Configuration** and upload your file. 3. Click **Restore Configuration** and upload your file.
4. Enter the passphrase. 4. Enter the passphrase.
## 📦 What Gets Migrated ## 📦 What Gets Migrated
| Included ✅ | Not Included ❌ | | Included ✅ | Not Included ❌ |
| :--- | :--- | | :--- | :--- |
| Nodes & Credentials | Historical Metrics | | Nodes & credentials | Historical metrics history (`metrics.db`) |
| Alert Settings | Alert History | | Alerts & overrides | Browser sessions and local cookies |
| Email & Webhooks | Auth Settings (Passwords/Tokens) | | Notifications (email, webhooks, Apprise) | Local login username/password (`.env`) |
| System Settings | Update Rollback History | | System settings (`system.json`) | Update history/backup folders |
| Guest Metadata | | | API token records | |
| OIDC config | |
| Guest metadata/notes | |
## 🔄 Common Scenarios ## 🔄 Common Scenarios
@ -45,12 +47,12 @@ The export file works across all installation methods. You can migrate from Dock
## 📋 Post-Migration Checklist ## 📋 Post-Migration Checklist
Because authentication secrets are excluded from exports, you must: Because local login credentials are stored in `.env` (not part of exports), you must:
1. **Re-create Admin User**: If not using `.env` overrides, create your admin account on the new instance. 1. **Re-create Admin User**: If not using `.env` overrides, create your admin account on the new instance.
2. **Re-issue API Tokens**: 2. **Confirm API access**:
* Go to **Settings → Security**. * If you created API tokens in the UI, those token records are included in the export and should continue working.
* Generate new tokens for your agents and scripts. * If you used `.env`-based `API_TOKENS`/`API_TOKEN`, reconfigure them on the new host.
3. **Update Agents**: 3. **Update Agents**:
* **Unified Agent**: Update the `--token` flag in your service definition. * **Unified Agent**: Update the `--token` flag in your service definition.
* **Docker**: Update `PULSE_TOKEN` in your container config. * **Docker**: Update `PULSE_TOKEN` in your container config.
@ -58,7 +60,7 @@ Because authentication secrets are excluded from exports, you must:
## 🔒 Security ## 🔒 Security
* **Encryption**: Exports are encrypted with PBKDF2 (100k iterations). * **Encryption**: Exports are encrypted with passphrase-based encryption (PBKDF2 + AES-GCM).
* **Storage**: Safe to store in cloud backups or password managers. * **Storage**: Safe to store in cloud backups or password managers.
* **Passphrase**: Use a strong, unique passphrase (min 12 chars). * **Passphrase**: Use a strong, unique passphrase (min 12 chars).
@ -67,4 +69,4 @@ Because authentication secrets are excluded from exports, you must:
* **"Invalid passphrase"**: Ensure exact match (case-sensitive). * **"Invalid passphrase"**: Ensure exact match (case-sensitive).
* **Missing Nodes**: Verify export date. * **Missing Nodes**: Verify export date.
* **Connection Errors**: Update node IPs in Settings if they changed. * **Connection Errors**: Update node IPs in Settings if they changed.
* **Logging**: Re-configure log levels in **Settings → System → Logging** if needed. * **Logging**: Adjust `LOG_LEVEL`/`LOG_FORMAT` via environment variables if needed.

View file

@ -2,6 +2,8 @@
The Control Plane synchronizes `pulse-sensor-proxy` instances with the Pulse server, ensuring they trust the correct nodes without manual configuration. The Control Plane synchronizes `pulse-sensor-proxy` instances with the Pulse server, ensuring they trust the correct nodes without manual configuration.
> **Deprecated in v5:** `pulse-sensor-proxy` (and its control-plane sync) is deprecated and not recommended for new deployments. New installs should use `pulse-agent --enable-proxmox` for temperature monitoring.
## 🏗️ Architecture ## 🏗️ Architecture
```mermaid ```mermaid
@ -37,4 +39,3 @@ pulse_control_plane:
* **Tokens**: The `ctrl_token` is unique per proxy instance. * **Tokens**: The `ctrl_token` is unique per proxy instance.
* **Least Privilege**: The proxy only knows about nodes explicitly added to Pulse. * **Least Privilege**: The proxy only knows about nodes explicitly added to Pulse.
* **Fallback**: If the control plane is unreachable, the proxy uses its last known good configuration. * **Fallback**: If the control plane is unreachable, the proxy uses its last known good configuration.

View file

@ -0,0 +1,401 @@
# Pulse Pro Implementation Plan
**Goal**: Gate AI features behind a Pro license to create a sustainable income stream.
**Timeline**: ~1-2 weeks of focused work
---
## Phase 1: License System Architecture
### 1.1 License Format (Simple JWT)
```go
// internal/license/license.go
type LicenseData struct {
LicenseID string `json:"lid"` // Unique license ID
Email string `json:"email"` // Customer email
Tier string `json:"tier"` // "pro", "msp", "enterprise"
IssuedAt time.Time `json:"iat"`
ExpiresAt time.Time `json:"exp"` // Empty = lifetime
MaxNodes int `json:"max_nodes"` // 0 = unlimited
Features []string `json:"features"` // ["ai_chat", "ai_patrol", "ai_alerts"]
}
```
**Why JWT?**
- Self-contained (no license server needed for validation)
- Signed with your private key, verified with public key embedded in binary
- Can be verified offline (important for air-gapped homelabs)
- Standard format, easy to generate from any payment processor webhook
### 1.2 License Validation Flow
```
┌────────────────────────────────────────────────────────────────┐
│ User purchases license on LemonSqueezy/Gumroad │
│ ↓ │
│ Webhook hits your simple license API (can be CloudFlare Worker)│
│ ↓ │
│ Generate JWT signed with private key │
│ ↓ │
│ Email license key to customer │
│ ↓ │
│ User pastes key in Pulse Settings → Pro tab │
│ ↓ │
│ Pulse validates signature with embedded public key │
│ ↓ │
│ Store encrypted license in config dir (license.enc) │
└────────────────────────────────────────────────────────────────┘
```
---
## Phase 2: Backend Implementation
### 2.1 New Files to Create
```
internal/license/
├── license.go # License struct, validation, JWT parsing
├── license_test.go # Tests
└── features.go # Feature flags (what Pro includes)
internal/config/
└── license.go # License persistence (store/load from disk)
```
### 2.2 License Service
```go
// internal/license/license.go
package license
import (
"crypto/ed25519"
"encoding/base64"
"errors"
"time"
"github.com/golang-jwt/jwt/v5"
)
// Embedded public key (compiled into binary)
// Generate keypair: go run ./cmd/license-keygen
var publicKeyBase64 = "YOUR_PUBLIC_KEY_HERE"
type Service struct {
license *LicenseData
loaded bool
}
func NewService() *Service {
return &Service{}
}
func (s *Service) LoadFromKey(licenseKey string) error {
// Parse and validate JWT
// Store in s.license
}
func (s *Service) IsValid() bool {
if s.license == nil {
return false
}
if !s.license.ExpiresAt.IsZero() && time.Now().After(s.license.ExpiresAt) {
return false
}
return true
}
func (s *Service) HasFeature(feature string) bool {
if !s.IsValid() {
return false
}
for _, f := range s.license.Features {
if f == feature || f == "all" {
return true
}
}
return false
}
// Feature constants
const (
FeatureAIChat = "ai_chat"
FeatureAIPatrol = "ai_patrol"
FeatureAIAlerts = "ai_alerts"
FeatureOIDC = "oidc" // SSO/OIDC authentication
FeatureKubernetes = "kubernetes" // K8s cluster monitoring
FeatureMultiUser = "multi_user" // Multiple user accounts
FeatureAPIAccess = "api_access" // Full API access for integrations
FeatureWhiteLabel = "white_label" // Custom branding (MSP tier)
FeatureAll = "all"
)
```
### 2.3 Integration Points
Modify these files to check license:
| File | What to Gate |
|------|--------------|
| `internal/api/ai_handlers.go` | Chat endpoints, patrol endpoints |
| `internal/ai/patrol.go` | Patrol service start |
| `internal/ai/service.go` | AI chat service |
| `internal/ai/alert_triggered.go` | Alert analysis |
| `internal/api/oidc_handlers.go` | OIDC/SSO configuration |
| `internal/api/kubernetes_handlers.go` | K8s cluster endpoints |
| `internal/monitoring/kubernetes/` | K8s monitoring service |
**Example gating in ai_handlers.go:**
```go
func (h *AISettingsHandler) HandleChat(w http.ResponseWriter, r *http.Request) {
// Check Pro license
if !h.licenseService.HasFeature(license.FeatureAIChat) {
utils.WriteJSONError(w, http.StatusPaymentRequired,
"AI Chat requires Pulse Pro. Visit https://pulserelay.pro to upgrade.")
return
}
// ... existing logic
}
```
---
## Phase 3: Frontend Implementation
### 3.1 New Settings Tab: "Pro License"
Location: `frontend-modern/src/routes/settings/+page.svelte` (or equivalent)
```
┌──────────────────────────────────────────────────────────────┐
│ ⚡ Pulse Pro │
├──────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ License Key │ │
│ │ [________________________________________________] │ │
│ │ [Activate License] │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
│ ✅ License Status: Active (Pro) │
│ 📧 Licensed to: user@example.com │
│ 📅 Expires: Never (Lifetime) │
│ │
│ ───────────────────────────────────────────────────────── │
│ │
│ Included Features: │
│ ✅ AI Chat Assistant │
│ ✅ AI Patrol (Background Health Checks) │
│ ✅ AI Alert Analysis │
│ ✅ Priority Support │
│ │
│ ───────────────────────────────────────────────────────── │
│ │
│ Don't have a license? │
│ [Get Pulse Pro →] https://pulserelay.pro │
│ │
└──────────────────────────────────────────────────────────────┘
```
### 3.2 Graceful Degradation for Unlicensed Users
When AI features are accessed without a license:
- **AI Settings Tab**: Show features but disabled with "Upgrade to Pro" message
- **Chat Button**: Show but with "Pro" badge, clicking prompts upgrade
- **Patrol Findings**: Hide or show "Enable with Pro" placeholder
**Don't be hostile.** The free version should still feel complete. Pro is an enhancement, not a hostage situation.
---
## Phase 4: Payment & License Generation
### 4.1 Payment Processor: LemonSqueezy
**Why LemonSqueezy over alternatives?**
- Handles global VAT/sales tax automatically
- Generates invoices (enterprises need this)
- Good webhook support for automation
- Reasonable fees (~5% + 50¢)
- Supports both subscription and one-time payments
### 4.2 Pricing Structure (Suggested)
| Tier | Price | Features | Target |
|------|-------|----------|--------|
| **Pro Monthly** | $12/month | AI features, OIDC/SSO, K8s monitoring | Individuals |
| **Pro Annual** | $99/year | Same as monthly, 2 months free | Power users |
| **Pro Lifetime** | $249 one-time | All Pro features, forever | Homelabbers who hate subscriptions |
| **MSP** | $49/month | All Pro + unlimited instances, white-label, multi-tenant | MSPs |
| **Enterprise** | Custom | All features + support SLA, on-prem license server | Large orgs |
### 4.3 Feature Matrix
| Feature | Free | Pro | MSP | Enterprise |
|---------|------|-----|-----|------------|
| Proxmox VE/PBS/PMG monitoring | ✅ | ✅ | ✅ | ✅ |
| Docker/Podman monitoring | ✅ | ✅ | ✅ | ✅ |
| Alerts (Discord, Slack, etc.) | ✅ | ✅ | ✅ | ✅ |
| Metrics history | ✅ | ✅ | ✅ | ✅ |
| Backup explorer | ✅ | ✅ | ✅ | ✅ |
| **AI Chat** | ❌ | ✅ | ✅ | ✅ |
| **AI Patrol** | ❌ | ✅ | ✅ | ✅ |
| **AI Alert Analysis** | ❌ | ✅ | ✅ | ✅ |
| **OIDC/SSO** | ❌ | ✅ | ✅ | ✅ |
| **Kubernetes monitoring** | ❌ | ✅ | ✅ | ✅ |
| Unlimited instances | ❌ | ❌ | ✅ | ✅ |
| White-label branding | ❌ | ❌ | ✅ | ✅ |
| Multi-tenant mode | ❌ | ❌ | ✅ | ✅ |
| Priority support | ❌ | Email | Email | Dedicated |
| SLA | ❌ | ❌ | ❌ | ✅ |
### 4.3 License Generation Service
A simple Cloudflare Worker or Vercel Edge Function:
```javascript
// Simplified license generator (LemonSqueezy webhook handler)
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const webhook = await request.json()
if (webhook.meta.event_name === 'order_created') {
const license = generateLicense({
email: webhook.data.attributes.user_email,
tier: 'pro',
expiresAt: null, // lifetime for now
})
// Send license via email
await sendLicenseEmail(webhook.data.attributes.user_email, license)
}
return new Response('OK')
}
function generateLicense(data) {
// Sign JWT with private key
// Return base64-encoded license key
}
```
---
## Phase 5: Launch Communication
### 5.1 Changelog Entry
```markdown
## v5.0.0 - The AI Update
### 🚀 Major Changes
**Introducing Pulse Pro**
Pulse 5.0 includes powerful AI features that require a Pro license:
- **AI Chat**: Natural language interface to your infrastructure
- **AI Patrol**: Background health monitoring and insights
- **AI Alert Analysis**: Smart analysis when alerts fire
Core monitoring features remain **completely free and open source**.
Pro licenses support ongoing development and enable me to work on Pulse full-time.
[Get Pulse Pro →](https://pulserelay.pro)
---
*Pulse has grown from a weekend project to something used by thousands.
To keep improving it, I need to make it sustainable. Thank you for your support!*
— Richard
```
### 5.2 Preemptive FAQ
Add to README or docs:
**Q: Why are AI features paid?**
A: AI features require significant development effort and ongoing maintenance. Pro licenses let me work on Pulse sustainably while keeping core monitoring free.
**Q: Will monitoring features become paid?**
A: No. Proxmox/Docker/K8s monitoring, alerts, history, and all current free features will remain free forever.
**Q: What if I'm already using AI in the RC?**
A: Thank you for testing! RC users were beta testers helping shape these features. The final release requires a Pro license.
**Q: I can't afford Pro.**
A: Email me (richard@pulserelay.pro). I offer discounts for students, hobbyists in financial hardship, and open source contributors.
**Q: Can I self-host without Pro?**
A: Absolutely. Pulse works great without AI features. Pro is optional.
---
## Implementation Order
1. **Week 1: Backend**
- [ ] Create `internal/license/` package
- [ ] Implement JWT validation with embedded public key
- [ ] Add license persistence (encrypted storage)
- [ ] Gate AI endpoints with license checks
- [ ] Add `/api/license` endpoints (check, activate)
2. **Week 2: Frontend + Payment**
- [ ] Add Pro License settings tab
- [ ] Update AI settings to show Pro-gated state
- [ ] Set up LemonSqueezy product
- [ ] Create license generation webhook
- [ ] Set up pulserelay.pro landing page (can be simple)
- [ ] Write announcement blog post
3. **Launch**
- [ ] Release v5.0.0 stable
- [ ] Post to Reddit (/r/homelab, /r/Proxmox, /r/selfhosted)
- [ ] Post to GitHub Discussions
- [ ] Email mailing list (if you have one)
---
## Security Considerations
1. **Private key**: Never commit to repo. Store in password manager + secure backup.
2. **License validation**: Always verify signature, never trust claims without verification.
3. **Obfuscation**: Consider light obfuscation of license check code (not for security, but to discourage trivial patching).
4. **Grace period**: If validation fails, maybe grant 7-day grace period before disabling (better UX).
---
## What NOT to Do
- ❌ Phone-home license validation (breaks air-gapped installs)
- ❌ Aggressive license enforcement (pisses off users)
- ❌ Remove free features to "encourage" upgrades
- ❌ Make the free version feel crippled
- ❌ Hide that it's paid (be upfront in README)
---
## Success Metrics (First 90 Days)
| Metric | Target |
|--------|--------|
| Pro licenses sold | 50-100 |
| Monthly revenue | $500-$1000 |
| Churn rate | <5% |
| Negative community reactions | <10 vocal complaints |
| GitHub stars lost | <50 |
If you hit these numbers, you've validated the model. Then you can expand to MSP tier, add features, etc.
---
*This plan can be adjusted based on your preferences. Want me to help implement any specific part?*

View file

@ -10,8 +10,12 @@ Welcome to the Pulse documentation portal. Here you'll find everything you need
Step-by-step guides for Docker, Kubernetes, and bare metal. Step-by-step guides for Docker, Kubernetes, and bare metal.
- **[Configuration](CONFIGURATION.md)** - **[Configuration](CONFIGURATION.md)**
Learn how to configure authentication, notifications (Email, Discord, etc.), and system settings. Learn how to configure authentication, notifications (Email, Discord, etc.), and system settings.
- **[Deployment Models](DEPLOYMENT_MODELS.md)**
Where config lives, how updates work, and what differs per deployment.
- **[Migration Guide](MIGRATION.md)** - **[Migration Guide](MIGRATION.md)**
Moving to a new server? Here's how to export and import your data safely. Moving to a new server? Here's how to export and import your data safely.
- **[Upgrade to v5](UPGRADE_v5.md)**
Practical upgrade guidance and post-upgrade checks.
- **[FAQ](FAQ.md)** - **[FAQ](FAQ.md)**
Common questions and quick answers. Common questions and quick answers.
@ -28,11 +32,11 @@ Welcome to the Pulse documentation portal. Here you'll find everything you need
## ✨ New in 5.0 ## ✨ New in 5.0
- **[Pulse AI](AI.md)** Intelligent monitoring assistant with chat, patrol mode, and auto-fix. - **[Pulse AI](AI.md)** Optional assistant for chat, patrol findings, alert analysis, and execution workflows.
- **[Metrics History](METRICS_HISTORY.md)** Persistent metrics storage with configurable retention. - **[Metrics History](METRICS_HISTORY.md)** Persistent metrics storage with configurable retention.
- **[Mail Gateway](MAIL_GATEWAY.md)** Proxmox Mail Gateway (PMG) monitoring. - **[Mail Gateway](MAIL_GATEWAY.md)** Proxmox Mail Gateway (PMG) monitoring.
- **[Auto Updates](AUTO_UPDATE.md)** One-click updates for supported deployments. - **[Auto Updates](AUTO_UPDATE.md)** One-click updates for supported deployments.
- **[Kubernetes](KUBERNETES.md)** Complete K8s cluster monitoring via agents. - **[Kubernetes](KUBERNETES.md)** Helm deployment (ingress, persistence, HA patterns).
## 📡 Monitoring & Agents ## 📡 Monitoring & Agents

View file

@ -1,128 +1,8 @@
# Pulse v4.31.0 # Release Notes
## What's Changed Pulse release notes live on GitHub:
https://github.com/rcourtman/Pulse/releases
### Temperature monitoring over HTTPS For historical v4 notes that previously lived in this repo, see:
- `pulse-sensor-proxy` now exposes an authenticated HTTPS endpoint per Proxmox host. Pulse stores each proxys URL + bearer token and always polls `https://node:8443/temps` before falling back to local sockets or SSH, eliminating the fragile “single proxy for every node” chain. `docs/releases/RELEASE_NOTES_v4.md`
- Installations auto-register via the new `/api/temperature-proxy/register` endpoint, generate 4096-bit certificates, enforce CIDR allowlists, and log every HTTP request through the proxys audit pipeline.
- The backend temperature collector understands proxy URLs/tokens, respects strict timeouts, and publishes richer diagnostics so operators can see which node failed and why.
### Installer, diagnostics, and UI updates
- `scripts/install-sensor-proxy.sh` gained `--http-mode` / `--http-addr`, automatic TLS generation, rollback-on-failure, allowed subnet auto-population, and a comprehensive uninstall path that purges sockets, TLS secrets, and LXC bind mounts.
- A new `Settings → Diagnostics → Temperature Proxy` table surfaces proxy health, registration status, and the errors returned by the HTTPS endpoint.
- `scripts/tests/test-sensor-proxy-http.sh` exercises the HTTP installer path end-to-end inside Docker to prevent regressions.
### Host agent refinements
- Windows PowerShell installers/uninstallers now log verbosely, harden permissions, and clean up services more reliably.
- Linux host-agent scripts aligned with the new diagnostics UX and scoped token workflow so onboarding is less error-prone.
## Upgrade Notes
Temperature monitoring will not work for remote nodes until every Proxmox host is reinstalled with the new HTTPS workflow. Follow these steps per host:
```bash
# 1. Remove any pre-v4.31.0 proxy install
curl -fsSL https://github.com/rcourtman/Pulse/releases/latest/download/install-sensor-proxy.sh | \
sudo bash -s -- --uninstall --purge
# 2. Install the HTTP-enabled proxy and register it with Pulse
curl -fsSL https://github.com/rcourtman/Pulse/releases/latest/download/install-sensor-proxy.sh | \
sudo bash -s -- --standalone --http-mode --pulse-server https://your-pulse-host:7655
```
Only the Pulse server (or container host) needs network access to TCP/8443 on each node. After reinstalling, open **Settings → Diagnostics → Temperature Proxy** to confirm each node reports “HTTPS proxy healthy”. If not, grab the diagnostics entry or run:
```bash
curl -vk https://node.example:8443/health \
-H "Authorization: Bearer $(sudo cat /etc/pulse-sensor-proxy/.http-auth-token)"
```
## Installation
- **Install or upgrade with the helper script**
```bash
curl -sL https://github.com/rcourtman/Pulse/releases/latest/download/install.sh | bash
```
- **Binary upgrade on systemd hosts**
```bash
sudo systemctl stop pulse
curl -fsSL https://github.com/rcourtman/Pulse/releases/download/v4.31.0/pulse-v4.31.0-linux-amd64.tar.gz \
| sudo tar -xz -C /opt/pulse --strip-components=1
sudo systemctl start pulse
```
- **Docker update**
```bash
docker pull rcourtman/pulse:v4.31.0
docker stop pulse || true
docker rm pulse || true
docker run -d --name pulse --restart unless-stopped -p 7655:7655 rcourtman/pulse:v4.31.0
```
- **Helm upgrade**
```bash
helm upgrade --install pulse oci://ghcr.io/rcourtman/pulse-chart \
--version 4.31.0 \
--namespace pulse --create-namespace
```
## Downloads
- Multi-arch Linux tarballs (amd64/arm64/armv7)
- Standalone sensor proxy binaries (now include HTTP mode)
- Helm chart archive (pulse-4.31.0-helm.tgz)
- SHA256 checksums (checksums.txt)
- Docker tags: rcourtman/pulse:v4.31.0, :4.31, :4, :latest
---
# Pulse v4.26.1
## What's Changed
### New
- Standalone host agents now ship with guided Linux, macOS, and Windows installers that stream registration status back to Pulse, generate scoped commands from **Settings → Agents**, and feed host metrics into alerts alongside Proxmox and Docker.
- Alert thresholds gained host-level overrides, connectivity toggles, and snapshot size guardrails so you can tune offline behaviour per host while keeping a global policy for other resources.
- API tokens now support fine-grained scopes with a redesigned manager that previews command templates, highlights unused credentials, and makes revocation a single click.
- Proxmox replication jobs surface in a dedicated **Settings → Hosts → Replication** view with API plumbing to track task health and bubble failures into the monitoring pipeline.
- Docker Swarm environments now receive service/task-aware reporting with configurable scope, plus a Docker settings view that highlights manager/worker roles, stack health, rollout status, and service alert thresholds.
### Improvements
- Dashboard loads and drawer links respond faster thanks to cached guest metadata, reduced polling allocations, and inline URL editing that no longer flashes on WebSocket updates.
- Settings navigation is reorganized with dedicated Docker and Hosts sections, richer filters, and platform icons that make agent onboarding and discovery workflows clearer.
- LXC guests now report dynamic interface IPs, configuration metadata, and queue metrics so alerting, discovery, and drawers stay accurate even during rapid container churn.
- Notifications consolidate into a consistent toast system, with clearer feedback during agent setup, token generation, and background job state changes.
### Bug Fixes
- Enforced explicit node naming and respected custom Proxmox ports so cluster discovery, overrides, and disk monitoring defaults remain intact after edits.
- Hardened setup-token flows and checksum handling in the installers to prevent stale credentials and guarantee the correct binaries are fetched.
- Treated 501 responses from the Proxmox API as non-fatal during failover, restored FreeBSD disk counter parsing, and stopped guest link icons from re-triggering animations on updates.
- Preserved inline editor state across WebSocket refreshes and ensured Docker host identifiers stay collision-safe in mixed environments.
## Installation
- **Install or upgrade with the helper script**
```bash
curl -sL https://github.com/rcourtman/Pulse/releases/latest/download/install.sh | bash
```
- **Binary upgrade on systemd hosts**
```bash
sudo systemctl stop pulse
curl -fsSL https://github.com/rcourtman/Pulse/releases/download/v4.26.1/pulse-v4.26.1-linux-amd64.tar.gz \
| sudo tar -xz -C /opt/pulse --strip-components=1
sudo systemctl start pulse
```
- **Docker update**
```bash
docker pull rcourtman/pulse:v4.26.1
docker stop pulse || true
docker rm pulse || true
docker run -d --name pulse --restart unless-stopped -p 7655:7655 rcourtman/pulse:v4.26.1
```
- **Helm upgrade**
```bash
helm upgrade --install pulse oci://ghcr.io/rcourtman/pulse-chart \
--version 4.26.1 \
--namespace pulse --create-namespace
```
## Downloads
- Multi-arch Linux tarballs (amd64/arm64/armv7)
- Standalone sensor proxy binaries
- Helm chart archive (pulse-4.26.1-helm.tgz)
- SHA256 checksums (checksums.txt)
- Docker tags: rcourtman/pulse:v4.26.1, :4.26, :4, :latest

View file

@ -0,0 +1,375 @@
# Security Audit Report - Pulse Application
## Date: 2025-12-18
## Auditor: Claude (Gemini)
---
## Executive Summary
This document presents the findings from a comprehensive security audit of the Pulse monitoring application. The audit examined authentication, authorization, cryptography, input validation, SSRF prevention, command execution, and general security practices.
### Overall Assessment
**Security Posture: A- (Excellent with minor recommendations)**
The codebase demonstrates a mature security posture with:
- ✅ Strong cryptographic practices (bcrypt, SHA3-256, AES-256-GCM)
- ✅ Comprehensive SSRF protection for webhooks
- ✅ CSRF protection for session-based authentication
- ✅ Rate limiting and account lockout
- ✅ Command execution policy with blocklist/allowlist
- ✅ Proper input sanitization and validation
- ✅ Security headers implementation
- ✅ Audit logging
A prior security audit on 2025-11-07 addressed 9 critical to low severity issues in the sensor-proxy component, all of which were successfully remediated.
---
## Audit Scope
### Components Reviewed
1. **Authentication System** (`internal/api/auth.go`, `internal/auth/`)
2. **Session Management** (`internal/api/security.go`, session stores)
3. **Cryptography** (`internal/crypto/crypto.go`)
4. **API Token Management** (`internal/api/security_tokens.go`)
5. **OIDC Integration** (`internal/api/security_oidc.go`)
6. **Webhook/Notification Security** (`internal/notifications/`)
7. **Command Execution** (`internal/agentexec/policy.go`)
8. **Database Operations** (`internal/metrics/store.go`)
9. **Configuration & Secrets** (`internal/config/`)
---
## Strengths Identified
### 1. Authentication & Password Security ✅
- **bcrypt hashing** with cost factor 12 for passwords
- **SHA3-256** for API token hashing
- **Constant-time comparison** for token validation (prevents timing attacks)
- **12-character minimum** password length requirement
- **Automatic hashing** of plain-text passwords on startup
### 2. Session Security ✅
- **HttpOnly cookies** for session tokens
- **Secure flag** set based on HTTPS detection
- **SameSite policy** properly configured (Lax/None based on proxy detection)
- **24-hour session expiry** with sliding window extension
- **Session invalidation** on password change
### 3. Rate Limiting & Account Lockout ✅
- **10 attempts/minute** for auth endpoints
- **5 failed attempts** triggers 15-minute lockout
- **Per-username AND per-IP** tracking
- **Lockout bypass prevention** (both must be clear)
### 4. CSRF Protection ✅
- **CSRF tokens** generated per session
- **Separate CSRF cookie** (not HttpOnly, readable by JS)
- **Header/form validation** for state-changing requests
- **Safe methods** (GET, HEAD, OPTIONS) exempted
- **API token auth** correctly bypasses CSRF (not vulnerable)
### 5. SSRF Prevention ✅
- **Webhook URL validation** with DNS resolution check
- **Private IP blocking** (RFC1918, link-local, loopback)
- **Cloud metadata endpoint blocking** (169.254.169.254, etc.)
- **Configurable allowlist** for internal webhooks
- **DNS rebinding protection** via IP resolution verification
### 6. Encryption at Rest ✅
- **AES-256-GCM** for credential encryption
- **Unique nonce** generation per encryption operation
- **Key file protections** with existence validation before encryption
- **Orphaned data prevention** (refuses to encrypt if key deleted)
### 7. Security Headers ✅
- Content-Security-Policy
- X-Frame-Options (DENY by default)
- X-Content-Type-Options: nosniff
- X-XSS-Protection
- Referrer-Policy
- Permissions-Policy
### 8. Command Execution Policy ✅
- **Blocklist** for dangerous commands (rm -rf, mkfs, dd, etc.)
- **Auto-approve list** for read-only inspection commands
- **Require approval** for service control, package management
- **Sudo normalization** for consistent policy application
### 9. SQL Injection Prevention ✅
- **Parameterized queries** used throughout metrics store
- **Prepared statements** for batch operations
- No string concatenation in SQL queries
### 10. XSS Prevention ✅
- **DOMPurify** for markdown rendering
- **HTML entity encoding** in tooltips
- **Allowed tag/attribute lists** for sanitized content
- **LLM output sanitization** (AI chat)
---
## Findings & Recommendations
### HIGH SEVERITY: None Identified
### MEDIUM SEVERITY
#### M1. Admin Bypass Debug Mode 🟡
**Location:** `internal/api/auth.go:675-691`
**Finding:**
The `adminBypassEnabled()` function allows bypassing authentication when both `ALLOW_ADMIN_BYPASS=1` and `PULSE_DEV=true` are set. While properly gated for development only:
```go
if os.Getenv("ALLOW_ADMIN_BYPASS") != "1" {
return
}
if os.Getenv("PULSE_DEV") == "true" || strings.EqualFold(os.Getenv("NODE_ENV"), "development") {
log.Warn().Msg("Admin authentication bypass ENABLED (development mode)")
adminBypassState.enabled = true
}
```
**Risk:** Accidental production deployment with these env vars could expose full admin access.
**Recommendation:**
1. Add prominent warning log at startup if either var is set
2. Consider disallowing in Docker `PULSE_DOCKER=true` mode
3. Document this explicitly as a development-only feature
---
#### M2. Recovery Token Exposure Window 🟡
**Location:** Session and recovery token stores
**Finding:**
Recovery tokens for password reset appear to be stored in JSON files. While tokens are hashed:
- File permissions should be verified as 0600
- Token expiration should be enforced server-side (appears to be implemented)
**Recommendation:**
1. Verify file permissions are set correctly (0600) during token store initialization
2. Add cleanup routine for expired tokens
---
### LOW SEVERITY
#### L1. Cookie Security in HTTP Proxies 🔵
**Location:** `internal/api/auth.go:75-107`
**Finding:**
When behind an HTTP (non-HTTPS) proxy, cookies fall back to `SameSite=Lax` with `Secure=false`. This is functionally necessary but reduces security.
**Recommendation:**
1. Log a warning when cookies are set without Secure flag
2. Add documentation recommending HTTPS termination at proxy
---
#### L2. Session Token Entropy 🔵
**Location:** `internal/api/auth.go:109-118`
**Finding:**
Session tokens are 32 bytes (256 bits) of entropy via `crypto/rand`, which is excellent. However, the error handling falls back to empty string:
```go
if _, err := cryptorand.Read(b); err != nil {
log.Error().Err(err).Msg("Failed to generate secure session token")
return "" // Fallback - should never happen
}
```
**Recommendation:**
Consider returning an error or panicking rather than returning empty string, as an empty session token could have undefined behavior.
---
#### L3. OIDC State Parameter Validation 🔵
**Location:** `internal/api/security_oidc.go`
**Finding:**
OIDC configuration is properly validated and state parameters should be verified during the OAuth flow. This should be confirmed in the callback handler.
**Recommendation:**
1. Verify state parameter is generated with sufficient entropy
2. Ensure state parameter has short expiration (5-10 minutes)
---
#### L4. Apprise CLI Command Execution 🔵
**Location:** `internal/notifications/notifications.go`
**Finding:**
The Apprise CLI path and targets are passed to `exec.CommandContext`. While the CLI path is configurable:
```go
args := []string{"-t", title, "-b", body}
args = append(args, cfg.Targets...)
execFn := n.appriseExec
```
**Risk:** If an attacker can control `cfg.Targets`, they might inject malicious arguments.
**Recommendation:**
1. Validate that targets match expected Apprise URL format
2. Consider sanitizing or escaping special characters in targets
---
### INFORMATIONAL
#### I1. Dependencies
The `go.mod` shows modern, well-maintained dependencies:
- Go 1.24.0 (latest stable)
- `golang.org/x/crypto v0.45.0` (current)
- `github.com/coreos/go-oidc/v3 v3.17.0` (current)
**Recommendation:**
Run `govulncheck` periodically to scan for known vulnerabilities.
---
#### I2. GitGuardian Integration
The `.gitguardian.yaml` is properly configured to:
- Ignore documentation and example files
- Block placeholder patterns
- Scan actual code and configuration
---
#### I3. Existing Security Audit
The previous audit (2025-11-07) addressed critical vulnerabilities in the sensor-proxy:
- Socket directory tampering (CRITICAL) ✅ Fixed
- SSRF via get_temperature (CRITICAL) ✅ Fixed
- Connection exhaustion DoS (CRITICAL) ✅ Fixed
- Multi-UID rate limit bypass (CRITICAL) ✅ Fixed
- Incomplete GID authorization (MEDIUM) ✅ Fixed
- Unbounded SSH output (MEDIUM) ✅ Fixed
- Weak host key validation (MEDIUM) ✅ Fixed
- Insufficient capability separation (MEDIUM) ✅ Fixed
- Missing systemd hardening (LOW) ✅ Fixed
---
## Security Architecture Summary
### Data Flow Security
```
┌─────────────────────────────────────────────────────────────────┐
│ Client (Browser/API) │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ HTTPS + TLS │ Session Cookie (HttpOnly, Secure, SameSite) ││
│ │ │ CSRF Token (Cookie + Header validation) ││
│ │ │ API Token (Header: X-API-Token) ││
│ └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Pulse Server │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Rate Limiter │ │ Auth Middleware │ │ CSRF Handler │ │
│ │ 10 auth/min │ │ Session/Token │ │ State-changing │ │
│ │ 500 api/min │ │ Validation │ │ operations │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Account Lockout │ │ Command Policy │ │ SSRF Prevention │ │
│ │ 5 attempts/15m │ │ Block/Allow/ │ │ Private IP │ │
│ │ │ │ Require Approval│ │ blocklist │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ Encryption at Rest (AES-256-GCM) ││
│ │ - Node credentials: /etc/pulse/nodes.enc ││
│ │ - Email settings: /etc/pulse/email.enc ││
│ │ - Webhooks: /etc/pulse/webhooks.enc ││
│ │ - OIDC config: /etc/pulse/oidc.enc ││
│ └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
```
### Password/Token Hashing
| Credential Type | Algorithm | Parameters |
|-----------------|-----------|------------|
| User Passwords | bcrypt | Cost factor 12 |
| API Tokens | SHA3-256 | - |
| Encryption Key | AES-256-GCM | 32-byte random key |
| Session Tokens | Random | 32 bytes (256-bit) |
---
## Compliance Checklist
| Requirement | Status | Notes |
|-------------|--------|-------|
| Password hashing | ✅ | bcrypt, cost 12 |
| Session management | ✅ | Secure cookies, 24h expiry |
| CSRF protection | ✅ | Token-based |
| Rate limiting | ✅ | Auth + API endpoints |
| Encryption at rest | ✅ | AES-256-GCM |
| HTTPS support | ✅ | TLS configurable |
| Security headers | ✅ | CSP, X-Frame-Options, etc. |
| Audit logging | ✅ | Auth events logged |
| Input validation | ✅ | SQL params, webhook URLs |
| Command execution control | ✅ | Policy-based |
---
## Recommendations Summary
### Priority 1 (Consider Addressing)
- [ ] M1: Add additional safeguards for dev mode bypass
- [ ] M2: Verify recovery token file permissions
### Priority 2 (Optional Improvements)
- [ ] L1: Add warning logs for non-secure cookies
- [ ] L2: Improve session token generation error handling
- [ ] L3: Document OIDC state parameter security
- [ ] L4: Add Apprise target validation
### Priority 3 (Ongoing)
- [ ] I1: Run `govulncheck` regularly
- [ ] Keep dependencies updated
- [ ] Review GitGuardian alerts
---
## Conclusion
The Pulse application demonstrates a **strong security posture** with comprehensive protections against common web application vulnerabilities. The codebase shows evidence of security-conscious development practices:
1. **Defense in depth** with multiple layers of authentication and authorization
2. **Secure defaults** requiring explicit configuration to reduce security
3. **Modern cryptography** using industry-standard algorithms
4. **Comprehensive validation** of user inputs and external URLs
5. **Audit trails** for security-relevant events
The identified findings are primarily of low to medium severity and represent opportunities for hardening rather than critical vulnerabilities.
**Final Security Grade: A-**
---
## References
- **Previous Audit:** `docs/SECURITY_AUDIT_2025-11-07.md`
- **Security Policy:** `SECURITY.md`
- **Security Changelog:** `docs/SECURITY_CHANGELOG.md`
---
## Audit Team
**Auditor:** Claude (Gemini 2.5)
**Methodology:** Static code analysis and architecture review
**Audit Duration:** 2025-12-18 (single session)
**Files Reviewed:** ~50 source files across 10 packages
---
**For security concerns or questions:**
https://github.com/rcourtman/Pulse/issues

View file

@ -374,9 +374,10 @@ go build ./cmd/pulse-sensor-proxy
### References ### References
- **Audit Report:** `/opt/pulse/docs/SECURITY_AUDIT_2025-11-07.md` (to be created) - **Audit Report:** `docs/SECURITY_AUDIT_2025-11-07.md`
- **Security Architecture:** `/opt/pulse/docs/TEMPERATURE_MONITORING_SECURITY.md` - **Audit Report:** `docs/SECURITY_AUDIT_2025-12-18.md`
- **Hardening Guide:** `/opt/pulse/docs/PULSE_SENSOR_PROXY_HARDENING.md` - **Temperature Monitoring Overview:** `docs/security/TEMPERATURE_MONITORING.md`
- **Sensor Proxy Hardening:** `docs/security/SENSOR_PROXY_HARDENING.md`
--- ---

View file

@ -2,19 +2,37 @@
Monitor real-time CPU and NVMe temperatures for your Proxmox nodes. Monitor real-time CPU and NVMe temperatures for your Proxmox nodes.
> **Deprecation notice (v5):** `pulse-sensor-proxy` is deprecated and not recommended for new deployments. Temperature monitoring should be done via the unified agent (`pulse-agent --enable-proxmox`). Existing proxy installs can continue during the migration window, but plan to migrate to the agent.
## Recommended: Pulse Agent
For new installations, prefer the unified agent on Proxmox hosts. It reads sensors locally and reports temperatures to Pulse without SSH keys or proxy wiring.
```bash
curl -fsSL http://<pulse-ip>:7655/install.sh | \
bash -s -- --url http://<pulse-ip>:7655 --token <api-token> --enable-proxmox
```
If you use the agent method, the rest of this document (sensor proxy) is optional. See `docs/security/TEMPERATURE_MONITORING.md` for the security model overview.
## 🚀 Quick Start ## 🚀 Quick Start
### 1. Enable in Pulse ### 1. Install the agent on Proxmox hosts
Go to **Settings → Nodes → [Node] → Advanced Monitoring** and enable "Temperature Monitoring". Install the unified agent on each Proxmox host with Proxmox integration enabled (example in the section above).
### 2. Install Sensor Proxy ### 2. Enable temperature monitoring (optional)
The setup depends on your deployment: Go to **Settings → Proxmox → [Node] → Advanced Monitoring** and enable "Temperature monitoring" if you want to collect temperatures for that node.
| Deployment | Recommended Method | ## Deprecated: pulse-sensor-proxy (existing installs only)
| :--- | :--- |
| **LXC (Pulse)** | Run the **Setup Script** in Pulse UI. It auto-installs the proxy on the host. | This section is retained for existing installations during the migration window.
| **Docker (Pulse)** | Install proxy on host + bind mount socket. (See below) |
| **Remote Node** | Install proxy in **HTTP Mode** on the remote node. | If you are starting fresh on Pulse v5, do not deploy `pulse-sensor-proxy`. Use the agent method above.
If you already have the proxy deployed:
- Keep it running while you migrate to `pulse-agent --enable-proxmox`.
- Expect future removal in a major release. Do not treat the proxy as a long-term solution.
## 📦 Docker Setup (Manual) ## 📦 Docker Setup (Manual)
@ -61,7 +79,7 @@ If you have Pulse running on **Server A** and want to monitor temperatures on **
| Issue | Solution | | Issue | Solution |
| :--- | :--- | | :--- | :--- |
| **No Data** | Check **Settings → Diagnostics → Temperature Proxy**. | | **No Data** | Check **Settings → Diagnostics** (Temperature Proxy section). |
| **Proxy Unreachable** | Ensure port `8443` is open on the remote node. | | **Proxy Unreachable** | Ensure port `8443` is open on the remote node. |
| **"Permission Denied"** | Re-run the installer to fix permissions or SSH keys. | | **"Permission Denied"** | Re-run the installer to fix permissions or SSH keys. |
| **LXC Issues** | Ensure the container has the bind mount: `lxc.mount.entry: /run/pulse-sensor-proxy ...` | | **LXC Issues** | Ensure the container has the bind mount: `lxc.mount.entry: /run/pulse-sensor-proxy ...` |
@ -98,7 +116,7 @@ If you can't run the installer script, create the configuration manually:
```bash ```bash
curl -L https://github.com/rcourtman/Pulse/releases/latest/download/pulse-sensor-proxy-linux-amd64 \ curl -L https://github.com/rcourtman/Pulse/releases/latest/download/pulse-sensor-proxy-linux-amd64 \
-o /tmp/pulse-sensor-proxy -o /tmp/pulse-sensor-proxy
install -D -m 0755 /tmp/pulse-sensor-proxy /opt/pulse/sensor-proxy/bin/pulse-sensor-proxy install -D -m 0755 /tmp/pulse-sensor-proxy /usr/local/bin/pulse-sensor-proxy
``` ```
**2. Create service user:** **2. Create service user:**
@ -128,7 +146,7 @@ Allowed nodes live in `/etc/pulse-sensor-proxy/allowed_nodes.yaml`; change them
**5. Install systemd service:** **5. Install systemd service:**
```bash ```bash
# Download from: https://github.com/rcourtman/Pulse/releases/latest/download/install-sensor-proxy.sh # Download from: https://github.com/rcourtman/Pulse/releases/latest/download/install-sensor-proxy.sh
# Extract the systemd unit from the installer (ExecStartPre/ExecStart use /opt/pulse/sensor-proxy/bin) # Extract the systemd unit from the installer (ExecStartPre/ExecStart typically uses /usr/local/bin/pulse-sensor-proxy)
systemctl daemon-reload systemctl daemon-reload
systemctl enable --now pulse-sensor-proxy systemctl enable --now pulse-sensor-proxy
``` ```
@ -230,16 +248,15 @@ The installer is idempotent and safe to re-run:
# After adding a new Proxmox node to cluster # After adding a new Proxmox node to cluster
bash install-sensor-proxy.sh --standalone --pulse-server http://pulse:7655 --quiet bash install-sensor-proxy.sh --standalone --pulse-server http://pulse:7655 --quiet
# After upgrading Pulse version
bash install-sensor-proxy.sh --standalone --pulse-server http://pulse:7655 --version v4.27.0 --quiet
# Verify installation # Verify installation
systemctl status pulse-sensor-proxy systemctl status pulse-sensor-proxy
``` ```
### Legacy Security Concerns (Pre-v4.24.0) ### Legacy SSH Security Concerns
Older versions stored SSH keys inside the container, creating security risks: SSH-based temperature collection from inside containers is unsafe. Pulse blocks this by default for container deployments.
In legacy/non-container setups where you intentionally use SSH, the main risks are:
- Compromised container = exposed SSH keys - Compromised container = exposed SSH keys
- Even with forced commands, keys could be extracted - Even with forced commands, keys could be extracted
@ -402,14 +419,15 @@ Rotate SSH keys periodically for security (recommended every 90 days).
**Automated Rotation (Recommended):** **Automated Rotation (Recommended):**
The `/opt/pulse/scripts/pulse-proxy-rotate-keys.sh` script handles rotation safely with staging, verification, and rollback support: The `pulse-proxy-rotate-keys.sh` helper script handles rotation safely with staging, verification, and rollback support:
```bash ```bash
# 1. Dry-run first (recommended) # 1. Dry-run first (recommended)
sudo /opt/pulse/scripts/pulse-proxy-rotate-keys.sh --dry-run curl -fsSL https://raw.githubusercontent.com/rcourtman/Pulse/main/scripts/pulse-proxy-rotate-keys.sh | \
sudo bash -s -- --dry-run
# 2. Perform rotation # 2. Perform rotation
sudo /opt/pulse/scripts/pulse-proxy-rotate-keys.sh curl -fsSL https://raw.githubusercontent.com/rcourtman/Pulse/main/scripts/pulse-proxy-rotate-keys.sh | sudo bash
``` ```
**What the script does:** **What the script does:**
@ -421,7 +439,8 @@ sudo /opt/pulse/scripts/pulse-proxy-rotate-keys.sh
**If rotation fails, rollback:** **If rotation fails, rollback:**
```bash ```bash
sudo /opt/pulse/scripts/pulse-proxy-rotate-keys.sh --rollback curl -fsSL https://raw.githubusercontent.com/rcourtman/Pulse/main/scripts/pulse-proxy-rotate-keys.sh | \
sudo bash -s -- --rollback
``` ```
**Manual Rotation (Fallback):** **Manual Rotation (Fallback):**
@ -444,11 +463,11 @@ curl -fsSL https://github.com/rcourtman/Pulse/releases/latest/download/install-s
# 4. Verify temperature data still works in Pulse UI # 4. Verify temperature data still works in Pulse UI
``` ```
### Automatic Cleanup When Nodes Are Removed (v4.26.0+) ### Automatic Cleanup When Nodes Are Removed
Starting in v4.26.0, SSH keys are **automatically removed** when you delete a node from Pulse: SSH keys are automatically removed when you delete a node from Pulse:
1. **When you remove a node** in Pulse Settings → Nodes, Pulse signals the temperature proxy 1. **When you remove a node** in Pulse (**Settings → Proxmox**), Pulse signals the temperature proxy
2. **The proxy creates a cleanup request** file at `/var/lib/pulse-sensor-proxy/cleanup-request.json` 2. **The proxy creates a cleanup request** file at `/var/lib/pulse-sensor-proxy/cleanup-request.json`
3. **A systemd path unit detects the request** and triggers the cleanup service 3. **A systemd path unit detects the request** and triggers the cleanup service
4. **The cleanup script automatically:** 4. **The cleanup script automatically:**
@ -572,7 +591,7 @@ test -S /run/pulse-sensor-proxy/pulse-sensor-proxy.sock && echo "Socket OK" || e
- Proxy uses `pvecm status` to discover cluster nodes (requires Proxmox IPC access) - Proxy uses `pvecm status` to discover cluster nodes (requires Proxmox IPC access)
- If Proxmox hardens IPC access or cluster topology changes unexpectedly, discovery may fail - If Proxmox hardens IPC access or cluster topology changes unexpectedly, discovery may fail
- Standalone Proxmox nodes work but only monitor that single node - Standalone Proxmox nodes work but only monitor that single node
- Fallback: Re-run setup script manually to reconfigure cluster access - Fallback: re-run the proxy installer script to reconfigure cluster access
**Rate Limiting & Scaling** (updated in commit 46b8b8d): **Rate Limiting & Scaling** (updated in commit 46b8b8d):
@ -637,7 +656,7 @@ test -S /run/pulse-sensor-proxy/pulse-sensor-proxy.sock && echo "Socket OK" || e
**New Cluster Node Not Showing Temperatures:** **New Cluster Node Not Showing Temperatures:**
1. Ensure lm-sensors installed: `ssh root@new-node "sensors -j"` 1. Ensure lm-sensors installed: `ssh root@new-node "sensors -j"`
2. Proxy auto-discovers on next poll (may take up to 1 minute) 2. Proxy auto-discovers on next poll (may take up to 1 minute)
3. Re-run the setup script to configure SSH keys on the new node: `curl -fsSL https://github.com/rcourtman/Pulse/releases/latest/download/install-sensor-proxy.sh | bash -s -- --ctid <CTID>` 3. Re-run the proxy installer script to configure SSH keys on the new node: `curl -fsSL https://github.com/rcourtman/Pulse/releases/latest/download/install-sensor-proxy.sh | bash -s -- --ctid <CTID>`
**Permission Denied Errors:** **Permission Denied Errors:**
1. Verify socket permissions: `ls -l /run/pulse-sensor-proxy/pulse-sensor-proxy.sock` 1. Verify socket permissions: `ls -l /run/pulse-sensor-proxy/pulse-sensor-proxy.sock`
@ -646,55 +665,13 @@ test -S /run/pulse-sensor-proxy/pulse-sensor-proxy.sock && echo "Socket OK" || e
**Proxy Service Won't Start:** **Proxy Service Won't Start:**
1. Check logs: `journalctl -u pulse-sensor-proxy -n 50` 1. Check logs: `journalctl -u pulse-sensor-proxy -n 50`
2. Verify binary exists: `ls -l /opt/pulse/sensor-proxy/bin/pulse-sensor-proxy` 2. Verify binary exists: `ls -l /usr/local/bin/pulse-sensor-proxy`
3. Test manually: `/opt/pulse/sensor-proxy/bin/pulse-sensor-proxy --version` 3. Test manually: `/usr/local/bin/pulse-sensor-proxy --version`
4. Check socket directory: `ls -ld /var/run` 4. Check socket directory: `ls -ld /var/run`
### Future Improvements
**Potential Enhancements (Roadmap):**
1. **Proxmox API Integration**
- If future Proxmox versions expose temperature telemetry via API, retire SSH approach
- Would eliminate SSH key management and improve security posture
- Monitor Proxmox development for metrics/RRD temperature endpoints
2. **Agent-Based Architecture**
- Deploy lightweight agents on each node for richer telemetry
- Reduces SSH fan-out overhead for large clusters
- Trade-off: Adds deployment/update complexity
- Consider only if demand for additional metrics grows
3. **SNMP/IPMI Support**
- Optional integration for baseboard management controllers
- Better for hardware-level sensors (baseboard temps, fan speeds)
- Requires hardware/firmware support, so keep as optional add-on
4. **Schema Validation**
- Add JSON schema validation for `sensors -j` output
- Detect format changes early with instrumentation
- Log warnings when unexpected sensor formats appear
5. **Caching & Throttling**
- Implement result caching for large clusters (10+ nodes)
- Reduce SSH overhead with configurable TTL
- Add request throttling to prevent SSH rate limiting
6. **Automated Key Rotation**
- Systemd timer for automatic 90-day rotation
- Already supported via `/opt/pulse/scripts/pulse-proxy-rotate-keys.sh`
- Just needs timer unit configuration (documented in hardening guide)
7. **Health Check Endpoint**
- Add `/health` endpoint separate from Prometheus metrics
- Enable external monitoring systems (Nagios, Zabbix, etc.)
- Return proxy status, socket accessibility, and last successful poll
**Contributions Welcome:** If any of these improvements interest you, open a GitHub issue to discuss implementation!
## Configuration Management ## Configuration Management
Starting with v4.31.1, the sensor proxy includes a built-in CLI for safe configuration management. This prevents config corruption that caused 99% of temperature monitoring failures. The sensor proxy includes a built-in CLI for safe configuration management. It uses locking and atomic writes to prevent config corruption.
### Quick Reference ### Quick Reference
@ -717,19 +694,19 @@ pulse-sensor-proxy config set-allowed-nodes --replace --merge 192.168.0.1
**See also:** **See also:**
- [Sensor Proxy Config Management Guide](operations/SENSOR_PROXY_CONFIG.md) - Complete runbook - [Sensor Proxy Config Management Guide](operations/SENSOR_PROXY_CONFIG.md) - Complete runbook
- [Sensor Proxy CLI Reference](/opt/pulse/cmd/pulse-sensor-proxy/README.md) - Full command documentation - [Sensor Proxy CLI Reference](../cmd/pulse-sensor-proxy/README.md) - Full command documentation
## Control-Plane Sync & Migration ## Control-Plane Sync & Migration
As of v4.32 the sensor proxy registers with Pulse and syncs its authorized node list via `/api/temperature-proxy/authorized-nodes`. No more manual `allowed_nodes` maintenance or `/etc/pve` access is required. The sensor proxy can register with Pulse and sync its authorized node list via `/api/temperature-proxy/authorized-nodes`. This avoids manual `allowed_nodes` maintenance and reduces reliance on `/etc/pve` access.
### New installs ### New installs
Always pass the Pulse URL when installing: Always pass the Pulse URL when installing:
```bash ```bash
curl -sSL https://pulse.example.com/api/install/install-sensor-proxy.sh \ curl -fsSL https://github.com/rcourtman/Pulse/releases/latest/download/install-sensor-proxy.sh | \
| sudo bash -s -- --ctid 108 --pulse-server http://192.168.0.149:7655 sudo bash -s -- --ctid <pulse-lxc-id> --pulse-server http://<pulse-ip>:7655
``` ```
The installer now: The installer now:
@ -740,11 +717,11 @@ The installer now:
### Migrating existing hosts ### Migrating existing hosts
If you installed before v4.32, run the migration helper on each host: If your proxy was installed without control-plane sync enabled, run the migration helper on each host:
```bash ```bash
curl -sSL https://pulse.example.com/api/install/migrate-sensor-proxy-control-plane.sh \ curl -fsSL http://<pulse-ip>:7655/api/install/migrate-sensor-proxy-control-plane.sh | \
| sudo bash -s -- --pulse-server http://192.168.0.149:7655 sudo bash -s -- --pulse-server http://<pulse-ip>:7655
``` ```
The script registers the existing proxy, writes the control token, updates the config, and restarts the service (use `--skip-restart` if you prefer to bounce it yourself). Once migrated, temperatures for every node defined in Pulse will continue working even if the proxy cant reach `/etc/pve` or Corosync IPC. The script registers the existing proxy, writes the control token, updates the config, and restarts the service (use `--skip-restart` if you prefer to bounce it yourself). Once migrated, temperatures for every node defined in Pulse will continue working even if the proxy cant reach `/etc/pve` or Corosync IPC.

View file

@ -7,10 +7,16 @@
```bash ```bash
docker exec pulse rm /data/.env docker exec pulse rm /data/.env
docker restart pulse docker restart pulse
# Access UI to run setup wizard again # Access UI again. Pulse will require a bootstrap token for setup.
# Get it with:
docker exec pulse /app/pulse bootstrap-token
``` ```
**Systemd**: **Systemd**:
Delete `/etc/pulse/.env` and restart the service. Delete `/etc/pulse/.env` and restart the service. Pulse will require a bootstrap token for setup:
```bash
sudo pulse bootstrap-token
```
### Port change didn't take effect ### Port change didn't take effect
1. Check which service is running: `systemctl status pulse` (or `pulse-backend`). 1. Check which service is running: `systemctl status pulse` (or `pulse-backend`).
@ -47,7 +53,7 @@ Delete `/etc/pulse/.env` and restart the service.
**Temperature data missing** **Temperature data missing**
- Install `lm-sensors` on the host. - Install `lm-sensors` on the host.
- Run `sensors-detect`. - Run `sensors-detect`.
- Re-run the Pulse setup script to install the sensor proxy. - Install the unified agent on the Proxmox host with `--enable-proxmox`.
- See [Temperature Monitoring](TEMPERATURE_MONITORING.md). - See [Temperature Monitoring](TEMPERATURE_MONITORING.md).
**Docker hosts appearing/disappearing** **Docker hosts appearing/disappearing**
@ -78,10 +84,15 @@ grep "request_id=abc123" /var/log/pulse/pulse.log
### Check Permissions (Proxmox) ### Check Permissions (Proxmox)
If Pulse can't see VMs or storage, check the user permissions on Proxmox: If Pulse can't see VMs or storage, check the user permissions on Proxmox:
```bash ```bash
pveum user permissions pulse-monitor@pam pveum user permissions <user>@pam
``` ```
Required: `PVEAuditor` role. At minimum, ensure the user/token has read access for inventory and metrics:
Recommended: `VM.Audit`, `Sys.Audit` (added by setup script).
- `Sys.Audit`
- `VM.Monitor`
- `Datastore.Audit`
For VM disk usage via QEMU guest agent, also ensure `VM.GuestAgent.Audit` (PVE 9+).
### Recovery Mode ### Recovery Mode
If you are completely locked out, you can trigger a recovery token from the localhost CLI: If you are completely locked out, you can trigger a recovery token from the localhost CLI:

View file

@ -52,6 +52,7 @@ curl -fsSL http://<pulse-ip>:7655/install.sh | \
| `--kube-include-namespace` | `PULSE_KUBE_INCLUDE_NAMESPACES` | Limit namespaces (repeatable or CSV, wildcards supported) | *(all)* | | `--kube-include-namespace` | `PULSE_KUBE_INCLUDE_NAMESPACES` | Limit namespaces (repeatable or CSV, wildcards supported) | *(all)* |
| `--kube-exclude-namespace` | `PULSE_KUBE_EXCLUDE_NAMESPACES` | Exclude namespaces (repeatable or CSV, wildcards supported) | *(none)* | | `--kube-exclude-namespace` | `PULSE_KUBE_EXCLUDE_NAMESPACES` | Exclude namespaces (repeatable or CSV, wildcards supported) | *(none)* |
| `--kube-include-all-pods` | `PULSE_KUBE_INCLUDE_ALL_PODS` | Include all non-succeeded pods | `false` | | `--kube-include-all-pods` | `PULSE_KUBE_INCLUDE_ALL_PODS` | Include all non-succeeded pods | `false` |
| `--kube-include-all-deployments` | `PULSE_KUBE_INCLUDE_ALL_DEPLOYMENTS` | Include all deployments, not just problems | `false` |
| `--kube-max-pods` | `PULSE_KUBE_MAX_PODS` | Max pods per report | `200` | | `--kube-max-pods` | `PULSE_KUBE_MAX_PODS` | Max pods per report | `200` |
| `--disable-auto-update` | `PULSE_DISABLE_AUTO_UPDATE` | Disable auto-updates | `false` | | `--disable-auto-update` | `PULSE_DISABLE_AUTO_UPDATE` | Disable auto-updates | `false` |
| `--insecure` | `PULSE_INSECURE_SKIP_VERIFY` | Skip TLS verification | `false` | | `--insecure` | `PULSE_INSECURE_SKIP_VERIFY` | Skip TLS verification | `false` |

66
docs/UPGRADE_v5.md Normal file
View file

@ -0,0 +1,66 @@
# Upgrade to Pulse v5
This is a practical guide for upgrading an existing Pulse install to v5.
## Before You Upgrade
- Create an encrypted config backup: **Settings → System → Backups → Create Backup**
- Confirm you can access the host/container console (for rollback and bootstrap token retrieval)
- Review the v5 release notes on GitHub before upgrading
## Upgrade Paths
### systemd and Proxmox LXC installs
Preferred path:
- **Settings → System → Updates**
If you prefer CLI, use the official installer for the target version:
```bash
curl -fsSL https://github.com/rcourtman/Pulse/releases/latest/download/install.sh | \
sudo bash -s -- --version v5.0.0
```
### Docker
```bash
docker pull rcourtman/pulse:latest
docker compose up -d
```
### Kubernetes (Helm)
```bash
helm upgrade pulse oci://ghcr.io/rcourtman/pulse-chart -n pulse
```
## Post-Upgrade Checklist
- Confirm version: `GET /api/version`
- Confirm scheduler health: `GET /api/monitoring/scheduler/health`
- Confirm nodes are polling and no breakers are stuck open
- Confirm notifications still send (send a test)
- Confirm agents are connected (if used)
## Notes and Common Gotchas
### Bootstrap token on fresh auth setup
If you reset auth (for example by deleting `.env`), Pulse may require a bootstrap token before you can complete setup.
- Docker: `docker exec pulse /app/pulse bootstrap-token`
- systemd/LXC: `sudo pulse bootstrap-token`
### Temperature monitoring in containers
If Pulse runs in a container and you are relying on SSH-based temperature collection, v5 blocks that in hardened configurations.
Preferred option:
- Install the unified agent (`pulse-agent`) on Proxmox hosts with `--enable-proxmox`
Deprecated option (existing installs only):
- `pulse-sensor-proxy` continues to work for now, but it is deprecated in v5 and not recommended for new installs. Plan to migrate to the unified agent.

View file

@ -20,14 +20,13 @@ Monitor actual disk usage inside your VMs using the QEMU Guest Agent.
* **Proxmox Permissions**: * **Proxmox Permissions**:
* **Proxmox 8**: `VM.Monitor` * **Proxmox 8**: `VM.Monitor`
* **Proxmox 9+**: `VM.GuestAgent.Audit` * **Proxmox 9+**: `VM.GuestAgent.Audit`
* *Note: The Pulse setup script handles these permissions automatically.*
## 🔧 Troubleshooting ## 🔧 Troubleshooting
| Issue | Solution | | Issue | Solution |
| :--- | :--- | | :--- | :--- |
| **Disk shows "-"** | Hover over the dash for details. Common causes: Agent not running, disabled in config, or permission denied. | | **Disk shows "-"** | Hover over the dash for details. Common causes: Agent not running, disabled in config, or permission denied. |
| **Permission Denied** | Re-run the Pulse setup script to update permissions, or manually add `VM.GuestAgent.Audit`. | | **Permission Denied** | Ensure your Proxmox token/user has `VM.GuestAgent.Audit` (PVE 9+) or `VM.Monitor` (PVE 8). |
| **Agent Timeout** | Increase timeouts via env vars if network is slow: `GUEST_AGENT_FSINFO_TIMEOUT=10s`. | | **Agent Timeout** | Increase timeouts via env vars if network is slow: `GUEST_AGENT_FSINFO_TIMEOUT=10s`. |
| **Windows VMs** | Ensure the **QEMU Guest Agent** service is running in Windows Services. | | **Windows VMs** | Ensure the **QEMU Guest Agent** service is running in Windows Services. |

View file

@ -39,5 +39,5 @@ For generic webhooks, use Go templates to format the JSON payload.
## 🛡️ Security ## 🛡️ Security
- **Private IPs**: By default, webhooks to private IPs are blocked. Allow them in **Settings → System → Webhook Security**. - **Private IPs**: By default, webhooks to private IPs are blocked. Allow them in **Settings → System → Network → Webhook Security**.
- **Headers**: Add custom headers (e.g., `Authorization: Bearer ...`) in the webhook config. - **Headers**: Add custom headers (e.g., `Authorization: Bearer ...`) in the webhook config.

View file

@ -23,7 +23,7 @@ pveum acl modify /nodes -user pulse-monitor@pam -role PVEAuditor
ZFS monitoring is **enabled by default**. To disable it: ZFS monitoring is **enabled by default**. To disable it:
```bash ```bash
# Add to /opt/pulse/.env # Add to /etc/pulse/.env (systemd/LXC) or /data/.env (Docker/Kubernetes)
PULSE_DISABLE_ZFS_MONITORING=true PULSE_DISABLE_ZFS_MONITORING=true
``` ```
@ -39,4 +39,4 @@ PULSE_DISABLE_ZFS_MONITORING=true
**No ZFS Data?** **No ZFS Data?**
1. Check permissions: `pveum user permissions pulse-monitor@pam`. 1. Check permissions: `pveum user permissions pulse-monitor@pam`.
2. Verify pools exist: `zpool list`. 2. Verify pools exist: `zpool list`.
3. Check logs: `grep ZFS /opt/pulse/pulse.log`. 3. Check logs: `journalctl -u pulse -n 200 | grep -i zfs`.

View file

@ -9,19 +9,22 @@ Pulse uses an adaptive scheduler to optimize polling based on instance health an
* **Backoff**: Exponential retry delays (5s to 5m). * **Backoff**: Exponential retry delays (5s to 5m).
## ⚙️ Configuration ## ⚙️ Configuration
Adaptive polling is **enabled by default**. Adaptive polling is **disabled by default**.
### UI ### UI
**Settings → System → Monitoring**. There is currently no dedicated UI for adaptive polling in v5.
### Environment Variables ### Environment Variables
| Variable | Default | Description | | Variable | Default | Description |
| :--- | :--- | :--- | | :--- | :--- | :--- |
| `ADAPTIVE_POLLING_ENABLED` | `true` | Enable/disable. | | `ADAPTIVE_POLLING_ENABLED` | `false` | Enable/disable. |
| `ADAPTIVE_POLLING_BASE_INTERVAL` | `10s` | Healthy poll rate. | | `ADAPTIVE_POLLING_BASE_INTERVAL` | `10s` | Healthy poll rate. |
| `ADAPTIVE_POLLING_MIN_INTERVAL` | `5s` | Active/busy rate. | | `ADAPTIVE_POLLING_MIN_INTERVAL` | `5s` | Active/busy rate. |
| `ADAPTIVE_POLLING_MAX_INTERVAL` | `5m` | Idle/backoff rate. | | `ADAPTIVE_POLLING_MAX_INTERVAL` | `5m` | Idle/backoff rate. |
### system.json
You can also set `adaptivePollingEnabled` (and related interval fields) in `system.json` and restart Pulse.
## 📊 Metrics ## 📊 Metrics
Exposed at `:9091/metrics`. Exposed at `:9091/metrics`.

View file

@ -2,6 +2,12 @@
Pulse exposes metrics at `/metrics` (default port `9091`). Pulse exposes metrics at `/metrics` (default port `9091`).
Example scrape target:
- `http://<pulse-host>:9091/metrics`
This listener is separate from the main UI/API port (`7655`). In Docker and Kubernetes you must expose `9091` explicitly if you want to scrape it from outside the container/pod.
## 🌐 HTTP Ingress ## 🌐 HTTP Ingress
| Metric | Type | Description | | Metric | Type | Description |
| :--- | :--- | :--- | | :--- | :--- | :--- |

View file

@ -1,18 +1,20 @@
# 🚀 Adaptive Polling Rollout # 🚀 Adaptive Polling Rollout
Safely enable dynamic scheduling (v4.24.0+). Safely enable dynamic scheduling (v5+).
## 📋 Pre-Flight ## 📋 Pre-Flight
1. **Snapshot Health**: 1. **Snapshot Health**:
```bash ```bash
curl -s http://localhost:7655/api/monitoring/scheduler/health | jq . curl -s -H "X-API-Token: $TOKEN" http://localhost:7655/api/monitoring/scheduler/health | jq .
``` ```
2. **Check Metrics**: Ensure `pulse_monitor_poll_queue_depth` is stable. 2. **Check Metrics**: Ensure `pulse_monitor_poll_queue_depth` is stable.
## 🟢 Enable ## 🟢 Enable
Choose one method: Choose one method:
* **UI**: Settings → System → Monitoring → Adaptive Polling. * **UI**: Not currently exposed in the v5 UI (use CLI or env vars).
* **CLI**: `jq '.AdaptivePollingEnabled=true' /var/lib/pulse/system.json > tmp && mv tmp system.json` * **CLI**:
- systemd/LXC: `jq '.adaptivePollingEnabled=true' /etc/pulse/system.json > /tmp/system.json && sudo mv /tmp/system.json /etc/pulse/system.json`
- Docker/Kubernetes: edit `/data/system.json` in the volume and restart the container/pod
* **Env**: `ADAPTIVE_POLLING_ENABLED=true` (Docker/K8s). * **Env**: `ADAPTIVE_POLLING_ENABLED=true` (Docker/K8s).
## 🔍 Monitor (First 15m) ## 🔍 Monitor (First 15m)
@ -25,6 +27,6 @@ watch -n 5 'curl -s http://localhost:9091/metrics | grep pulse_monitor_poll_queu
## ↩️ Rollback ## ↩️ Rollback
If instability occurs > 10m: If instability occurs > 10m:
1. **Disable**: Toggle off via UI or Env. 1. **Disable**: Remove the env var override or set `adaptivePollingEnabled=false` in `system.json`.
2. **Restart**: Required if using Env/CLI overrides. 2. **Restart**: Required if using Env/CLI overrides.
3. **Verify**: Confirm queue drains. 3. **Verify**: Confirm queue drains.

View file

@ -1,5 +1,8 @@
# 🔄 Sensor Proxy Audit Log Rotation # 🔄 Sensor Proxy Audit Log Rotation
> **Deprecated in v5:** `pulse-sensor-proxy` is deprecated and not recommended for new deployments.
> This document is retained for existing installations during the migration window.
The proxy writes append-only, hash-chained logs to `/var/log/pulse/sensor-proxy/audit.log`. The proxy writes append-only, hash-chained logs to `/var/log/pulse/sensor-proxy/audit.log`.
## ⚠️ Important ## ⚠️ Important

View file

@ -13,16 +13,16 @@ Manage Pulse auto-updates on host-mode installations.
## 🚀 Enable/Disable ## 🚀 Enable/Disable
### Via UI (Recommended) ### Via UI (Recommended)
**Settings → System → Updates → Automatic Updates**. **Settings → System → Updates**.
### Via CLI ### Via CLI
```bash ```bash
# Enable # Enable
sudo jq '.autoUpdateEnabled=true' /var/lib/pulse/system.json > tmp && sudo mv tmp /var/lib/pulse/system.json sudo jq '.autoUpdateEnabled=true' /etc/pulse/system.json > /tmp/system.json && sudo mv /tmp/system.json /etc/pulse/system.json
sudo systemctl enable --now pulse-update.timer sudo systemctl enable --now pulse-update.timer
# Disable # Disable
sudo jq '.autoUpdateEnabled=false' /var/lib/pulse/system.json > tmp && sudo mv tmp /var/lib/pulse/system.json sudo jq '.autoUpdateEnabled=false' /etc/pulse/system.json > /tmp/system.json && sudo mv /tmp/system.json /etc/pulse/system.json
sudo systemctl disable --now pulse-update.timer sudo systemctl disable --now pulse-update.timer
``` ```
@ -40,8 +40,9 @@ journalctl -u pulse-update -f
## ↩️ Rollback ## ↩️ Rollback
If an update fails: If an update fails:
1. Check logs: `/var/log/pulse/update-YYYYMMDDHHMMSS.log`. 1. Check logs: `/var/log/pulse/update-YYYYMMDDHHMMSS.log`.
2. Revert manually: 2. Use the **Rollback** action in **Settings → System → Updates** if available for your deployment type.
3. If you need to pin a specific version, re-run the installer with a version:
```bash ```bash
sudo /opt/pulse/install.sh --version v4.30.0 curl -fsSL https://github.com/rcourtman/Pulse/releases/latest/download/install.sh | \
sudo bash -s -- --version vX.Y.Z
``` ```
Or use the **Rollback** button in the UI if available.

View file

@ -1,10 +1,10 @@
# ⚙️ Sensor Proxy Configuration # ⚙️ Sensor Proxy Configuration
> **⚠️ Deprecated:** The sensor-proxy is deprecated in favor of the unified Pulse agent. > **Deprecated in v5:** `pulse-sensor-proxy` is deprecated and not recommended for new deployments.
> For new installations, use `install.sh --enable-proxmox` instead. > Use `pulse-agent --enable-proxmox` for temperature monitoring.
> See [TEMPERATURE_MONITORING.md](/docs/security/TEMPERATURE_MONITORING.md). > This document is retained for existing installations during the migration window.
Safe configuration management using the CLI (v4.31.1+). Safe configuration management using the built-in CLI.
## 📂 Files ## 📂 Files
* **`config.yaml`**: General settings (logging, metrics). * **`config.yaml`**: General settings (logging, metrics).

View file

@ -1,8 +1,8 @@
# 📝 Sensor Proxy Log Forwarding # 📝 Sensor Proxy Log Forwarding
> **⚠️ Deprecated:** The sensor-proxy is deprecated in favor of the unified Pulse agent. > **Deprecated in v5:** `pulse-sensor-proxy` is deprecated and not recommended for new deployments.
> For new installations, use `install.sh --enable-proxmox` instead. > Use `pulse-agent --enable-proxmox` for temperature monitoring.
> See [TEMPERATURE_MONITORING.md](/docs/security/TEMPERATURE_MONITORING.md). > This document is retained for existing installations during the migration window.
Forward `audit.log` and `proxy.log` to a central SIEM via RELP + TLS. Forward `audit.log` and `proxy.log` to a central SIEM via RELP + TLS.
@ -16,7 +16,7 @@ sudo REMOTE_HOST=logs.example.com \
CA_CERT=/path/to/ca.crt \ CA_CERT=/path/to/ca.crt \
CLIENT_CERT=/path/to/client.crt \ CLIENT_CERT=/path/to/client.crt \
CLIENT_KEY=/path/to/client.key \ CLIENT_KEY=/path/to/client.key \
/opt/pulse/scripts/setup-log-forwarding.sh bash -c "$(curl -fsSL https://raw.githubusercontent.com/rcourtman/Pulse/main/scripts/setup-log-forwarding.sh)"
``` ```
## 📋 What It Does ## 📋 What It Does

View file

@ -0,0 +1,137 @@
# Release Notes (v4 archive)
This file archives the v4-era release notes that previously lived at `docs/RELEASE_NOTES.md`.
For current releases, refer to GitHub Releases:
https://github.com/rcourtman/Pulse/releases
---
# Pulse v4.31.0
## What's Changed
### Temperature monitoring over HTTPS
- `pulse-sensor-proxy` now exposes an authenticated HTTPS endpoint per Proxmox host. Pulse stores each proxys URL + bearer token and always polls `https://node:8443/temps` before falling back to local sockets or SSH, eliminating the fragile “single proxy for every node” chain.
- Installations auto-register via the new `/api/temperature-proxy/register` endpoint, generate 4096-bit certificates, enforce CIDR allowlists, and log every HTTP request through the proxys audit pipeline.
- The backend temperature collector understands proxy URLs/tokens, respects strict timeouts, and publishes richer diagnostics so operators can see which node failed and why.
### Installer, diagnostics, and UI updates
- `scripts/install-sensor-proxy.sh` gained `--http-mode` / `--http-addr`, automatic TLS generation, rollback-on-failure, allowed subnet auto-population, and a comprehensive uninstall path that purges sockets, TLS secrets, and LXC bind mounts.
- A new `Settings → Diagnostics → Temperature Proxy` table surfaces proxy health, registration status, and the errors returned by the HTTPS endpoint.
- `scripts/tests/test-sensor-proxy-http.sh` exercises the HTTP installer path end-to-end inside Docker to prevent regressions.
### Host agent refinements
- Windows PowerShell installers/uninstallers now log verbosely, harden permissions, and clean up services more reliably.
- Linux host-agent scripts aligned with the new diagnostics UX and scoped token workflow so onboarding is less error-prone.
## Upgrade Notes
Temperature monitoring will not work for remote nodes until every Proxmox host is reinstalled with the new HTTPS workflow. Follow these steps per host:
```bash
# 1. Remove any pre-v4.31.0 proxy install
curl -fsSL https://github.com/rcourtman/Pulse/releases/latest/download/install-sensor-proxy.sh | \
sudo bash -s -- --uninstall --purge
# 2. Install the HTTP-enabled proxy and register it with Pulse
curl -fsSL https://github.com/rcourtman/Pulse/releases/latest/download/install-sensor-proxy.sh | \
sudo bash -s -- --standalone --http-mode --pulse-server https://your-pulse-host:7655
```
Only the Pulse server (or container host) needs network access to TCP/8443 on each node. After reinstalling, open **Settings → Diagnostics → Temperature Proxy** to confirm each node reports “HTTPS proxy healthy”. If not, grab the diagnostics entry or run:
```bash
curl -vk https://node.example:8443/health \
-H "Authorization: Bearer $(sudo cat /etc/pulse-sensor-proxy/.http-auth-token)"
```
## Installation
- **Install or upgrade with the helper script**
```bash
curl -sL https://github.com/rcourtman/Pulse/releases/latest/download/install.sh | bash
```
- **Binary upgrade on systemd hosts**
```bash
sudo systemctl stop pulse
curl -fsSL https://github.com/rcourtman/Pulse/releases/download/v4.31.0/pulse-v4.31.0-linux-amd64.tar.gz \
| sudo tar -xz -C /opt/pulse --strip-components=1
sudo systemctl start pulse
```
- **Docker update**
```bash
docker pull rcourtman/pulse:v4.31.0
docker stop pulse || true
docker rm pulse || true
docker run -d --name pulse --restart unless-stopped -p 7655:7655 rcourtman/pulse:v4.31.0
```
- **Helm upgrade**
```bash
helm upgrade --install pulse oci://ghcr.io/rcourtman/pulse-chart \
--version 4.31.0 \
--namespace pulse --create-namespace
```
## Downloads
- Multi-arch Linux tarballs (amd64/arm64/armv7)
- Standalone sensor proxy binaries (now include HTTP mode)
- Helm chart archive (pulse-4.31.0-helm.tgz)
- SHA256 checksums (checksums.txt)
- Docker tags: rcourtman/pulse:v4.31.0, :4.31, :4, :latest
---
# Pulse v4.26.1
## What's Changed
### New
- Standalone host agents now ship with guided Linux, macOS, and Windows installers that stream registration status back to Pulse, generate scoped commands from **Settings → Agents**, and feed host metrics into alerts alongside Proxmox and Docker.
- Alert thresholds gained host-level overrides, connectivity toggles, and snapshot size guardrails so you can tune offline behaviour per host while keeping a global policy for other resources.
- API tokens now support fine-grained scopes with a redesigned manager that previews command templates, highlights unused credentials, and makes revocation a single click.
- Proxmox replication jobs surface in a dedicated **Settings → Hosts → Replication** view with API plumbing to track task health and bubble failures into the monitoring pipeline.
- Docker Swarm environments now receive service/task-aware reporting with configurable scope, plus a Docker settings view that highlights manager/worker roles, stack health, rollout status, and service alert thresholds.
### Improvements
- Dashboard loads and drawer links respond faster thanks to cached guest metadata, reduced polling allocations, and inline URL editing that no longer flashes on WebSocket updates.
- Settings navigation is reorganized with dedicated Docker and Hosts sections, richer filters, and platform icons that make agent onboarding and discovery workflows clearer.
- LXC guests now report dynamic interface IPs, configuration metadata, and queue metrics so alerting, discovery, and drawers stay accurate even during rapid container churn.
- Notifications consolidate into a consistent toast system, with clearer feedback during agent setup, token generation, and background job state changes.
### Bug Fixes
- Enforced explicit node naming and respected custom Proxmox ports so cluster discovery, overrides, and disk monitoring defaults remain intact after edits.
- Hardened setup-token flows and checksum handling in the installers to prevent stale credentials and guarantee the correct binaries are fetched.
- Treated 501 responses from the Proxmox API as non-fatal during failover, restored FreeBSD disk counter parsing, and stopped guest link icons from re-triggering animations on updates.
- Preserved inline editor state across WebSocket refreshes and ensured Docker host identifiers stay collision-safe in mixed environments.
## Installation
- **Install or upgrade with the helper script**
```bash
curl -sL https://github.com/rcourtman/Pulse/releases/latest/download/install.sh | bash
```
- **Binary upgrade on systemd hosts**
```bash
sudo systemctl stop pulse
curl -fsSL https://github.com/rcourtman/Pulse/releases/download/v4.26.1/pulse-v4.26.1-linux-amd64.tar.gz \
| sudo tar -xz -C /opt/pulse --strip-components=1
sudo systemctl start pulse
```
- **Docker update**
```bash
docker pull rcourtman/pulse:v4.26.1
docker stop pulse || true
docker rm pulse || true
docker run -d --name pulse --restart unless-stopped -p 7655:7655 rcourtman/pulse:v4.26.1
```
- **Helm upgrade**
```bash
helm upgrade --install pulse oci://ghcr.io/rcourtman/pulse-chart \
--version 4.26.1 \
--namespace pulse --create-namespace
```
## Downloads
- Multi-arch Linux tarballs (amd64/arm64/armv7)
- Standalone sensor proxy binaries
- Helm chart archive (pulse-4.26.1-helm.tgz)
- SHA256 checksums (checksums.txt)
- Docker tags: rcourtman/pulse:v4.26.1, :4.26, :4, :latest

View file

@ -1,8 +1,8 @@
# 🛡️ Sensor Proxy Hardening # 🛡️ Sensor Proxy AppArmor (Optional)
> **⚠️ Deprecated:** The sensor-proxy is deprecated in favor of the unified Pulse agent. > **Deprecated in v5:** `pulse-sensor-proxy` is deprecated and not recommended for new deployments.
> For new installations, use `install.sh --enable-proxmox` instead. > Use `pulse-agent --enable-proxmox` for temperature monitoring.
> See [TEMPERATURE_MONITORING.md](/docs/security/TEMPERATURE_MONITORING.md). > This document is retained for existing installations during the migration window.
Secure `pulse-sensor-proxy` with AppArmor and Seccomp. Secure `pulse-sensor-proxy` with AppArmor and Seccomp.
@ -14,7 +14,8 @@ Profile: `security/apparmor/pulse-sensor-proxy.apparmor`
### Install & Enforce ### Install & Enforce
```bash ```bash
sudo install -m 0644 security/apparmor/pulse-sensor-proxy.apparmor /etc/apparmor.d/pulse-sensor-proxy curl -fsSL https://raw.githubusercontent.com/rcourtman/Pulse/main/security/apparmor/pulse-sensor-proxy.apparmor | \
sudo tee /etc/apparmor.d/pulse-sensor-proxy >/dev/null
sudo apparmor_parser -r /etc/apparmor.d/pulse-sensor-proxy sudo apparmor_parser -r /etc/apparmor.d/pulse-sensor-proxy
sudo aa-enforce pulse-sensor-proxy sudo aa-enforce pulse-sensor-proxy
``` ```
@ -36,7 +37,10 @@ SystemCallAllow=accept;connect;recvfrom;sendto;recvmsg;sendmsg;sendmmsg;getsockn
### Containers (Docker/Podman) ### Containers (Docker/Podman)
```bash ```bash
podman run --seccomp-profile /opt/pulse/security/seccomp/pulse-sensor-proxy.json ... curl -fsSL https://raw.githubusercontent.com/rcourtman/Pulse/main/security/seccomp/pulse-sensor-proxy.json | \
sudo tee /etc/pulse-sensor-proxy.seccomp.json >/dev/null
podman run --seccomp-profile /etc/pulse-sensor-proxy.seccomp.json ...
``` ```
## 🔍 Verification ## 🔍 Verification

View file

@ -1,8 +1,8 @@
# 🛡️ Sensor Proxy Hardening # 🛡️ Sensor Proxy Hardening
> **⚠️ Deprecated:** The sensor-proxy is deprecated in favor of the unified Pulse agent. > **Deprecated in v5:** `pulse-sensor-proxy` is deprecated and not recommended for new deployments.
> For new installations, use `install.sh --enable-proxmox` instead. > Use `pulse-agent --enable-proxmox` for temperature monitoring.
> See [TEMPERATURE_MONITORING.md](/docs/security/TEMPERATURE_MONITORING.md). > This document is retained for existing installations during the migration window.
The `pulse-sensor-proxy` runs on the host to securely collect temperatures, keeping SSH keys out of containers. The `pulse-sensor-proxy` runs on the host to securely collect temperatures, keeping SSH keys out of containers.
@ -48,7 +48,7 @@ SSH keys are restricted to `sensors -j` only.
**Rotation**: **Rotation**:
```bash ```bash
/opt/pulse/scripts/pulse-sensor-proxy-rotate-keys.sh bash -c "$(curl -fsSL https://raw.githubusercontent.com/rcourtman/Pulse/main/scripts/pulse-proxy-rotate-keys.sh)"
``` ```
* **Dry Run**: Add `--dry-run`. * **Dry Run**: Add `--dry-run`.
* **Rollback**: Add `--rollback`. * **Rollback**: Add `--rollback`.
@ -56,6 +56,9 @@ SSH keys are restricted to `sensors -j` only.
## 🚨 Incident Response ## 🚨 Incident Response
If compromised: If compromised:
1. **Stop Proxy**: `systemctl stop pulse-sensor-proxy`. 1. **Stop Proxy**: `systemctl stop pulse-sensor-proxy`.
2. **Rotate Keys**: Remove old keys from nodes manually or use `pulse-sensor-proxy-rotate-keys.sh`. 2. **Rotate Keys**: Remove old keys from nodes manually or use the rotation helper above.
3. **Audit Logs**: Check `journalctl -u pulse-sensor-proxy`. 3. **Audit Logs**: Check `journalctl -u pulse-sensor-proxy`.
4. **Reinstall**: Run `/opt/pulse/scripts/install-sensor-proxy.sh`. 4. **Reinstall**:
```bash
curl -fsSL https://github.com/rcourtman/Pulse/releases/latest/download/install-sensor-proxy.sh | sudo bash
```

View file

@ -1,8 +1,8 @@
# 🌐 Sensor Proxy Network Segmentation # 🌐 Sensor Proxy Network Segmentation
> **⚠️ Deprecated:** The sensor-proxy is deprecated in favor of the unified Pulse agent. > **Deprecated in v5:** `pulse-sensor-proxy` is deprecated and not recommended for new deployments.
> For new installations, use `install.sh --enable-proxmox` instead. > Use `pulse-agent --enable-proxmox` for temperature monitoring.
> See [TEMPERATURE_MONITORING.md](/docs/security/TEMPERATURE_MONITORING.md). > This document is retained for existing installations during the migration window.
Isolate the proxy to prevent lateral movement. Isolate the proxy to prevent lateral movement.

View file

@ -1,13 +1,16 @@
# 🌡️ Temperature Monitoring # 🌡️ Temperature Monitoring
Pulse supports two methods for collecting hardware temperatures from Proxmox nodes. This page describes the recommended v5 approach for temperature monitoring and the security tradeoffs between approaches.
For the full sensor-proxy setup guide (socket mounts, HTTP mode, troubleshooting), see:
`docs/TEMPERATURE_MONITORING.md`.
## Recommended: Pulse Agent ## Recommended: Pulse Agent
The simplest and most feature-rich method is installing the Pulse agent on your Proxmox nodes: The simplest and most feature-rich method is installing the Pulse agent on your Proxmox nodes:
```bash ```bash
curl -fsSL http://your-pulse-server:7655/api/download/install.sh | bash -s -- \ curl -fsSL http://your-pulse-server:7655/install.sh | bash -s -- \
--url http://your-pulse-server:7655 \ --url http://your-pulse-server:7655 \
--token YOUR_TOKEN \ --token YOUR_TOKEN \
--enable-proxmox --enable-proxmox
@ -15,20 +18,16 @@ curl -fsSL http://your-pulse-server:7655/api/download/install.sh | bash -s -- \
**Benefits:** **Benefits:**
- ✅ One-command setup - ✅ One-command setup
- ✅ Automatic API token creation
- ✅ Temperature monitoring built-in - ✅ Temperature monitoring built-in
- ✅ Enables AI features for VM/container management
- ✅ No SSH keys or proxy configuration required - ✅ No SSH keys or proxy configuration required
The agent runs `sensors -j` locally and reports temperatures directly to Pulse. The agent runs `sensors -j` locally and reports temperatures directly to Pulse.
--- ---
## Legacy: Sensor Proxy (SSH-based) ## Deprecated: Sensor Proxy (Host Service)
For users who prefer not to install an agent on their hypervisor, the sensor-proxy method is still available. `pulse-sensor-proxy` is deprecated in v5 and is not recommended for new deployments. This section is retained for existing installations during the migration window.
> **Note:** This method is deprecated and will be removed in a future release. Consider migrating to the agent-based approach.
### 🛡️ Security Model ### 🛡️ Security Model
* **Isolation**: SSH keys live on the host, not in the container. * **Isolation**: SSH keys live on the host, not in the container.
@ -57,3 +56,9 @@ All requests logged to system journal:
journalctl -u pulse-sensor-proxy journalctl -u pulse-sensor-proxy
``` ```
Logs include: `uid`, `pid`, `method`, `node`, `correlation_id`. Logs include: `uid`, `pid`, `method`, `node`, `correlation_id`.
### Related Docs
- Sensor proxy hardening: `docs/security/SENSOR_PROXY_HARDENING.md`
- Network segmentation: `docs/security/SENSOR_PROXY_NETWORK.md`
- AppArmor/Seccomp: `docs/security/SENSOR_PROXY_APPARMOR.md`

View file

@ -6,7 +6,6 @@ import { copyToClipboard } from '@/utils/clipboard';
import { showSuccess, showError } from '@/utils/toast'; import { showSuccess, showError } from '@/utils/toast';
import { getPulseBaseUrl } from '@/utils/url'; import { getPulseBaseUrl } from '@/utils/url';
import { NodesAPI } from '@/api/nodes'; import { NodesAPI } from '@/api/nodes';
import { apiFetchJSON } from '@/utils/apiClient';
import { SectionHeader } from '@/components/shared/SectionHeader'; import { SectionHeader } from '@/components/shared/SectionHeader';
import { import {
formField, formField,
@ -40,12 +39,6 @@ type TemperatureTransportDetail = {
disable?: boolean; disable?: boolean;
}; };
interface ProxyInstallResponse {
command: string;
pulseURL: string;
node?: string;
}
const deriveNameFromHost = (host: string): string => { const deriveNameFromHost = (host: string): string => {
let value = host.trim(); let value = host.trim();
if (!value) { if (!value) {
@ -98,9 +91,6 @@ export const NodeModal: Component<NodeModalProps> = (props) => {
const [quickSetupCommand, setQuickSetupCommand] = createSignal(''); const [quickSetupCommand, setQuickSetupCommand] = createSignal('');
const [quickSetupToken, setQuickSetupToken] = createSignal(''); const [quickSetupToken, setQuickSetupToken] = createSignal('');
const [quickSetupExpiry, setQuickSetupExpiry] = createSignal<number | null>(null); const [quickSetupExpiry, setQuickSetupExpiry] = createSignal<number | null>(null);
const [proxyInstallCommand, setProxyInstallCommand] = createSignal('');
const [loadingProxyCommand, setLoadingProxyCommand] = createSignal(false);
const [proxyCommandError, setProxyCommandError] = createSignal<string | null>(null);
const [agentInstallCommand, setAgentInstallCommand] = createSignal(''); const [agentInstallCommand, setAgentInstallCommand] = createSignal('');
const [loadingAgentCommand, setLoadingAgentCommand] = createSignal(false); const [loadingAgentCommand, setLoadingAgentCommand] = createSignal(false);
const [agentCommandError, setAgentCommandError] = createSignal<string | null>(null); const [agentCommandError, setAgentCommandError] = createSignal<string | null>(null);
@ -164,36 +154,6 @@ export const NodeModal: Component<NodeModalProps> = (props) => {
} }
return undefined; return undefined;
}; };
const shouldOfferProxyCommand = () =>
props.nodeType === 'pve' && Boolean(props.editingNode?.id) && Boolean(temperatureTransportDetail()?.disable);
const fetchProxyInstallCommand = async () => {
if (loadingProxyCommand()) {
return;
}
setLoadingProxyCommand(true);
setProxyCommandError(null);
setProxyInstallCommand('');
try {
const nodeName = props.editingNode?.name ? encodeURIComponent(props.editingNode!.name) : '';
const query = nodeName ? `?node=${nodeName}` : '';
const response = (await apiFetchJSON(
`/api/temperature-proxy/install-command${query}`,
)) as ProxyInstallResponse;
if (!response || typeof response.command !== 'string') {
throw new Error('Proxy installer command unavailable');
}
setProxyInstallCommand(response.command);
showSuccess('HTTPS proxy command ready', undefined, 2000);
} catch (error) {
const message =
error instanceof Error ? error.message : 'Failed to generate HTTPS proxy command';
setProxyCommandError(message);
showError(message);
logger.error('Failed to load proxy install command', error);
} finally {
setLoadingProxyCommand(false);
}
};
const quickSetupExpiryLabel = () => { const quickSetupExpiryLabel = () => {
const expiry = quickSetupExpiry(); const expiry = quickSetupExpiry();
if (!expiry) { if (!expiry) {
@ -2105,56 +2065,6 @@ export const NodeModal: Component<NodeModalProps> = (props) => {
{temperatureTransportDetail()?.message} {temperatureTransportDetail()?.message}
</p> </p>
</Show> </Show>
<Show when={shouldOfferProxyCommand()}>
<div class="mt-3 rounded border border-blue-200 bg-blue-50 p-2 text-xs text-blue-800 dark:border-blue-700 dark:bg-blue-900/20 dark:text-blue-100 space-y-2">
<div class="font-semibold">Temperature proxy (legacy)</div>
<div class="text-amber-700 dark:text-amber-300 text-[11px]">
Recommended: Install the Pulse agent instead (Settings Agents) for temperatures + AI features.
</div>
<div>Or generate a one-line installer command for the standalone temperature proxy:</div>
<div class="flex flex-wrap gap-2">
<button
type="button"
class="rounded bg-blue-600 px-2 py-1 text-white hover:bg-blue-700 disabled:opacity-70"
onClick={() => void fetchProxyInstallCommand()}
disabled={loadingProxyCommand()}
>
{loadingProxyCommand() ? 'Generating…' : 'Generate command'}
</button>
<a
href="/api/install/install-sensor-proxy.sh"
target="_blank"
rel="noreferrer"
class="rounded border border-blue-400 px-2 py-1 text-blue-700 hover:bg-blue-100 dark:text-blue-200 dark:border-blue-300 dark:hover:bg-blue-900/40"
>
Download installer script
</a>
</div>
<Show when={proxyCommandError()}>
<p class="text-xs text-red-600 dark:text-red-300">
{proxyCommandError()}
</p>
</Show>
<Show when={proxyInstallCommand()}>
<pre class="overflow-x-auto rounded bg-white/70 px-2 py-1 font-mono text-[0.65rem] text-gray-800 dark:bg-gray-900/40 dark:text-gray-200">
{proxyInstallCommand()}
</pre>
<button
type="button"
class="rounded bg-blue-600 px-2 py-1 text-white hover:bg-blue-700"
onClick={() => {
const command = proxyInstallCommand();
if (command) {
void copyToClipboard(command);
showSuccess('Proxy installer command copied');
}
}}
>
Copy command
</button>
</Show>
</div>
</Show>
<Show when={!temperatureMonitoringEnabledValue()}> <Show when={!temperatureMonitoringEnabledValue()}>
<p class="mt-3 rounded border border-blue-200 bg-blue-50 p-2 text-xs text-blue-700 dark:border-blue-700 dark:bg-blue-900/20 dark:text-blue-200"> <p class="mt-3 rounded border border-blue-200 bg-blue-50 p-2 text-xs text-blue-700 dark:border-blue-700 dark:bg-blue-900/20 dark:text-blue-200">
Pulse will skip SSH temperature polling for this node. Existing dashboard readings will stop refreshing. Pulse will skip SSH temperature polling for this node. Existing dashboard readings will stop refreshing.

View file

@ -4738,6 +4738,12 @@ func (r *Router) handleDownloadInstallerScript(w http.ResponseWriter, req *http.
return return
} }
log.Warn().
Str("path", req.URL.Path).
Str("remote", req.RemoteAddr).
Msg("Deprecated pulse-sensor-proxy installer requested - use pulse-agent --enable-proxmox instead")
w.Header().Set("Warning", `299 - "pulse-sensor-proxy is deprecated in v5; use pulse-agent --enable-proxmox"`)
// Try pre-built location first (in container) // Try pre-built location first (in container)
scriptPath := "/opt/pulse/scripts/install-sensor-proxy.sh" scriptPath := "/opt/pulse/scripts/install-sensor-proxy.sh"
content, err := os.ReadFile(scriptPath) content, err := os.ReadFile(scriptPath)
@ -4842,6 +4848,12 @@ func (r *Router) handleTemperatureProxyInstallCommand(w http.ResponseWriter, req
return return
} }
log.Warn().
Str("path", req.URL.Path).
Str("remote", req.RemoteAddr).
Msg("Deprecated sensor-proxy install command requested - use pulse-agent --enable-proxmox instead")
w.Header().Set("Warning", `299 - "pulse-sensor-proxy is deprecated in v5; use pulse-agent --enable-proxmox"`)
baseURL := strings.TrimSpace(r.resolvePublicURL(req)) baseURL := strings.TrimSpace(r.resolvePublicURL(req))
if baseURL == "" { if baseURL == "" {
http.Error(w, "Pulse public URL is not configured", http.StatusBadRequest) http.Error(w, "Pulse public URL is not configured", http.StatusBadRequest)

View file

@ -44,10 +44,11 @@ type Config struct {
KubeContext string KubeContext string
// Report shaping // Report shaping
IncludeNamespaces []string IncludeNamespaces []string
ExcludeNamespaces []string ExcludeNamespaces []string
IncludeAllPods bool // Include all non-succeeded pods (still capped) IncludeAllPods bool // Include all non-succeeded pods (still capped)
MaxPods int // Max pods included in the report IncludeAllDeployments bool // Include all deployments, not just problem ones
MaxPods int // Max pods included in the report
} }
type Agent struct { type Agent struct {
@ -600,7 +601,7 @@ func (a *Agent) collectDeployments(ctx context.Context) ([]agentsk8s.Deployment,
if !a.namespaceAllowed(dep.Namespace) { if !a.namespaceAllowed(dep.Namespace) {
continue continue
} }
if !isProblemDeployment(dep) { if !a.cfg.IncludeAllDeployments && !isProblemDeployment(dep) {
continue continue
} }

View file

@ -183,6 +183,39 @@ func TestCollectDeployments_FiltersProblems(t *testing.T) {
} }
} }
func TestCollectDeployments_IncludeAllDeployments(t *testing.T) {
replicas := int32(3)
okReplicas := int32(2)
clientset := fake.NewSimpleClientset(
&appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{Namespace: "a", Name: "bad"},
Spec: appsv1.DeploymentSpec{Replicas: &replicas},
Status: appsv1.DeploymentStatus{AvailableReplicas: 2, ReadyReplicas: 2, UpdatedReplicas: 2},
},
&appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{Namespace: "a", Name: "ok"},
Spec: appsv1.DeploymentSpec{Replicas: &okReplicas},
Status: appsv1.DeploymentStatus{AvailableReplicas: 2, ReadyReplicas: 2, UpdatedReplicas: 2},
},
)
a := &Agent{
cfg: Config{IncludeAllDeployments: true},
kubeClient: clientset,
includeNamespaces: nil,
excludeNamespaces: nil,
}
deps, err := a.collectDeployments(context.Background())
if err != nil {
t.Fatalf("collectDeployments: %v", err)
}
if len(deps) != 2 {
t.Fatalf("expected 2 deployments with IncludeAllDeployments=true, got %d (%+v)", len(deps), deps)
}
}
func TestCollectNodes_MapsReadyRolesAndResources(t *testing.T) { func TestCollectNodes_MapsReadyRolesAndResources(t *testing.T) {
clientset := fake.NewSimpleClientset( clientset := fake.NewSimpleClientset(
&corev1.Node{ &corev1.Node{