docs: standardize markdown syntax and remove deprecated sensor-proxy docs

This commit is contained in:
rcourtman
2026-01-20 09:43:49 +00:00
parent 39d647c947
commit ee63d438cc
45 changed files with 228 additions and 512 deletions

View File

@@ -51,9 +51,9 @@ pd # Alias for ./scripts/hot-dev.sh
Or use VS Code task: `Cmd+Shift+P` → "Tasks: Run Task" → "Start Pulse Dev Server"
**Access the app:**
- Frontend: http://localhost:7655
- Backend API: http://localhost:7656
- Metrics: http://localhost:9091
- Frontend: <http://localhost:7655>
- Backend API: <http://localhost:7656>
- Metrics: <http://localhost:9091>
## Development Workflows
@@ -66,7 +66,7 @@ Or use VS Code task: `Cmd+Shift+P` → "Tasks: Run Task" → "Start Pulse Dev Se
**Backend (3-5 seconds):**
- Edit any `.go` file
- Save → Terminal shows:
```
```text
🔄 Change detected: yourfile.go
Rebuilding backend...
✓ Build successful, restarting backend...
@@ -241,7 +241,7 @@ Custom overrides: Create `.env.devcontainer` (gitignored)
## Architecture
```
```text
MacBook (VS Code)
↓ Remote-SSH
dev-containers VM (Proxmox)

View File

@@ -20,10 +20,10 @@ Steps to reproduce the behavior:
What you expected to happen.
**Environment:**
- Pulse Version: [e.g. v4.15.0]
- Installation Type: [e.g. ProxmoxVE LXC, Docker, Manual]
- Pulse Version: [e.g. v4.15.0]
- Installation Type: [e.g. ProxmoxVE LXC, Docker, Manual]
**Additional context**
Add any other context, screenshots, or logs here.
💡 **Tip:** If you're experiencing connection issues, API errors, or missing data, you can attach diagnostics from Settings → Diagnostics tab → Export for GitHub (sanitized version)
💡 **Tip:** If you're experiencing connection issues, API errors, or missing data, you can attach diagnostics from Settings → Diagnostics tab → Export for GitHub (sanitized version)

View File

@@ -17,4 +17,4 @@ A clear and concise description of what you want to happen.
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
Add any other context or screenshots about the feature request here.

10
.markdownlint-cli2.jsonc Normal file
View File

@@ -0,0 +1,10 @@
{
"config": ".markdownlint.json",
"globs": [
"**/*.md"
],
"ignores": [
"**/node_modules/**",
".agent/workflows/hot-dev.md"
]
}

25
.markdownlint.json Normal file
View File

@@ -0,0 +1,25 @@
{
"MD013": false,
"MD029": {
"style": "ordered"
},
"MD022": false,
"MD030": false,
"MD031": false,
"MD032": false,
"MD060": false,
"MD033": {
"allowed_elements": [
"br",
"details",
"div",
"h1",
"h2",
"h3",
"img",
"p",
"summary",
"strong"
]
}
}

View File

@@ -75,9 +75,7 @@ The frontend is a modern Single Page Application (SPA) built with **SolidJS** an
* **Vite**: For fast development and optimized builds.
### State Management
* **Stores (`frontend-modern/src/stores`)**:
* `websocket.ts`: The central nervous system. It maintains the WS connection, handles reconnection logic, and updates reactive signals when new data arrives.
* `metricsHistory.ts`: Buffers incoming metrics to drive historical charts (Sparklines) without needing a time-series database backend.
* **Stores (`frontend-modern/src/stores`)**: `websocket.ts` manages the WS connection and reactive updates; `metricsHistory.ts` buffers metrics for sparklines.
### Component Design
* **Atomic Design**: Small, reusable components (`MetricBar`, `StatusBadge`) compose into larger views (`NodeSummaryTable`).

View File

@@ -43,6 +43,14 @@ cd ..
npm run mock:on # Optional: enable mock data
```
Backend-only hot reload (requires `air`):
```bash
air -c .air.toml
```
Set `HOT_DEV_USE_PRO=true` to build the Pro variant when available.
Mock mode is supported for development, but the internal developer notes are not shipped in this repository.
---

View File

@@ -1,6 +1,7 @@
# Pulse
<div align="center">
<img src="docs/images/pulse-logo.svg" alt="Pulse Logo" width="120" />
<h1>Pulse</h1>
<p><strong>Real-time monitoring for Proxmox, Docker, and Kubernetes infrastructure.</strong></p>
[![GitHub Stars](https://img.shields.io/github/stars/rcourtman/Pulse?style=flat&logo=github)](https://github.com/rcourtman/Pulse)

View File

@@ -70,7 +70,7 @@ docker exec pulse rm -rf /home/pulse/.ssh/id_ed25519*
#### Security Boundary
```
```text
┌─────────────────────────────────────┐
│ Proxmox Host │
│ ┌───────────────────────────────┐ │
@@ -112,9 +112,9 @@ Verify temperature collection is agent-based:
journalctl -u pulse-agent -n 200 --no-pager
```
**Documentation:** https://github.com/rcourtman/Pulse/blob/main/SECURITY.md#critical-security-notice-for-container-deployments
**Issues:** https://github.com/rcourtman/pulse/issues
**Private disclosures:** security@pulseapp.io
**Documentation:** <https://github.com/rcourtman/Pulse/blob/main/SECURITY.md#critical-security-notice-for-container-deployments>
**Issues:** <https://github.com/rcourtman/pulse/issues>
**Private disclosures:** <security@pulseapp.io>
---
@@ -239,7 +239,7 @@ docker run -e ALLOW_UNPROTECTED_EXPORT=true rcourtman/pulse:latest
**Note:** for production, prefer Docker secrets or systemd environment files
for sensitive data.
## Security Features
## Security Features Summary
### Core Protection
- **Encryption**: credentials encrypted at rest (AES-256-GCM)

View File

@@ -150,10 +150,10 @@ all HTTP access attempts to the audit log.
- Format: JSON with hash chaining (`prev_hash`, `event_hash`, `seq`)
- Access: Owned by `pulse-sensor-proxy`, `0640`, `chattr +a`
Follow `docs/operations/AUDIT_LOG_ROTATION.md` for rotation (remove `+a`,
Follow standard log rotation practices (e.g., `logrotate`) for rotation (remove `+a`,
truncate, restart service, reapply `+a`). Also consider forwarding with
`scripts/setup-log-forwarding.sh`; see
`docs/operations/SENSOR_PROXY_LOGS.md` for log forwarding and verification
See standard rsyslog practices for log forwarding and verification.
steps.
## Metrics & Monitoring
@@ -234,5 +234,5 @@ If you suspect config corruption (service won't start, temperatures stopped):
sudo systemctl start pulse-sensor-proxy
```
For additional hardening steps, read `docs/security/SENSOR_PROXY_HARDENING.md` and
For additional hardening steps, read the main `docs/security/TEMPERATURE_MONITORING.md` guide and
`docs/security/TEMPERATURE_MONITORING.md`.

View File

@@ -1,6 +1,6 @@
# Pulse AI
Pulse Pro unlocks **AI Patrol** for continuous, automated health checks. Learn more at https://pulserelay.pro or see the technical overview in [PULSE_PRO.md](PULSE_PRO.md).
Pulse Pro unlocks **AI Patrol** for continuous, automated health checks. Learn more at <https://pulserelay.pro> or see the technical overview in [PULSE_PRO.md](PULSE_PRO.md).
## What Patrol Actually Does (Technical)

View File

@@ -8,18 +8,18 @@ Pulse provides a comprehensive REST API for automation and integration.
Most API requests require authentication via one of the following methods:
**1. API Token (Recommended)**
### API Token (Recommended)
Pass the token in the `X-API-Token` header.
```bash
curl -H "X-API-Token: your-token" http://localhost:7655/api/health
```
**2. Bearer Token**
### Bearer Token
```bash
curl -H "Authorization: Bearer your-token" http://localhost:7655/api/health
```
**3. Session Cookie**
### Session Cookie
Standard browser session cookie (used by the UI).
Public endpoints include:
@@ -54,6 +54,18 @@ Returns the complete state of your infrastructure (Nodes, VMs, Containers, Stora
### Version Info
`GET /api/version`
Returns version, build time, and update status.
Example response:
```json
{
"version": "5.0.16",
"buildTime": "2026-01-19T22:20:18Z",
"channel": "stable",
"deploymentType": "systemd",
"updateAvailable": true,
"latestVersion": "5.0.17"
}
```
Version fields are returned as plain semantic versions (no leading `v`).
---
@@ -312,7 +324,7 @@ Returns scheduler health, DLQ, and breaker status. Requires `monitoring:read`.
- `POST /api/updates/apply`
- `GET /api/updates/status`
- `GET /api/updates/stream`
- `GET /api/updates/plan?version=vX.Y.Z` (optional `channel`)
- `GET /api/updates/plan?version=X.Y.Z` (optional `channel`, accepts `v` prefix)
- `GET /api/updates/history`
- `GET /api/updates/history/entry?id=<event_id>`

View File

@@ -3,7 +3,7 @@
Pulse uses a split-configuration model to ensure security and flexibility.
| File | Purpose | Security Level |
|------|---------|----------------|
| ------ | --------- | ---------------- |
| `.env` | Authentication & Secrets | 🔒 **Critical** (Read-only by owner) |
| `.encryption.key` | Encryption key for `.enc` files | 🔒 **Critical** |
| `system.json` | General Settings | 📝 Standard |
@@ -36,6 +36,8 @@ Pulse uses a split-configuration model to ensure security and flexibility.
| `ai_remediations.json` | AI remediation suggestions | 📝 Standard |
| `ai_incidents.json` | AI incident tracking | 📝 Standard |
Guest metadata entries are keyed by the canonical guest ID format `instance:node:vmid` (for example, `pve1:node1:100`). Legacy dash-separated keys are migrated automatically.
All files are located in `/etc/pulse/` (Systemd) or `/data/` (Docker/Kubernetes) by default.
Path overrides:
@@ -85,7 +87,7 @@ See [OIDC Documentation](OIDC.md) and [Proxy Auth](PROXY_AUTH.md) for details.
Environment overrides (lock the corresponding UI fields):
| Variable | Description |
|----------|-------------|
| ---------- | ------------- |
| `OIDC_ENABLED` | Enable OIDC (`true`/`false`) |
| `OIDC_ISSUER_URL` | Issuer URL from your IdP |
| `OIDC_CLIENT_ID` | Client ID |
@@ -101,6 +103,7 @@ Environment overrides (lock the corresponding UI fields):
| `OIDC_ALLOWED_EMAILS` | Allowed emails (space or comma-separated) |
| `OIDC_GROUP_ROLE_MAPPINGS` | Group-to-role mappings (Pro). Format: `group1=role1,group2=role2` |
| `OIDC_CA_BUNDLE` | Custom CA bundle path |
</details>
> **Note**: `API_TOKEN` / `API_TOKENS` are legacy and will be migrated into `api_tokens.json` on startup.
@@ -138,7 +141,7 @@ Controls runtime behavior like ports, logging, and polling intervals. Most of th
Environment variables take precedence over `system.json`.
| Variable | Description | Default |
|----------|-------------|---------|
| ---------- | ------------- | --------- |
| `FRONTEND_PORT` | Public listening port | `7655` |
| `PORT` | Legacy alias for `FRONTEND_PORT` | *(unset)* |
| `BACKEND_HOST` | Bind host for the HTTP server and metrics listener (advanced) | *(unset)* |
@@ -149,7 +152,7 @@ Environment variables take precedence over `system.json`.
#### Log Levels
| Level | Description |
|-------|-------------|
| ------- | ------------- |
| `error` | Only errors and critical issues |
| `warn` | Errors + warnings (recommended for minimal logging) |
| `info` | Standard operational messages (startup, connections, alerts) |
@@ -158,7 +161,7 @@ Environment variables take precedence over `system.json`.
> **Tip**: If your syslog is being flooded with Pulse messages, set `LOG_LEVEL=warn` to significantly reduce log volume while still capturing important events.
| Variable | Description | Default |
|----------|-------------|---------|
| ---------- | ------------- | --------- |
| `PULSE_PUBLIC_URL` | URL for UI links, notifications, and OIDC. For reverse proxies, keep this as the public URL and use `PULSE_AGENT_CONNECT_URL` for agent installs if you need a direct/internal address. | Auto-detected |
| `PULSE_AGENT_CONNECT_URL` | Dedicated direct URL for agents (overrides `PULSE_PUBLIC_URL` for agent install commands). Alias: `PULSE_AGENT_URL`. | *(unset)* |
| `PULSE_AGENT_CONFIG_SIGNING_KEY` | Base64 Ed25519 private key used to sign remote agent config payloads. | *(unset)* |
@@ -195,7 +198,7 @@ When `allowEmbedding` is `false`, Pulse sends `X-Frame-Options: DENY` and `frame
### Monitoring Overrides
| Variable | Description | Default |
|----------|-------------|---------|
| ---------- | ------------- | --------- |
| `PVE_POLLING_INTERVAL` | PVE metrics polling frequency | `10s` |
| `PBS_POLLING_INTERVAL` | PBS metrics polling frequency | `60s` |
| `PMG_POLLING_INTERVAL` | PMG metrics polling frequency | `60s` |
@@ -222,7 +225,7 @@ When `allowEmbedding` is `false`, Pulse sends `X-Frame-Options: DENY` and `frame
### Logging Overrides
| Variable | Description | Default |
|----------|-------------|---------|
| ---------- | ------------- | --------- |
| `LOG_FILE` | Log file path (empty = stdout) | *(unset)* |
| `LOG_MAX_SIZE` | Log file max size (MB) | `100` |
| `LOG_MAX_AGE` | Log file retention (days) | `30` |
@@ -233,7 +236,7 @@ When `allowEmbedding` is `false`, Pulse sends `X-Frame-Options: DENY` and `frame
These are stored in `system.json` and managed via the UI.
| Key | Description | Default |
|-----|-------------|---------|
| ----- | ------------- | --------- |
| `updateChannel` | Update channel (`stable` or `rc`) | `stable` |
| `autoUpdateEnabled` | Allow one-click updates | `false` |
| `autoUpdateCheckInterval` | Stored UI preference (server currently checks hourly) | `24` |
@@ -244,7 +247,7 @@ These are stored in `system.json` and managed via the UI.
You can auto-import an encrypted backup on first startup. This is useful for automated provisioning and test environments.
| Variable | Description |
|----------|-------------|
| ---------- | ------------- |
| `PULSE_INIT_CONFIG_DATA` | Base64 or raw contents of an export bundle (auto-imports on first start) |
| `PULSE_INIT_CONFIG_FILE` | Path to an export bundle on disk (auto-imports on first start) |
| `PULSE_INIT_CONFIG_PASSPHRASE` | Passphrase for the export bundle (required) |
@@ -256,7 +259,7 @@ You can auto-import an encrypted backup on first startup. This is useful for aut
These are primarily for development or test harnesses and should not be used in production.
| Variable | Description | Default |
|----------|-------------|---------|
| ---------- | ------------- | --------- |
| `PULSE_UPDATE_SERVER` | Override update server base URL (testing only) | *(unset)* |
| `PULSE_UPDATE_STAGE_DELAY_MS` | Adds artificial delays between update stages (testing only) | *(unset)* |
| `PULSE_ALLOW_DOCKER_UPDATES` | Expose update UI/actions in Docker (debug only) | `false` |
@@ -339,7 +342,7 @@ API tokens provide scoped, revocable access to Pulse. Manage tokens in **Setting
### Token Scopes
| Scope | Description |
|-------|-------------|
| ------- | ------------- |
| `*` (Full access) | All permissions (legacy, not recommended) |
| `monitoring:read` | View dashboards, metrics, alerts |
| `monitoring:write` | Acknowledge/silence alerts |
@@ -356,7 +359,7 @@ API tokens provide scoped, revocable access to Pulse. Manage tokens in **Setting
The UI offers quick presets for common use cases:
| Preset | Scopes | Use Case |
|--------|--------|----------|
| -------- | -------- | ---------- |
| **Kiosk / Dashboard** | `monitoring:read` | Read-only dashboard displays |
| **Host agent** | `host-agent:report` | Host agent authentication |
| **Container report** | `docker:report` | Container agent (read-only) |
@@ -372,7 +375,7 @@ For unattended displays (wall monitors, dashboards), use a kiosk token to avoid
2. Click **New token** and select the **Kiosk / Dashboard** preset
3. Copy the generated token
4. Access Pulse via URL with token:
```
```text
https://your-pulse-url/?token=YOUR_TOKEN_HERE
```

View File

@@ -65,7 +65,6 @@ Pull a new image and restart:
docker pull rcourtman/pulse:latest
docker compose up -d
```
### Kubernetes (Helm)
Upgrade the chart:
@@ -74,4 +73,3 @@ Upgrade the chart:
helm repo update
helm upgrade pulse pulse/pulse -n pulse
```

View File

@@ -42,7 +42,7 @@ If a setting is disabled with an amber warning, it's being overridden by an envi
Pulse Pro unlocks **AI Patrol** — scheduled, cross-system analysis that correlates real-time state, recent metrics history, and diagnostics to surface actionable findings.
Example output includes trend-based capacity warnings, backup regressions, Kubernetes AI cluster analysis, and correlated container failures that simple threshold alerts miss.
See [AI Patrol](AI.md), [Pulse Pro technical overview](PULSE_PRO.md), and https://pulserelay.pro.
See [AI Patrol](AI.md), [Pulse Pro technical overview](PULSE_PRO.md), and <https://pulserelay.pro>.
### Why do VMs show "-" for disk usage?
Proxmox API returns `0` for VM disk usage by default. You must install the **QEMU Guest Agent** inside the VM and enable it in Proxmox (VM → Options → QEMU Guest Agent).

View File

@@ -143,6 +143,7 @@ Pulse can self-update to the latest stable version.
**Enable via UI**: Settings → System → Updates
### Manual Update
| Platform | Command |
|----------|---------|
| **Docker** | `docker pull rcourtman/pulse:latest && docker restart pulse` |

View File

@@ -4,9 +4,9 @@ This guide explains how to deploy the Pulse Server (Hub) and Pulse Agents on Kub
## Prerequisites
* A Kubernetes cluster (v1.19+)
* `helm` (v3+) installed locally
* `kubectl` configured to talk to your cluster
- A Kubernetes cluster (v1.19+)
- `helm` (v3+) installed locally
- `kubectl` configured to talk to your cluster
## 1. Deploying the Pulse Server
@@ -42,7 +42,7 @@ helm template pulse pulse/pulse \
--namespace pulse \
--set persistence.enabled=true \
> pulse-server.yaml
```
```text
You can then apply this file:
@@ -126,11 +126,11 @@ Use a token scoped for the agent:
#### Important DaemonSet Configuration
**PULSE_AGENT_ID (Required for DaemonSets)**
##### PULSE_AGENT_ID (Required for DaemonSets)
When running as a DaemonSet, all pods share the same API token but need a unified identity. Without `PULSE_AGENT_ID`, each pod auto-generates a unique ID (e.g., `mac-xxxxx`), causing token conflicts:
```
```text
API token is already in use by agent "mac-aa5496fed726". Each Kubernetes agent must use a unique API token.
```
@@ -141,7 +141,7 @@ Set `PULSE_AGENT_ID` to a shared cluster name so all pods report as one logical
value: "my-k8s-cluster"
```
**Resource Visibility Flags**
##### Resource Visibility Flags
By default, Pulse only shows resources with problems (unhealthy pods, failing deployments). To see all resources:
@@ -241,8 +241,8 @@ roleRef:
Talos Linux is immutable, so you cannot install the agent via the shell script. Use the DaemonSet approach above.
### Agent Configuration for Talos
* **Storage**: Talos mounts the ephemeral OS on `/`. Persistent data is usually in `/var`. The Pulse agent generally doesn't store state, but if it did, ensure it maps to a persistent path.
* **Network**: The agent will report the Pod IP by default. To report the Node IP, set `PULSE_REPORT_IP` using the Downward API:
- **Storage**: Talos mounts the ephemeral OS on `/`. Persistent data is usually in `/var`. The Pulse agent generally doesn't store state, but if it did, ensure it maps to a persistent path.
- **Network**: The agent will report the Pod IP by default. To report the Node IP, set `PULSE_REPORT_IP` using the Downward API:
Add this to the DaemonSet `env` section:
```yaml
@@ -254,6 +254,6 @@ Talos Linux is immutable, so you cannot install the agent via the shell script.
## 4. Troubleshooting
* **Agent not showing in UI**: Check logs for the DaemonSet pods, for example: `kubectl logs -l app=pulse-agent -n pulse`.
* **"Permission Denied" on metrics**: Ensure `securityContext.privileged: true` is set or proper capabilities are added.
* **Connection Refused**: Ensure `PULSE_URL` is correct and reachable from the agent pods.
- **Agent not showing in UI**: Check logs for the DaemonSet pods, for example: `kubectl logs -l app=pulse-agent -n pulse`.
- **"Permission Denied" on metrics**: Ensure `securityContext.privileged: true` is set or proper capabilities are added.
- **Connection Refused**: Ensure `PULSE_URL` is correct and reachable from the agent pods.

View File

@@ -58,8 +58,8 @@ These endpoints require authentication with the `monitoring:read` scope.
`GET /api/metrics-store/history` supports:
- `resourceType` (required): `node`, `guest`, `storage`, `docker`, `dockerHost`
- `resourceId` (required): resource identifier
- `resourceType` (required): `node`, `vm`, `container`, `storage`, `dockerHost`, `dockerContainer`
- `resourceId` (required): resource identifier (for VMs/containers use `instance:node:vmid`)
- `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`)
@@ -67,7 +67,7 @@ Example:
```bash
curl -H "X-API-Token: $TOKEN" \
"http://localhost:7655/api/metrics-store/history?resourceType=guest&resourceId=vm-100&range=7d&metric=cpu"
"http://localhost:7655/api/metrics-store/history?resourceType=vm&resourceId=pve1:node1:100&range=7d&metric=cpu"
```
## Troubleshooting

View File

@@ -5,12 +5,12 @@ Enable Single Sign-On (SSO) with providers like Authentik, Keycloak, Okta, and A
## 🚀 Quick Start
1. **Configure Provider**: Create an OIDC application in your IdP.
* **Redirect URI**: `https://<your-pulse-domain>/api/oidc/callback`
* **Scopes**: `openid`, `profile`, `email`
- **Redirect URI**: `https://<your-pulse-domain>/api/oidc/callback`
- **Scopes**: `openid`, `profile`, `email`
2. **Enable in Pulse**: Go to **Settings → Security → Single Sign-On**.
3. **Enter Details**:
* **Issuer URL**: The base URL of your IdP (e.g., `https://auth.example.com/application/o/pulse/`).
* **Client ID & Secret**: From your IdP.
- **Issuer URL**: The base URL of your IdP (e.g., `https://auth.example.com/application/o/pulse/`).
- **Client ID & Secret**: From your IdP.
4. **Save**: The login page will now show a "Continue with Single Sign-On" button.
> **Tip**: To hide the username/password form and only show the SSO button, set `PULSE_AUTH_HIDE_LOCAL_LOGIN=true` in your environment. You can still access the local login by appending `?show_local=true` to the URL (e.g., `https://your-pulse-instance/?show_local=true`).
@@ -30,9 +30,9 @@ Enable Single Sign-On (SSO) with providers like Authentik, Keycloak, Okta, and A
### Access Control
Restrict access to specific users or groups:
* **Allowed Groups**: Only users in these groups can login. Requires the `groups` scope/claim.
* **Allowed Domains**: Restrict to specific email domains (e.g., `example.com`).
* **Allowed Emails**: Allow specific email addresses.
- **Allowed Groups**: Only users in these groups can login. Requires the `groups` scope/claim.
- **Allowed Domains**: Restrict to specific email domains (e.g., `example.com`).
- **Allowed Emails**: Allow specific email addresses.
### Group-to-Role Mapping (Pro)
@@ -86,21 +86,21 @@ For persistent sessions that don't require frequent re-authentication:
## 📚 Provider Examples
### Authentik
* **Type**: OAuth2/OpenID (Confidential)
* **Redirect URI**: `https://pulse.example.com/api/oidc/callback`
* **Signing Key**: Must use **RS256** (create a certificate/key pair if needed).
* **Issuer URL**: `https://auth.example.com/application/o/pulse/`
- **Type**: OAuth2/OpenID (Confidential)
- **Redirect URI**: `https://pulse.example.com/api/oidc/callback`
- **Signing Key**: Must use **RS256** (create a certificate/key pair if needed).
- **Issuer URL**: `https://auth.example.com/application/o/pulse/`
### Keycloak
* **Client ID**: `pulse`
* **Access Type**: Confidential
* **Valid Redirect URIs**: `https://pulse.example.com/api/oidc/callback`
* **Issuer URL**: `https://keycloak.example.com/realms/myrealm`
- **Client ID**: `pulse`
- **Access Type**: Confidential
- **Valid Redirect URIs**: `https://pulse.example.com/api/oidc/callback`
- **Issuer URL**: `https://keycloak.example.com/realms/myrealm`
### Azure AD
* **Redirect URI**: `https://pulse.example.com/api/oidc/callback` (Web)
* **Issuer URL**: `https://login.microsoftonline.com/<tenant-id>/v2.0`
* **Note**: Enable "ID tokens" in Authentication settings.
- **Redirect URI**: `https://pulse.example.com/api/oidc/callback` (Web)
- **Issuer URL**: `https://login.microsoftonline.com/<tenant-id>/v2.0`
- **Note**: Enable "ID tokens" in Authentication settings.
## 🔧 Troubleshooting

View File

@@ -19,7 +19,7 @@ Authenticate users via your existing reverse proxy (Authentik, Authelia, Cloudfl
| `PROXY_AUTH_SECRET` | **Required**. Shared secret to verify requests. | - |
| `PROXY_AUTH_USER_HEADER` | **Required**. Header containing the username. | - |
| `PROXY_AUTH_ROLE_HEADER` | Header containing user groups/roles. | - |
| `PROXY_AUTH_ROLE_SEPARATOR` | Separator for multiple roles in the header. | `|` |
| `PROXY_AUTH_ROLE_SEPARATOR` | Separator for multiple roles in the header. | `\|` |
| `PROXY_AUTH_ADMIN_ROLE` | Role name that grants admin access. | `admin` |
| `PROXY_AUTH_LOGOUT_URL` | URL to redirect to after logout. | - |

View File

@@ -1,43 +0,0 @@
# 📡 Proxy Control Plane
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.
> **Important**: The control-plane endpoints are disabled by default. Set `PULSE_ENABLE_SENSOR_PROXY=true` on the Pulse server to enable legacy proxy support.
## 🏗️ Architecture
```mermaid
graph LR
Pulse[Pulse Server] -- HTTPS /api/temperature-proxy --> Proxy[Sensor Proxy]
Proxy -- SSH --> Nodes[Cluster Nodes]
```
1. **Registration**: The proxy registers with Pulse on startup/install.
2. **Sync**: The proxy periodically fetches the "Authorized Nodes" list from Pulse.
3. **Validation**: The proxy only executes commands on nodes authorized by Pulse.
## 🔄 Workflow
1. **Install**: `install-sensor-proxy.sh` calls `/api/temperature-proxy/register`.
2. **Token Exchange**: Pulse returns a control-plane token which the proxy saves to `/etc/pulse-sensor-proxy/.pulse-control-token`.
3. **Polling**: The proxy polls `/api/temperature-proxy/authorized-nodes` every 60s (configurable).
4. **Update**: If the node list changes (e.g., a new node is added to Pulse), the proxy updates its internal allowlist automatically.
## ⚙️ Configuration
The proxy configuration in `/etc/pulse-sensor-proxy/config.yaml` handles the sync:
```yaml
pulse_control_plane:
url: https://pulse.example.com:7655
token_file: /etc/pulse-sensor-proxy/.pulse-control-token
refresh_interval: 60
```
## 🛡️ Security
* **Tokens**: The control-plane token is unique per proxy instance.
* **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.

View File

@@ -68,9 +68,6 @@ Pulse Pro unlocks **LLM-backed AI Patrol** — automated background monitoring t
---
<div align="center">
<p>Found a bug or have a suggestion?</p>
<a href="https://github.com/rcourtman/Pulse/issues">
<img src="https://img.shields.io/badge/GitHub-Issues-green" alt="GitHub Issues" />
</a>
</div>
Found a bug or have a suggestion?
[![GitHub Issues](https://img.shields.io/badge/GitHub-Issues-green)](https://github.com/rcourtman/Pulse/issues)

View File

@@ -1,8 +1,7 @@
# Release Notes
Pulse release notes live on GitHub:
https://github.com/rcourtman/Pulse/releases
<https://github.com/rcourtman/Pulse/releases>
For historical v4 notes that previously lived in this repo, see:
`docs/releases/RELEASE_NOTES_v4.md`

View File

@@ -54,7 +54,7 @@ allowed_nodes:
# Require cluster membership validation
strict_node_validation: true
```
```text
**Default Behavior:** If `allowed_nodes` is empty and proxy runs on Proxmox host, automatically validates against cluster membership (secure by default).
@@ -241,7 +241,7 @@ allowed_peers:
#### Enhanced Metrics
New Prometheus metrics for security monitoring:
```
```text
pulse_proxy_node_validation_failures_total{reason}
pulse_proxy_read_timeouts_total
pulse_proxy_write_timeouts_total
@@ -378,7 +378,7 @@ go build ./cmd/pulse-sensor-proxy
### References
- **Temperature Monitoring Overview:** `docs/security/TEMPERATURE_MONITORING.md`
- **Sensor Proxy Hardening:** `docs/security/SENSOR_PROXY_HARDENING.md`
- **Sensor Proxy Hardening:** Standardized security controls for legacy deployments.
---
@@ -396,4 +396,4 @@ All fixes implemented and tested 2025-11-07.
---
**For questions or security concerns, file issues at:** https://github.com/rcourtman/Pulse/issues
**For questions or security concerns, file issues at:** <https://github.com/rcourtman/Pulse/issues>

View File

@@ -13,7 +13,7 @@ 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.
If you use the agent method, the rest of this document (sensor proxy) is optional.
## Migration: pulse-sensor-proxy → pulse-agent
@@ -79,14 +79,14 @@ If running Pulse in Docker, you must install the proxy on the host and share the
```bash
curl -fsSL https://github.com/rcourtman/Pulse/releases/latest/download/install-sensor-proxy.sh | \
sudo bash -s -- --standalone --pulse-server http://<pulse-ip>:7655
```
```text
2. **Update `docker-compose.yml`**:
Add the socket volume to your Pulse service:
```yaml
volumes:
- /mnt/pulse-proxy:/run/pulse-sensor-proxy:ro
```
```text
> **Note**: The standalone installer creates the socket at `/mnt/pulse-proxy` on the host. Map it to `/run/pulse-sensor-proxy` inside the container.
3. **Restart Pulse**: `docker compose up -d`
@@ -103,7 +103,7 @@ If you have Pulse running on **Server A** and want to monitor temperatures on **
Replace `<PULSE_CONTAINER_ID>` with the LXC container ID where Pulse runs on Server A (e.g., `100`).
2. The installer will detect that the container doesn't exist locally and install in **host monitoring only** mode:
```
```text
[WARN] Container 100 does not exist on this node
[WARN] Will install sensor-proxy for host temperature monitoring only
```
@@ -137,15 +137,15 @@ journalctl -u pulse-sensor-proxy -f
1. **Pulse Sensor Proxy**: A lightweight service runs on the Proxmox host.
2. **Secure Access**: It reads sensors (via `lm-sensors`) and exposes them securely.
3. **Transport**:
* **Local**: Uses a Unix socket (`/run/pulse-sensor-proxy`) for zero-latency, secure access.
* **Remote**: Uses mutual TLS over HTTPS (port 8443).
- **Local**: Uses a Unix socket (`/run/pulse-sensor-proxy`) for zero-latency, secure access.
- **Remote**: Uses mutual TLS over HTTPS (port 8443).
4. **No SSH Keys**: Pulse containers no longer need SSH keys to read temperatures.
---
## 🔧 Advanced Configuration
#### Manual Configuration (No Script)
### Manual Configuration (No Script)
If you can't run the installer script, create the configuration manually:
@@ -375,8 +375,7 @@ ls -l /run/pulse-sensor-proxy/pulse-sensor-proxy.sock
journalctl -u pulse-sensor-proxy -f
```
Forward these logs off-host for retention by following
[operations/SENSOR_PROXY_LOGS.md](operations/SENSOR_PROXY_LOGS.md).
Forward these logs off-host for retention by following standard rsyslog/syslog practices.
In the Pulse container, check the logs at startup:
```bash
@@ -730,7 +729,6 @@ pulse-sensor-proxy config set-allowed-nodes --replace --merge 192.168.0.1
- Installer uses CLI (no more shell/Python divergence)
**See also:**
- [Sensor Proxy Config Management Guide](operations/SENSOR_PROXY_CONFIG.md) - Complete runbook
- [Sensor Proxy CLI Reference](../cmd/pulse-sensor-proxy/README.md) - Full command documentation
## Control-Plane Sync & Migration
@@ -786,7 +784,7 @@ If temperature monitoring isn't working:
ssh root@cluster-node "sensors -j"
```
3. **Check GitHub Issues:** https://github.com/rcourtman/Pulse/issues
3. **Check GitHub Issues:** <https://github.com/rcourtman/Pulse/issues>
4. **Include in bug report:**
- Pulse version
- Deployment type (LXC/Docker/native)

View File

@@ -34,56 +34,56 @@ sudo pulse bootstrap-token
### Authentication
**"Invalid username or password" after setup**
#### "Invalid username or password" after setup
- **Docker Compose**: Did you escape the `$` signs in your hash? Use `$$2a$$...`.
- **Truncated Hash**: Ensure your bcrypt hash is exactly 60 characters.
**Cannot login / 401 Unauthorized**
#### Cannot login / 401 Unauthorized
- Clear browser cookies.
- Check if your IP is locked out (wait 15 mins).
- If another admin can log in, use `POST /api/security/reset-lockout` to clear the lockout for your username or IP.
**Audit Log verification shows unsigned events**
#### Audit Log verification shows unsigned events
- **Symptom**: Audit Log entries show “Unsigned” or verification fails in the UI.
- **Root cause**: `PULSE_AUDIT_SIGNING_KEY` is not set, so events are stored without signatures.
- **Fix**: Set `PULSE_AUDIT_SIGNING_KEY` and restart Pulse Pro. Newly created events will be signed; existing unsigned events remain unsigned.
**Audit Log is empty**
#### Audit Log is empty
- **Symptom**: Audit Log shows zero events or "Console Logging Only."
- **Root cause**: OSS build uses console logging only, or Pulse Pro audit logging is not enabled.
- **Fix**: Use Pulse Pro with audit logging enabled, then generate new audit events (logins, token creation, password changes).
**Audit Log verification fails for older events**
#### Audit Log verification fails for older events
- **Symptom**: Older events fail verification while newer events pass.
- **Root cause**: The signing key changed or was rotated, so signatures no longer match.
- **Fix**: Keep `PULSE_AUDIT_SIGNING_KEY` stable. If rotated intentionally, expect older events to fail verification.
### Monitoring Data
**VMs show "-" for disk usage**
#### VMs show "-" for disk usage
- Install **QEMU Guest Agent** in the VM.
- Enable "QEMU Guest Agent" in Proxmox VM Options.
- Restart the VM.
- See [VM Disk Monitoring](VM_DISK_MONITORING.md).
**Temperature data missing**
#### Temperature data missing
- Install `lm-sensors` on the host.
- Run `sensors-detect`.
- Install the unified agent on the Proxmox host with `--enable-proxmox`.
- See [Temperature Monitoring](TEMPERATURE_MONITORING.md).
**Docker hosts appearing/disappearing**
#### Docker hosts appearing/disappearing
- **Duplicate IDs**: Cloned VMs often share `/etc/machine-id`.
- **Fix**: Run `rm /etc/machine-id && systemd-machine-id-setup` on the clone.
### Notifications
**Emails not sending**
#### Emails not sending
- Check SMTP settings in **Alerts → Notification Destinations**.
- Check logs: `docker logs pulse | grep email`.
- Ensure your SMTP provider allows the connection (e.g., Gmail App Passwords).
**Webhooks failing**
#### Webhooks failing
- Verify the URL is reachable from the Pulse server.
- If targeting private IPs, allow them in **Settings → System → Network → Webhook Security**.
- Check Pulse logs for HTTP status codes and response bodies.

View File

@@ -85,7 +85,6 @@ curl -fsSL http://<pulse-ip>:7655/install.sh | \
Legacy env var: `PULSE_KUBE_INCLUDE_ALL_POD_FILES` is still accepted for backward compatibility.
## Auto-Detection
Auto-detection behavior:
@@ -178,7 +177,6 @@ The agent can report S.M.A.R.T. disk temperatures when running in Agent mode. Th
- **Disk exclusions** (`--disk-exclude` / `PULSE_DISK_EXCLUDE`) also apply to S.M.A.R.T. monitoring.
Use patterns like `sda`, `/dev/sdb`, `nvme*`, or `*cache*` to exclude specific block devices.
## Auto-Update
The unified agent automatically checks for updates every hour. When a new version is available:

View File

@@ -17,9 +17,7 @@ Monitor actual disk usage inside your VMs using the QEMU Guest Agent.
## ⚙️ Requirements
* **QEMU Guest Agent**: Must be installed and running inside the VM.
* **Proxmox Permissions**:
* **Proxmox 8**: `VM.Monitor`
* **Proxmox 9+**: `VM.GuestAgent.Audit`
* **Proxmox Permissions**: `VM.Monitor` (Proxmox 8) or `VM.GuestAgent.Audit` (Proxmox 9+).
## 🔧 Troubleshooting

View File

@@ -85,20 +85,9 @@ Returns a real-time snapshot of the adaptive scheduler, including queue state, c
### Instances (`instances`)
The authoritative source for per-instance health.
* **`pollStatus`**:
* `lastSuccess`: Timestamp of last successful poll.
* `lastError`: Details of the last error (message, category).
* `consecutiveFailures`: Current failure streak.
* **`breaker`**:
* `state`: `closed` (healthy), `open` (failing), `half_open` (recovering).
* `retryAt`: Next retry time if open/half-open.
* `since`: When the current breaker state started.
* `lastTransition`: Timestamp of the last state transition.
* **`deadLetter`**:
* `present`: `true` if the instance is in the DLQ (stopped polling).
* `reason`: Why it was moved to DLQ (e.g., `permanent_failure`).
* `retryCount`: DLQ retry attempts.
* `nextRetry`: Next scheduled retry (if any).
* **`pollStatus`**: `lastSuccess` timestamp, `lastError` details, `consecutiveFailures` count.
* **`breaker`**: `state` (`closed`/`open`/`half_open`), `retryAt` next retry, `since` state start, `lastTransition` timestamp.
* **`deadLetter`**: `present` flag, `reason` (e.g., `permanent_failure`), `retryCount`, `nextRetry` if scheduled.
### Top-Level Queue and DLQ
* **`queue`**: Snapshot of the active task queue (depth + per-type counts).

View File

@@ -3,12 +3,12 @@
Pulse uses an adaptive scheduler to optimize polling based on instance health and activity.
## 🧠 Architecture
* **Scheduler**: Calculates intervals based on health/staleness.
* **Priority Queue**: Min-heap keyed by `NextRun`.
* **Circuit Breaker**: Prevents hot loops on failing instances using success/failure counters.
* **Backoff**: Exponential retry delays (5s min to 5m max).
* **Worker Pool**: One worker per configured instance (PVE/PBS/PMG), capped at 10.
* **Global Concurrency Cap**: At most 2 polling cycles run at once to avoid resource spikes.
- **Scheduler**: Calculates intervals based on health/staleness.
- **Priority Queue**: Min-heap keyed by `NextRun`.
- **Circuit Breaker**: Prevents hot loops on failing instances using success/failure counters.
- **Backoff**: Exponential retry delays (5s min to 5m max).
- **Worker Pool**: One worker per configured instance (PVE/PBS/PMG), capped at 10.
- **Global Concurrency Cap**: At most 2 polling cycles run at once to avoid resource spikes.
## 🔬 Implementation Details (Developer Info)
@@ -43,6 +43,7 @@ Adaptive polling is **disabled by default**.
There is currently no dedicated UI for adaptive polling in v5.
### Environment Variables
| Variable | Default | Description |
| :--- | :--- | :--- |
| `ADAPTIVE_POLLING_ENABLED` | `false` | Enable/disable. |
@@ -66,6 +67,7 @@ Exposed at `:9091/metrics`.
| `pulse_scheduler_queue_due_soon` | Gauge | Tasks due in the next 12 seconds. |
## ⚡ Circuit Breaker
| State | Trigger | Recovery |
| :--- | :--- | :--- |
| **Closed** | Normal operation. | — |
@@ -78,7 +80,7 @@ Exposed at `:9091/metrics`.
`GET /api/monitoring/scheduler/health` (Auth required)
Returns:
* Queue depth & breakdown.
* Dead-letter tasks.
* Circuit breaker states.
* Per-instance staleness.
- Queue depth & breakdown.
- Dead-letter tasks.
- Circuit breaker states.
- Per-instance staleness.

View File

@@ -11,6 +11,7 @@ This listener is separate from the main UI/API port (`7655`). In Docker and Kube
**Helm note:** the current chart exposes only port `7655`, so Prometheus scraping requires an additional Service that targets `9091` (and a matching ServiceMonitor).
## 🌐 HTTP Ingress
| Metric | Type | Description |
| :--- | :--- | :--- |
| `pulse_http_request_duration_seconds` | Histogram | Latency buckets by `method`, `route`, `status`. |
@@ -18,6 +19,7 @@ This listener is separate from the main UI/API port (`7655`). In Docker and Kube
| `pulse_http_request_errors_total` | Counter | Error totals by `method`, `route`, `status_class` (`client_error`, `server_error`, `none`). |
## 🔄 Polling & Nodes
| Metric | Type | Description |
| :--- | :--- | :--- |
| `pulse_monitor_poll_duration_seconds` | Histogram | Per-instance poll latency. |
@@ -34,6 +36,7 @@ This listener is separate from the main UI/API port (`7655`). In Docker and Kube
| `pulse_monitor_node_poll_staleness_seconds` | Gauge | Seconds since last node success (`-1` if never succeeded). |
## 🧠 Scheduler Health
| Metric | Type | Description |
| :--- | :--- | :--- |
| `pulse_scheduler_queue_due_soon` | Gauge | Tasks due within the next 12 seconds. |
@@ -45,6 +48,7 @@ This listener is separate from the main UI/API port (`7655`). In Docker and Kube
| `pulse_scheduler_breaker_retry_seconds` | Gauge | Seconds until next retry allowed. |
## ⚡ Diagnostics Cache
| Metric | Type | Description |
| :--- | :--- | :--- |
| `pulse_diagnostics_cache_hits_total` | Counter | Cache hits. |
@@ -52,6 +56,7 @@ This listener is separate from the main UI/API port (`7655`). In Docker and Kube
| `pulse_diagnostics_refresh_duration_seconds` | Histogram | Refresh latency. |
## 🚨 Alert Lifecycle
| Metric | Type | Description |
| :--- | :--- | :--- |
| `pulse_alerts_active` | Gauge | Active alerts by `level` and `type`. |
@@ -63,6 +68,6 @@ This listener is separate from the main UI/API port (`7655`). In Docker and Kube
| `pulse_alert_duration_seconds` | Histogram | Time from alert fire to resolve (by `type`). |
## 🚨 Alerting Examples
* **High Error Rate**: `rate(pulse_http_request_errors_total[5m]) > 0.05`
* **Stale Node**: `pulse_monitor_node_poll_staleness_seconds > 300`
* **Breaker Open**: `pulse_scheduler_breaker_state == 2`
- **High Error Rate**: `rate(pulse_http_request_errors_total[5m]) > 0.05`
- **Stale Node**: `pulse_monitor_node_poll_staleness_seconds > 300`
- **Breaker Open**: `pulse_scheduler_breaker_state == 2`

View File

@@ -11,19 +11,19 @@ Safely enable dynamic scheduling (v5+).
## 🟢 Enable
Choose one method:
* **UI**: Not currently exposed in the v5 UI (use CLI or env vars).
* **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).
- **UI**: Not currently exposed in the v5 UI (use CLI or env vars).
- **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).
## 🔍 Monitor (First 15m)
Watch for stability:
```bash
watch -n 5 'curl -s http://localhost:9091/metrics | grep pulse_monitor_poll_queue_depth'
```
* **Success**: Queue depth < 50, no permanent errors.
* **Failure**: High queue depth, open breakers.
- **Success**: Queue depth < 50, no permanent errors.
- **Failure**: High queue depth, open breakers.
## ↩️ Rollback
If instability occurs > 10m:

View File

@@ -1,54 +0,0 @@
# 🔄 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`.
## ⚠️ Important
* **Do not delete**: The file is protected with `chattr +a`.
* **Rotate when**: >200MB or >30 days.
## 🛠️ Manual Rotation
Run as root:
```bash
# 1. Unlock file
chattr -a /var/log/pulse/sensor-proxy/audit.log
# 2. Rotate (copy & truncate)
cp -a /var/log/pulse/sensor-proxy/audit.log /var/log/pulse/sensor-proxy/audit.log.$(date +%Y%m%d)
: > /var/log/pulse/sensor-proxy/audit.log
# 3. Relock & Restart
chown pulse-sensor-proxy:pulse-sensor-proxy /var/log/pulse/sensor-proxy/audit.log
chmod 0640 /var/log/pulse/sensor-proxy/audit.log
chattr +a /var/log/pulse/sensor-proxy/audit.log
systemctl restart pulse-sensor-proxy
```
## 🤖 Logrotate Config
Create `/etc/logrotate.d/pulse-sensor-proxy`:
```conf
/var/log/pulse/sensor-proxy/audit.log {
weekly
rotate 8
compress
missingok
notifempty
create 0640 pulse-sensor-proxy pulse-sensor-proxy
sharedscripts
prerotate
/usr/bin/chattr -a /var/log/pulse/sensor-proxy/audit.log || true
endscript
postrotate
/bin/systemctl restart pulse-sensor-proxy.service || true
/usr/bin/chattr +a /var/log/pulse/sensor-proxy/audit.log || true
endscript
}
```
**Note**: Do NOT use `copytruncate`. The restart is required to reset the hash chain.

View File

@@ -4,6 +4,7 @@ Manage Pulse auto-updates on host-mode installations.
> **Note**: Docker/Kubernetes users should manage updates via their orchestrator.
## ⚙️ Components
| File | Purpose |
| :--- | :--- |
| `pulse-update.timer` | Daily check (02:00 + jitter). |

View File

@@ -1,44 +0,0 @@
# ⚙️ Sensor Proxy Configuration
> **Deprecated in v5:** `pulse-sensor-proxy` is deprecated and not recommended for new deployments.
> Use `pulse-agent --enable-proxmox` for temperature monitoring.
> This document is retained for existing installations during the migration window.
Safe configuration management using the built-in CLI.
## 📂 Files
* **`config.yaml`**: General settings (logging, metrics).
* **`allowed_nodes.yaml`**: Authorized node list (managed via CLI).
## 🛠️ CLI Reference
### Validation
Check for errors before restart.
```bash
pulse-sensor-proxy config validate
```
### Managing Nodes
**Add Nodes (Merge):**
```bash
pulse-sensor-proxy config set-allowed-nodes --merge 192.168.0.10
```
**Replace List:**
```bash
pulse-sensor-proxy config set-allowed-nodes --replace \
--merge 192.168.0.1 --merge 192.168.0.2
```
## ⚠️ Troubleshooting
**Validation Fails:**
* Check for duplicate `allowed_nodes` blocks in `config.yaml`.
* Run `pulse-sensor-proxy config validate 2>&1` for details.
**Lock Errors:**
* Remove stale locks if process is dead: `rm /etc/pulse-sensor-proxy/*.lock`.
**Empty List:**
* Valid for IPC-only clusters.
* Populate manually if needed using `--replace`.

View File

@@ -1,35 +0,0 @@
# 📝 Sensor Proxy Log Forwarding
> **Deprecated in v5:** `pulse-sensor-proxy` is deprecated and not recommended for new deployments.
> Use `pulse-agent --enable-proxmox` for temperature monitoring.
> This document is retained for existing installations during the migration window.
Forward `audit.log` and `proxy.log` to a central SIEM via RELP + TLS.
## 🚀 Quick Start
Run the helper script with your collector details:
```bash
sudo REMOTE_HOST=logs.example.com \
REMOTE_PORT=6514 \
CERT_DIR=/etc/pulse/log-forwarding \
CA_CERT=/path/to/ca.crt \
CLIENT_CERT=/path/to/client.crt \
CLIENT_KEY=/path/to/client.key \
bash -c "$(curl -fsSL https://raw.githubusercontent.com/rcourtman/Pulse/main/scripts/setup-log-forwarding.sh)"
```
## 📋 What It Does
1. **Inputs**: Watches `/var/log/pulse/sensor-proxy/{audit,proxy}.log`.
2. **Queue**: Disk-backed queue (50k messages) for reliability.
3. **Output**: RELP over TLS to `REMOTE_HOST`.
4. **Mirror**: Local debug file at `/var/log/pulse/sensor-proxy/forwarding.log`.
## ✅ Verification
1. **Check Status**: `sudo systemctl status rsyslog`
2. **View Mirror**: `tail -f /var/log/pulse/sensor-proxy/forwarding.log`
3. **Test**: Restart proxy and check remote collector for `pulse.audit` tag.
## 🧹 Maintenance
* **Disable**: Remove `/etc/rsyslog.d/pulse-sensor-proxy.conf` and restart rsyslog.
* **Rotate Certs**: Replace files in `CERT_DIR` and restart rsyslog.

View File

@@ -3,29 +3,29 @@
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
<https://github.com/rcourtman/Pulse/releases>
---
# Pulse v4.31.0
## Pulse v4.31.0
## What's Changed
### What's Changed (v4.31.0)
### Temperature monitoring over HTTPS
#### Temperature monitoring over HTTPS
- `pulse-sensor-proxy` now exposes an authenticated HTTPS endpoint per Proxmox host. Pulse stores each proxys URL + bearer token and always polls `https://node:8443/temps` before falling back to local sockets or SSH, eliminating the fragile “single proxy for every node” chain.
- Installations auto-register via the new `/api/temperature-proxy/register` endpoint, generate 4096-bit certificates, enforce CIDR allowlists, and log every HTTP request through the proxys audit pipeline.
- The backend temperature collector understands proxy URLs/tokens, respects strict timeouts, and publishes richer diagnostics so operators can see which node failed and why.
### Installer, diagnostics, and UI updates
#### 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
#### 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
### Upgrade Notes (v4.31.0)
Temperature monitoring will not work for remote nodes until every Proxmox host is reinstalled with the new HTTPS workflow. Follow these steps per host:
@@ -46,7 +46,7 @@ curl -vk https://node.example:8443/health \
-H "Authorization: Bearer $(sudo cat /etc/pulse-sensor-proxy/.http-auth-token)"
```
## Installation
### Installation (v4.31.0)
- **Install or upgrade with the helper script**
```bash
curl -sL https://github.com/rcourtman/Pulse/releases/latest/download/install.sh | bash
@@ -72,7 +72,7 @@ curl -vk https://node.example:8443/health \
--namespace pulse --create-namespace
```
## Downloads
### Downloads (v4.31.0)
- Multi-arch Linux tarballs (amd64/arm64/armv7)
- Standalone sensor proxy binaries (now include HTTP mode)
- Helm chart archive (pulse-4.31.0-helm.tgz)
@@ -81,29 +81,29 @@ curl -vk https://node.example:8443/health \
---
# Pulse v4.26.1
## Pulse v4.26.1
## What's Changed
### New
### What's Changed (v4.26.1)
#### 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 **Proxmox → 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
#### 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 platform and agent sections, richer filters, and platform icons that make 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
#### 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
### Installation (v4.26.1)
- **Install or upgrade with the helper script**
```bash
curl -sL https://github.com/rcourtman/Pulse/releases/latest/download/install.sh | bash
@@ -129,7 +129,7 @@ curl -vk https://node.example:8443/health \
--namespace pulse --create-namespace
```
## Downloads
### Downloads (v4.26.1)
- Multi-arch Linux tarballs (amd64/arm64/armv7)
- Standalone sensor proxy binaries
- Helm chart archive (pulse-4.26.1-helm.tgz)

View File

@@ -1,47 +0,0 @@
# 🛡️ Sensor Proxy AppArmor (Optional)
> **Deprecated in v5:** `pulse-sensor-proxy` is deprecated and not recommended for new deployments.
> Use `pulse-agent --enable-proxmox` for temperature monitoring.
> This document is retained for existing installations during the migration window.
Secure `pulse-sensor-proxy` with AppArmor and Seccomp.
## 🛡️ AppArmor
Profile: `security/apparmor/pulse-sensor-proxy.apparmor`
* **Allows**: Configs, logs, SSH keys, outbound TCP/SSH.
* **Blocks**: Raw sockets, module loading, ptrace, exec outside allowlist.
### Install & Enforce
```bash
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 aa-enforce pulse-sensor-proxy
```
## 🔒 Seccomp
Profile: `security/seccomp/pulse-sensor-proxy.json`
* **Allows**: Go runtime syscalls, network, file IO.
* **Blocks**: Everything else (returns `EPERM`).
### Systemd (Classic)
Add to service override:
```ini
[Service]
AppArmorProfile=pulse-sensor-proxy
SystemCallFilter=@system-service
SystemCallAllow=accept;connect;recvfrom;sendto;recvmsg;sendmsg;sendmmsg;getsockname;getpeername;getsockopt;setsockopt;shutdown
```
### Containers (Docker/Podman)
```bash
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
Check status with `aa-status` or `journalctl -t auditbeat`.

View File

@@ -1,64 +0,0 @@
# 🛡️ Sensor Proxy Hardening
> **Deprecated in v5:** `pulse-sensor-proxy` is deprecated and not recommended for new deployments.
> Use `pulse-agent --enable-proxmox` for temperature monitoring.
> 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.
## 🏗️ Architecture
* **Host**: Runs `pulse-sensor-proxy` (unprivileged user).
* **Container**: Connects via Unix socket (`/run/pulse-sensor-proxy/pulse-sensor-proxy.sock`).
* **Auth**: Uses `SO_PEERCRED` to verify container UID/PID.
## 🔒 Host Hardening
### Service Account
Runs as `pulse-sensor-proxy` (no shell, no home).
```bash
id pulse-sensor-proxy # uid=XXX(pulse-sensor-proxy)
```
### Systemd Security
The service unit uses:
* `User=pulse-sensor-proxy`
* `NoNewPrivileges=true`
* `ProtectSystem=strict`
* `PrivateTmp=true`
### File Permissions
| Path | Owner | Mode |
| :--- | :--- | :--- |
| `/var/lib/pulse-sensor-proxy/` | `pulse-sensor-proxy` | `0750` |
| `/var/lib/pulse-sensor-proxy/ssh/` | `pulse-sensor-proxy` | `0700` |
| `/run/pulse-sensor-proxy/` | `pulse-sensor-proxy` | `0775` |
## 📦 LXC Configuration
Required for the container to access the proxy socket.
**`/etc/pve/lxc/<VMID>.conf`**:
```ini
unprivileged: 1
lxc.apparmor.profile: generated
lxc.mount.entry: /run/pulse-sensor-proxy mnt/pulse-proxy none bind,create=dir 0 0
```
## 🔑 Key Management
SSH keys are restricted to `sensors -j` only.
**Rotation**:
```bash
bash -c "$(curl -fsSL https://raw.githubusercontent.com/rcourtman/Pulse/main/scripts/pulse-proxy-rotate-keys.sh)"
```
* **Dry Run**: Add `--dry-run`.
* **Rollback**: Add `--rollback`.
## 🚨 Incident Response
If compromised:
1. **Stop Proxy**: `systemctl stop pulse-sensor-proxy`.
2. **Rotate Keys**: Remove old keys from nodes manually or use the rotation helper above.
3. **Audit Logs**: Check `journalctl -u pulse-sensor-proxy`.
4. **Reinstall**:
```bash
curl -fsSL https://github.com/rcourtman/Pulse/releases/latest/download/install-sensor-proxy.sh | sudo bash
```

View File

@@ -1,39 +0,0 @@
# 🌐 Sensor Proxy Network Segmentation
> **Deprecated in v5:** `pulse-sensor-proxy` is deprecated and not recommended for new deployments.
> Use `pulse-agent --enable-proxmox` for temperature monitoring.
> This document is retained for existing installations during the migration window.
Isolate the proxy to prevent lateral movement.
## 🚧 Zones
* **Pulse App**: Connects to Proxy via Unix socket (local).
* **Sensor Proxy**: Outbound SSH to Proxmox nodes only.
* **Proxmox Nodes**: Accept SSH from Proxy.
* **Logging**: Accepts RELP/TLS from Proxy.
## 🛡️ Firewall Rules
| Source | Dest | Port | Purpose | Action |
| :--- | :--- | :--- | :--- | :--- |
| **Pulse App** | Proxy | `unix` | RPC Requests | **Allow** (Local) |
| **Proxy** | Nodes | `22` | SSH (sensors) | **Allow** |
| **Proxy** | Logs | `6514` | Audit Logs | **Allow** |
| **Any** | Proxy | `22` | SSH Access | **Deny** (Use Bastion) |
| **Proxy** | Internet | `any` | Outbound | **Deny** |
## 🔧 Implementation (iptables)
```bash
# Allow SSH to Proxmox
iptables -A OUTPUT -p tcp -d <PROXMOX_SUBNET> --dport 22 -j ACCEPT
# Allow Log Forwarding
iptables -A OUTPUT -p tcp -d <LOG_HOST> --dport 6514 -j ACCEPT
# Drop all other outbound
iptables -P OUTPUT DROP
```
## 🚨 Monitoring
* Alert on outbound connections to non-whitelisted IPs.
* Monitor `pulse_proxy_limiter_rejects_total` for abuse.

View File

@@ -32,9 +32,9 @@ The agent runs `sensors -j` locally and reports temperatures directly to Pulse.
`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.
### 🛡️ Security Model
* **Isolation**: SSH keys live on the host, not in the container.
* **Least Privilege**: Proxy runs as `pulse-sensor-proxy` (no shell).
* **Verification**: Container identity verified via `SO_PEERCRED`.
- **Isolation**: SSH keys live on the host, not in the container.
- **Least Privilege**: Proxy runs as `pulse-sensor-proxy` (no shell).
- **Verification**: Container identity verified via `SO_PEERCRED`.
### 🏗️ Components
1. **Pulse Backend**: Connects to Unix socket `/mnt/pulse-proxy/pulse-sensor-proxy.sock`.
@@ -43,14 +43,14 @@ The agent runs `sensors -j` locally and reports temperatures directly to Pulse.
### 🔒 Key Restrictions
SSH keys deployed to nodes are locked down:
```
```text
command="sensors -j",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty
```
### 🚦 Rate Limiting
* **Per Peer**: ~12 req/min.
* **Concurrency**: Max 2 parallel requests per peer.
* **Global**: Max 8 concurrent requests.
- **Per Peer**: ~12 req/min.
- **Concurrency**: Max 2 parallel requests per peer.
- **Global**: Max 8 concurrent requests.
### 📝 Auditing
All requests logged to system journal:
@@ -61,6 +61,5 @@ 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`
- Unified Agent Security: [`docs/AGENT_SECURITY.md`](../AGENT_SECURITY.md)
- Repository Security Policy: [`/SECURITY.md`](../../SECURITY.md)

View File

@@ -2,7 +2,7 @@
This `frontend-modern` directory is **AUTO-GENERATED** during builds.
## The REAL frontend location is:
## The REAL frontend location is
### `/opt/pulse/frontend-modern`
## Why does this exist?
@@ -14,10 +14,10 @@ This `frontend-modern` directory is **AUTO-GENERATED** during builds.
- **YOUR CHANGES WILL BE LOST** on the next build
- The Makefile deletes and recreates this directory
## How to edit frontend code:
## How to edit frontend code
1. Edit files in `/opt/pulse/frontend-modern/src/`
2. The dev server (port 7655) will hot-reload
3. When building for production, the Makefile copies it here
---
This file exists to prevent confusion. The directory structure is intentional and required by Go's limitations.
This file exists to prevent confusion. The directory structure is intentional and required by Go's limitations.

View File

@@ -19,4 +19,4 @@ Go's `//go:embed` directive has limitations:
2. Cannot follow symbolic links
3. Must embed files within the Go module
This is a known Go limitation and our structure works around it.
This is a known Go limitation and our structure works around it.

View File

@@ -13,7 +13,7 @@ This guide will help you get the update integration tests running quickly.
```bash
cd tests/integration
./scripts/setup.sh
```
```text
This will:
- Install npm dependencies
@@ -85,11 +85,11 @@ npm run docker:rebuild
While the test environment is running:
- **Pulse UI**: http://localhost:7655
- **Mock GitHub API**: http://localhost:8080
- **Pulse UI**: <http://localhost:7655>
- **Mock GitHub API**: <http://localhost:8080>
- **Health checks**:
- http://localhost:7655/api/health
- http://localhost:8080/health
- <http://localhost:7655/api/health>
- <http://localhost:8080/health>
## Viewing Test Results
@@ -175,7 +175,7 @@ See `.github/workflows/test-updates.yml` for CI configuration.
## Architecture
```
```text
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────────┐
│ Playwright │────▶│ Pulse Server │────▶│ Mock GitHub API │
│ (Browser UI) │ │ (Test Instance) │ │ (Controlled │
@@ -216,4 +216,4 @@ test('my new test', async ({ page }) => {
- Check the [main README](./README.md) for detailed information
- Review existing test files for examples
- Check Docker logs for service issues
- Review Playwright documentation: https://playwright.dev
- Review Playwright documentation: <https://playwright.dev>

View File

@@ -4,7 +4,7 @@ End-to-end Playwright tests that validate critical user flows against a running
## Architecture
```
```text
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────────┐
│ Playwright │────▶│ Pulse Server │────▶│ Mock GitHub API │
│ (Browser UI) │ │ (Test Instance) │ │ (Controlled │