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:
parent
bd7d2c22f7
commit
ae70d9e423
48 changed files with 1903 additions and 686 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
66
SECURITY.md
66
SECURITY.md
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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. |
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@
|
||||||
|
|
||||||
  
|
  
|
||||||
|
|
||||||
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>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
133
docs/AI.md
133
docs/AI.md
|
|
@ -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
|
|
||||||
|
|
|
||||||
61
docs/API.md
61
docs/API.md
|
|
@ -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*
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
62
docs/DEPLOYMENT_MODELS.md
Normal 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
|
||||||
|
```
|
||||||
|
|
||||||
|
|
@ -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
317
docs/DOCS_AUDIT_V5.md
Normal 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. It’s 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.
|
||||||
42
docs/FAQ.md
42
docs/FAQ.md
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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 |
|
||||||
|
|
|
||||||
|
|
@ -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**:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
||||||
401
docs/PULSE_PRO_IMPLEMENTATION.md
Normal file
401
docs/PULSE_PRO_IMPLEMENTATION.md
Normal 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?*
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 proxy’s 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 proxy’s 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
|
|
||||||
|
|
|
||||||
375
docs/SECURITY_AUDIT_2025-12-18.md
Normal file
375
docs/SECURITY_AUDIT_2025-12-18.md
Normal 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
|
||||||
|
|
@ -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`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 can’t 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 can’t reach `/etc/pve` or Corosync IPC.
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
66
docs/UPGRADE_v5.md
Normal 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.
|
||||||
|
|
@ -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. |
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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`.
|
||||||
|
|
|
||||||
|
|
@ -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`.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 |
|
||||||
| :--- | :--- | :--- |
|
| :--- | :--- | :--- |
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.
|
|
||||||
|
|
|
||||||
|
|
@ -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).
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
137
docs/releases/RELEASE_NOTES_v4.md
Normal file
137
docs/releases/RELEASE_NOTES_v4.md
Normal 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 proxy’s 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 proxy’s 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
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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`
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue