diff --git a/.gitignore b/.gitignore
index 81f66e9..c63b063 100644
--- a/.gitignore
+++ b/.gitignore
@@ -139,6 +139,7 @@ mock.env.backup
# Sensitive files - DO NOT COMMIT
secrets.env
*secret*.env
+docs/PULSE_PRO_IMPLEMENTATION.md
# Browser/session artifacts
**/cookies.txt
@@ -149,6 +150,10 @@ secrets.env
# Development documentation (local only)
CLAUDE_DEV_SETUP.md
AGENT_METRICS_*.md
+DEV-QUICK-START.md
+docs/DOCS_AUDIT_*.md
+docs/SECURITY_AUDIT_*.md
+docs/development/
# Temporary scripts
tmp_*.py
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index edb78e8..a35147a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -43,7 +43,7 @@ cd ..
npm run mock:on # Optional: enable mock data
```
-See `docs/development/MOCK_MODE.md` for tips on synthetic fixtures.
+Mock mode is supported for development, but the internal developer notes are not shipped in this repository.
---
diff --git a/DEV-QUICK-START.md b/DEV-QUICK-START.md
deleted file mode 100644
index 947eed4..0000000
--- a/DEV-QUICK-START.md
+++ /dev/null
@@ -1,99 +0,0 @@
-# Development Quick Start
-
-## Prerequisites
-
-- Go **1.24.9** or newer
-- Node.js 20+
-- pnpm 9+ (for frontend work)
-
-> **Tip**: Read [`ARCHITECTURE.md`](ARCHITECTURE.md) to understand the system design before diving in.
-
-## Hot-Reload Development Mode
-
-Start the development environment with hot-reload:
-
-```bash
-./scripts/hot-dev.sh
-```
-
-This starts:
-- Backend API on port 7656
-- Frontend on port 7655 with hot-reload
-- Both backend and frontend automatically reload on code changes
-
-Access the app at: http://localhost:7655 or http://192.168.0.123:7655
-
-## Toggle Between Mock and Production Data
-
-Switch modes seamlessly without manually restarting services:
-
-```bash
-# Enable mock mode (test with fake data)
-npm run mock:on
-
-# Disable mock mode (use real Proxmox nodes)
-npm run mock:off
-
-# Check current mode
-npm run mock:status
-
-# Edit mock configuration
-npm run mock:edit
-```
-
-Or use the script directly:
-
-```bash
-./scripts/toggle-mock.sh on # Enable mock mode
-./scripts/toggle-mock.sh off # Disable mock mode (use production data)
-./scripts/toggle-mock.sh status # Show current status
-```
-
-The toggle script automatically:
-- Updates `mock.env` configuration
-- Restarts the backend with new settings
-- Keeps the frontend running (no restart needed)
-- Syncs production config when switching to production mode
-- Switches `PULSE_DATA_DIR` between `/opt/pulse/tmp/mock-data` (mock) and `/etc/pulse` (production) so test data never touches real credentials
-
-## Mock Mode Configuration
-
-Edit `mock.env` to customize mock data:
-
-```bash
-PULSE_MOCK_MODE=false # Enable/disable mock mode
-PULSE_MOCK_NODES=7 # Number of mock nodes
-PULSE_MOCK_VMS_PER_NODE=5 # Average VMs per node
-PULSE_MOCK_LXCS_PER_NODE=8 # Average containers per node
-PULSE_MOCK_RANDOM_METRICS=true # Enable metric fluctuations
-PULSE_MOCK_STOPPED_PERCENT=20 # Percentage of stopped guests
-```
-
-Prefer `mock.env.local` for personal tweaks (`cp mock.env mock.env.local`). The toggle script honours `.local` first, keeping the shared defaults untouched.
-
-## Development Workflow
-
-1. Start hot-dev: `./scripts/hot-dev.sh`
-2. Switch to mock mode for testing: `npm run mock:on`
-3. Develop and test your changes
-4. Switch to production mode to verify: `npm run mock:off`
-5. Code changes auto-reload, no manual restarts needed!
-
-## Troubleshooting
-
-If the backend doesn't pick up changes:
-```bash
-npm run mock:off # Force restart with production data
-npm run mock:on # Force restart with mock data
-```
-
-Check backend logs:
-```bash
-tail -f /tmp/pulse-backend.log
-```
-
-Check if services are running:
-```bash
-lsof -i :7656 # Backend
-lsof -i :7655 # Frontend
-```
diff --git a/SECURITY.md b/SECURITY.md
index 2e804df..20f3522 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -52,7 +52,7 @@ Preferred option (no SSH keys, no proxy wiring):
Deprecated option (existing installs only):
-- `pulse-sensor-proxy` is deprecated in Pulse v5 and is not recommended for new deployments.
+- `pulse-sensor-proxy` is deprecated in Pulse v5 and is not recommended for new deployments. In v5, legacy sensor-proxy endpoints are disabled by default unless `PULSE_ENABLE_SENSOR_PROXY=true` is set on the Pulse server.
- 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`
@@ -376,7 +376,7 @@ docker run -e API_TOKENS=ansible-token,docker-agent-token rcourtman/pulse:latest
**Security Note**: Tokens defined via environment variables are hashed with SHA3-256 before being stored on disk. Plain values never persist beyond startup.
-#### Token Management (Settings → Security → API tokens)
+#### Token Management (Settings → API Tokens)
- Issue dedicated tokens for automation/agents without sharing a global credential
- View prefixes/suffixes and last-used timestamps for auditing
- Revoke tokens individually without downtime
@@ -441,7 +441,7 @@ docker run \
- Debug logs may contain sensitive data—enable only when needed
- JSON format recommended for security monitoring and SIEM
- Adjust retention based on compliance requirements
- - Changes take effect on restart
+- Changes take effect on restart
## CORS (Cross-Origin Resource Sharing)
@@ -460,14 +460,14 @@ sudo systemctl edit pulse
[Service]
Environment="ALLOWED_ORIGINS=https://app.example.com"
-# Multiple origins (comma-separated)
-ALLOWED_ORIGINS="https://app.example.com,https://dashboard.example.com"
-
# Development mode (allows localhost)
PULSE_DEV=true
```
-**Security Note**: Never use `ALLOWED_ORIGINS=*` in production as it allows any website to access your API.
+Notes:
+
+- `ALLOWED_ORIGINS` currently supports a single origin or `*` (it is written directly to `Access-Control-Allow-Origin`).
+- Never use `ALLOWED_ORIGINS=*` in production as it allows any website to access your API.
## Monitoring and Observability
@@ -566,7 +566,7 @@ curl -X POST http://localhost:7655/api/security/reset-lockout \
**Export blocked?** You're on a public network – login with password, set an API token (`API_TOKENS`), or set `ALLOW_UNPROTECTED_EXPORT=true`
**Rate limited?** Wait 1 minute and try again
**Can't login?** Check `PULSE_AUTH_USER` and `PULSE_AUTH_PASS` environment variables
-**API access denied?** Verify the token you supplied matches one of the values created in *Settings → Security → API tokens* (use the original token, not the hash)
+**API access denied?** Verify the token you supplied matches one of the values created in *Settings → API Tokens* (use the original token, not the hash)
**CORS errors?** Configure `ALLOWED_ORIGINS` for your domain
**Forgot password?** Start fresh – delete your Pulse data and restart
diff --git a/docker-compose.yml b/docker-compose.yml
index 18d584d..00ca491 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -9,9 +9,9 @@ services:
- "${PULSE_PORT:-7655}:7655"
volumes:
- pulse-data:/data
- # Secure temperature monitoring via host-side proxy (requires setup - see docs)
- # Uncomment after installing pulse-sensor-proxy on host with --standalone flag
- # Mount is read-only (:ro) for security - proxy uses SO_PEERCRED for access control
+ # Temperature monitoring:
+ # - Recommended (v5): install pulse-agent on each Proxmox host with --enable-proxmox.
+ # - Deprecated: pulse-sensor-proxy (host-side proxy). If you already use it, mount the socket read-only.
# - /run/pulse-sensor-proxy:/run/pulse-sensor-proxy:ro
environment:
- TZ=${TZ:-UTC}
diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md
index 69a8278..a86ab6d 100644
--- a/docs/CONFIGURATION.md
+++ b/docs/CONFIGURATION.md
@@ -53,7 +53,7 @@ docker run -d \
Advanced: OIDC / SSO
-Configure Single Sign-On in **Settings → Security → OIDC**, or use environment variables to lock the configuration.
+Configure Single Sign-On in **Settings → Security → Single Sign-On**, or use environment variables to lock the configuration.
See [OIDC Documentation](OIDC.md) and [Proxy Auth](PROXY_AUTH.md) for details.
@@ -92,6 +92,7 @@ Environment variables take precedence over `system.json`.
| `PULSE_PUBLIC_URL` | Public URL for notifications/OIDC | `""` |
| `ALLOWED_ORIGINS` | CORS allowed domains | `""` (Same origin) |
| `DISCOVERY_ENABLED` | Auto-discover nodes | `false` |
+| `PULSE_ENABLE_SENSOR_PROXY` | Enable legacy `pulse-sensor-proxy` endpoints (deprecated, unsupported) | `false` |
| `PULSE_AUTH_HIDE_LOCAL_LOGIN` | Hide username/password form | `false` |
| `DEMO_MODE` | Enable read-only demo mode | `false` |
diff --git a/docs/DOCS_AUDIT_V5.md b/docs/DOCS_AUDIT_V5.md
deleted file mode 100644
index eccd7c9..0000000
--- a/docs/DOCS_AUDIT_V5.md
+++ /dev/null
@@ -1,317 +0,0 @@
-# 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.
diff --git a/docs/PULSE_PRO_IMPLEMENTATION.md b/docs/PULSE_PRO_IMPLEMENTATION.md
deleted file mode 100644
index d8df644..0000000
--- a/docs/PULSE_PRO_IMPLEMENTATION.md
+++ /dev/null
@@ -1,401 +0,0 @@
-# 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?*
diff --git a/docs/README.md b/docs/README.md
index 1bbe4cf..0eb287f 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -42,16 +42,14 @@ Welcome to the Pulse documentation portal. Here you'll find everything you need
- **[Unified Agent](UNIFIED_AGENT.md)** – Single binary for Host and Docker monitoring.
- **[VM Disk Monitoring](VM_DISK_MONITORING.md)** – Enabling QEMU Guest Agent for disk stats.
-- **[Temperature Monitoring](TEMPERATURE_MONITORING.md)** – Setting up hardware sensors.
+- **[Temperature Monitoring](TEMPERATURE_MONITORING.md)** – Agent-based temperature monitoring (`pulse-agent --enable-proxmox`). Sensor proxy is deprecated in v5.
- **[Webhooks](WEBHOOKS.md)** – Custom notification payloads.
## 💻 Development
- **[API Reference](API.md)** – Complete REST API documentation.
-- **[Dev Quick Start](../DEV-QUICK-START.md)** – Hot-reload workflow for contributors.
- **[Architecture](../ARCHITECTURE.md)** – System design and component interaction.
- **[Contributing](../CONTRIBUTING.md)** – How to contribute to Pulse.
-- **[Mock Mode](development/MOCK_MODE.md)** – Developing without real infrastructure.
---
diff --git a/docs/SECURITY_AUDIT_2025-11-07.md b/docs/SECURITY_AUDIT_2025-11-07.md
deleted file mode 100644
index 664e8f3..0000000
--- a/docs/SECURITY_AUDIT_2025-11-07.md
+++ /dev/null
@@ -1,631 +0,0 @@
-# Security Audit Report - Pulse Temperature Proxy
-## Date: 2025-11-07
-## Auditors: Claude (Sonnet 4.5) + Codex
-
----
-
-## Executive Summary
-
-This document presents the findings and remediations from a comprehensive security audit of the pulse-sensor-proxy architecture. The audit identified **9 security issues** ranging from critical to low severity, all of which have been **successfully remediated**.
-
-### Overall Assessment
-
-**Before Audit:** B+ (Good, with important gaps)
-**After Remediation:** A (Excellent security posture)
-
-**Risk Reduction:** All critical vulnerabilities eliminated. System now resilient to container compromise scenarios.
-
----
-
-## Audit Methodology
-
-1. **Architecture Review** - Analysis of trust boundaries and security design
-2. **Code Review** - Line-by-line examination of security-critical code paths
-3. **Threat Modeling** - Evaluation of attack vectors and exploitation scenarios
-4. **Collaborative Analysis** - Claude + Codex independent review and challenge
-5. **Implementation** - Codex-led implementation of all fixes
-6. **Testing** - Comprehensive test coverage for all security features
-
----
-
-## Findings and Remediations
-
-### CRITICAL SEVERITY
-
-#### 1. Socket Directory Tampering ✅ FIXED
-
-**Finding:**
-Socket directory mounted read-write into containers, allowing compromised containers to:
-- Unlink socket and create man-in-the-middle proxies
-- Fill `/run/pulse-sensor-proxy/` to exhaust tmpfs
-- Race proxy service on restart to hijack socket path
-
-**CVSS Score:** 8.1 (High)
-**Attack Complexity:** Low
-**Privileges Required:** Low (container access)
-**Impact:** Complete compromise of proxy communication
-
-**Remediation:**
-- Changed all socket mounts to read-only (`:ro`)
-- Updated documentation to reflect secure configuration
-- Added validation to installer
-
-**Files Modified:**
-- `docker-compose.yml`
-- `docs/TEMPERATURE_MONITORING.md`
-
-**Status:** ✅ Deployed
-
----
-
-#### 2. Unrestricted SSRF via get_temperature ✅ FIXED
-
-**Finding:**
-Proxy would SSH to ANY hostname/IP passing format validation, enabling:
-- Internal network reconnaissance via SSH handshakes
-- Port scanning using proxy as relay
-- Resource exhaustion via slow-loris SSH attacks
-- Complete bypass of network security controls
-
-**CVSS Score:** 8.6 (High)
-**Attack Complexity:** Low
-**Privileges Required:** Low (container access)
-**Impact:** Full internal network access from host context
-
-**Remediation:**
-- Implemented multi-layer node validation system
-- Configurable `allowed_nodes` list (hostnames, IPs, CIDR ranges)
-- Automatic cluster membership validation on Proxmox hosts
-- 5-minute cache of cluster membership
-- New metric: `pulse_proxy_node_validation_failures_total`
-
-**Configuration Example:**
-```yaml
-allowed_nodes:
- - "pve1"
- - "192.168.1.0/24"
-strict_node_validation: true
-```
-
-**Files Created:**
-- `cmd/pulse-sensor-proxy/validation.go`
-- `cmd/pulse-sensor-proxy/validation_test.go`
-
-**Files Modified:**
-- `cmd/pulse-sensor-proxy/config.go`
-- `cmd/pulse-sensor-proxy/main.go`
-- `cmd/pulse-sensor-proxy/metrics.go`
-
-**Tests:** 4 new tests, all passing
-**Status:** ✅ Deployed
-
----
-
-#### 3. Missing Read Deadline (Connection Exhaustion) ✅ FIXED
-
-**Finding:**
-No read deadline allowed attackers to hold connection slots indefinitely:
-- Connect but never send data
-- 4 UIDs could consume all 8 global slots
-- Trivial DoS with minimal resources
-
-**CVSS Score:** 7.5 (High)
-**Attack Complexity:** Low
-**Privileges Required:** Low (container access)
-**Impact:** Complete service denial
-
-**Remediation:**
-- Added configurable `read_timeout` (default 5s) and `write_timeout` (default 10s)
-- Read deadline set before request parsing, cleared before handler
-- Write deadline set before response transmission
-- Automatic penalty on timeout
-- New metrics: `pulse_proxy_read_timeouts_total`, `pulse_proxy_write_timeouts_total`
-
-**Configuration:**
-```yaml
-read_timeout: 5s
-write_timeout: 10s
-```
-
-**Files Modified:**
-- `cmd/pulse-sensor-proxy/config.go`
-- `cmd/pulse-sensor-proxy/main.go`
-- `cmd/pulse-sensor-proxy/metrics.go`
-
-**Status:** ✅ Deployed
-
----
-
-#### 4. Multi-UID Rate Limit Bypass ✅ FIXED
-
-**Finding:**
-Rate limiting per-UID easily bypassed by creating multiple users in container:
-- Each user mapped to unique host UID (100000-165535 range)
-- Each UID got separate rate limit quota
-- Attackers could drive proxy to 100% CPU
-
-**CVSS Score:** 7.5 (High)
-**Attack Complexity:** Low
-**Privileges Required:** Low (container access)
-**Impact:** Service degradation/denial
-
-**Remediation:**
-- Automatic detection of ID-mapped UID ranges from `/etc/subuid` and `/etc/subgid`
-- Rate limits applied per-range for container UIDs
-- Rate limits applied per-UID for host UIDs (backwards compatible)
-- Metrics show `peer="range:100000-165535"` or `peer="uid:0"`
-
-**Technical Implementation:**
-- `identifyPeer()` checks if BOTH UID AND GID are in mapped ranges
-- If in range: all UIDs share rate limits
-- If NOT in range: legacy per-UID limiting
-
-**Files Modified:**
-- `cmd/pulse-sensor-proxy/throttle.go`
-- `cmd/pulse-sensor-proxy/main.go`
-- `cmd/pulse-sensor-proxy/auth.go`
-- `cmd/pulse-sensor-proxy/metrics.go`
-
-**Files Created:**
-- `cmd/pulse-sensor-proxy/throttle_test.go`
-
-**Tests:** 1 new test, passing
-**Status:** ✅ Deployed
-
----
-
-### MEDIUM SEVERITY
-
-#### 5. Incomplete GID Authorization ✅ FIXED
-
-**Finding:**
-`allowed_peer_gids` populated from config but never checked during authorization:
-- Created false sense of security
-- GID-based policies silently ignored
-- Administrators unaware policies not enforced
-
-**CVSS Score:** 5.3 (Medium)
-**Attack Complexity:** Low
-**Impact:** Authorization bypass
-
-**Remediation:**
-- Implemented GID checking in `authorizePeer()`
-- Peer authorized if UID **OR** GID matches
-- Debug logging shows which rule granted access
-- Updated documentation
-
-**Files Modified:**
-- `cmd/pulse-sensor-proxy/auth.go`
-
-**Files Created:**
-- `cmd/pulse-sensor-proxy/auth_test.go`
-
-**Tests:** 2 new tests, all passing
-**Status:** ✅ Deployed
-
----
-
-#### 6. Unbounded SSH Output ✅ FIXED
-
-**Finding:**
-No limit on SSH command output size:
-- Malicious remote node could stream gigabytes
-- Memory exhaustion
-- CPU spike on parsing
-
-**CVSS Score:** 6.5 (Medium)
-**Attack Complexity:** Low
-**Impact:** Resource exhaustion
-
-**Remediation:**
-- Added `max_ssh_output_bytes` config (default: 1MB)
-- Stream with `io.LimitReader` to cap output size
-- Error if limit exceeded
-- New metric: `pulse_proxy_ssh_output_oversized_total{node}`
-- WARN logging for oversized outputs
-
-**Configuration:**
-```yaml
-max_ssh_output_bytes: 1048576 # 1MB default
-```
-
-**Files Modified:**
-- `cmd/pulse-sensor-proxy/config.go`
-- `cmd/pulse-sensor-proxy/ssh.go`
-- `cmd/pulse-sensor-proxy/metrics.go`
-
-**Files Created:**
-- `cmd/pulse-sensor-proxy/ssh_test.go`
-
-**Tests:** 1 new test, passing
-**Status:** ✅ Deployed
-
----
-
-#### 7. Weak Host Key Validation (TOFU) ✅ FIXED
-
-**Finding:**
-Trust-On-First-Use (TOFU) via `ssh-keyscan`:
-- Trusts whatever key remote offers on first contact
-- No administrator approval for new fingerprints
-- Vulnerable to MITM if container influences routing
-
-**CVSS Score:** 6.5 (Medium)
-**Attack Complexity:** Medium
-**Impact:** MITM attacks possible
-
-**Remediation:**
-- Implemented Proxmox host key seeding from `/etc/pve/priv/known_hosts`
-- Falls back to ssh-keyscan only if Proxmox unavailable (with WARN)
-- Added `require_proxmox_hostkeys` config option
-- Fingerprint change detection with ERROR logging
-- New metric: `pulse_proxy_hostkey_changes_total{node}`
-
-**Configuration:**
-```yaml
-require_proxmox_hostkeys: false # true = strict mode
-```
-
-**Files Modified:**
-- `internal/ssh/knownhosts/manager.go`
-- `cmd/pulse-sensor-proxy/ssh.go`
-- `cmd/pulse-sensor-proxy/config.go`
-- `cmd/pulse-sensor-proxy/metrics.go`
-
-**Files Created:**
-- `internal/ssh/knownhosts/manager_test.go`
-
-**Tests:** 7 new tests, all passing
-**Status:** ✅ Deployed
-
----
-
-#### 8. Insufficient Capability Separation ✅ FIXED
-
-**Finding:**
-Any UID in `allowed_peer_uids` could call privileged methods:
-- No separation between read-only and admin capabilities
-- If another service's UID in allowlist, inherits full control
-
-**CVSS Score:** 6.5 (Medium)
-**Attack Complexity:** Low
-**Impact:** Privilege escalation
-
-**Remediation:**
-- Implemented capability-based authorization system
-- Three capability levels: `read`, `write`, `admin`
-- Per-UID capability assignment
-- Privileged methods require `admin` capability
-- Backwards compatible with legacy config
-
-**Configuration:**
-```yaml
-allowed_peers:
- - uid: 0
- capabilities: [read, write, admin] # Root gets all
- - uid: 1000
- capabilities: [read] # Docker: read-only
- - uid: 1001
- capabilities: [read, write] # Can call temps but not key distribution
-```
-
-**Files Created:**
-- `cmd/pulse-sensor-proxy/capabilities.go`
-
-**Files Modified:**
-- `cmd/pulse-sensor-proxy/config.go`
-- `cmd/pulse-sensor-proxy/auth.go`
-- `cmd/pulse-sensor-proxy/main.go`
-
-**Tests:** 1 new test, passing
-**Status:** ✅ Deployed
-
----
-
-### LOW SEVERITY
-
-#### 9. Missing Systemd Hardening ✅ FIXED
-
-**Finding:**
-Additional systemd hardening options available but not enabled:
-- `MemoryDenyWriteExecute` (prevents RWX memory)
-- `RestrictRealtime` (denies realtime scheduling)
-- `ProtectHostname` (hostname protection)
-- `ProtectKernelLogs` (kernel log protection)
-- `SystemCallArchitectures` (native only)
-
-**CVSS Score:** 3.1 (Low)
-**Attack Complexity:** High
-**Impact:** Defense in depth
-
-**Remediation:**
-- Added all missing hardening directives
-- Verified compatibility with Go runtime
-- Updated systemd unit file
-
-**Files Modified:**
-- `scripts/pulse-sensor-proxy.service`
-
-**Status:** ✅ Deployed
-
----
-
-## New Security Features
-
-### Enhanced Metrics
-
-All new security features include Prometheus metrics:
-
-| Metric | Purpose |
-|--------|---------|
-| `pulse_proxy_node_validation_failures_total{node, reason}` | SSRF attempt detection |
-| `pulse_proxy_read_timeouts_total` | Connection DoS detection |
-| `pulse_proxy_write_timeouts_total` | Write timeout tracking |
-| `pulse_proxy_limiter_rejections_total{peer, reason}` | Rate limit monitoring |
-| `pulse_proxy_limiter_penalties_total{peer, reason}` | Penalty tracking |
-| `pulse_proxy_global_concurrency_inflight` | Concurrency monitoring |
-| `pulse_proxy_ssh_output_oversized_total{node}` | Output size violations |
-| `pulse_proxy_hostkey_changes_total{node}` | Fingerprint changes |
-
-### Improved Logging
-
-- Node validation failures: WARN with "potential SSRF attempt"
-- Read timeouts: WARN with "slow client or attack"
-- Fingerprint changes: ERROR level
-- All events include correlation IDs
-- Peer labels show "range:X-Y" for containers
-
-### Configuration Flexibility
-
-All features configurable via:
-- YAML config file (`/etc/pulse-sensor-proxy/config.yaml`)
-- Environment variables (e.g., `PULSE_SENSOR_PROXY_READ_TIMEOUT`)
-- Command-line flags
-
----
-
-## Testing Summary
-
-### Test Coverage
-
-**Total New Tests:** 17
-**All Tests Passing:** ✅ Yes
-
-**Test Breakdown:**
-- Node validation: 4 tests
-- Authorization: 3 tests
-- Rate limiting: 1 test
-- SSH output limits: 1 test
-- Host key management: 7 tests
-- Capability system: 1 test
-
-### Build Verification
-
-```bash
-✅ All tests pass: go test ./cmd/pulse-sensor-proxy ./internal/ssh/knownhosts
-✅ Binary builds: ./pulse-sensor-proxy-hardened
-✅ Configuration validated
-✅ Systemd unit verified
-```
-
----
-
-## Deployment Guide
-
-### Breaking Changes
-
-1. **Socket mounts MUST be changed to `:ro`** (security fix)
- ```yaml
- # OLD:
- - /run/pulse-sensor-proxy:/run/pulse-sensor-proxy:rw
-
- # NEW:
- - /run/pulse-sensor-proxy:/run/pulse-sensor-proxy:ro
- ```
-
-2. **Containers with multiple users now share rate limits** (security fix, prevents bypass)
-
-### Migration Steps
-
-1. **Update Configuration**
-
- Create `/etc/pulse-sensor-proxy/config.yaml`:
- ```yaml
- # Node allowlist (prevents SSRF)
- allowed_nodes:
- - "10.0.0.0/24" # Your cluster network
- strict_node_validation: true
-
- # Timeouts (prevents DoS)
- read_timeout: 5s
- write_timeout: 10s
-
- # SSH output limits
- max_ssh_output_bytes: 1048576 # 1MB
-
- # Host key management
- require_proxmox_hostkeys: false # Set true for strict mode
-
- # Capability-based authorization
- allowed_peers:
- - uid: 0
- capabilities: [read, write, admin]
- - uid: 1000
- capabilities: [read] # Docker containers: read-only
- ```
-
-2. **Update Socket Mounts**
-
- Docker:
- ```bash
- # Edit docker-compose.yml
- sed -i 's/:rw$/:ro/g' docker-compose.yml
- docker compose down && docker compose up -d
- ```
-
- LXC:
- ```bash
- # Mounts created by install script are already correct
- # Verify: pct config | grep mp
- ```
-
-3. **Restart Proxy**
-
- ```bash
- systemctl restart pulse-sensor-proxy
- ```
-
-4. **Update Monitoring**
-
- Add Prometheus alerts:
- ```yaml
- groups:
- - name: pulse-sensor-proxy-security
- rules:
- # SSRF attempts
- - alert: PulseSensorSSRFAttempt
- expr: rate(pulse_proxy_node_validation_failures_total[5m]) > 0
- labels:
- severity: warning
- annotations:
- summary: "SSRF attempt blocked on {{ $labels.instance }}"
-
- # Read timeout attacks
- - alert: PulseSensorReadTimeouts
- expr: rate(pulse_proxy_read_timeouts_total[5m]) > 1
- labels:
- severity: warning
- annotations:
- summary: "High read timeout rate on {{ $labels.instance }}"
-
- # Fingerprint changes
- - alert: PulseSensorHostKeyChange
- expr: increase(pulse_proxy_hostkey_changes_total[1h]) > 0
- labels:
- severity: critical
- annotations:
- summary: "SSH host key changed for {{ $labels.node }}"
- ```
-
-### Backwards Compatibility
-
-**Preserved:**
-- Empty `allowed_nodes` + Proxmox host = auto-validate cluster
-- Empty `allowed_nodes` + non-Proxmox = allow all (legacy)
-- Host UID rate limiting unchanged
-- Legacy `allowed_peer_uids` format still works (grants all capabilities)
-
-**Changed (intentionally):**
-- Socket mounts now `:ro` (security fix)
-- Container UIDs now share rate limits (security fix)
-
----
-
-## Security Posture Comparison
-
-| Attack Vector | Before | After | Improvement |
-|---------------|--------|-------|-------------|
-| **SSRF** | ❌ Trivially exploitable | ✅ Eliminated | Node validation |
-| **Connection DoS** | ❌ 4 UIDs = full starvation | ✅ Eliminated | Read deadlines |
-| **Multi-UID Bypass** | ❌ 100+ UIDs available | ✅ Eliminated | Range-based limiting |
-| **Socket Tampering** | ❌ Container can MITM | ✅ Eliminated | Read-only mount |
-| **GID Policy Bypass** | ❌ Silently ignored | ✅ Enforced | GID checking |
-| **Memory Exhaustion** | ❌ Unbounded SSH output | ✅ Mitigated | Output limits |
-| **MITM Attacks** | ⚠️ TOFU vulnerable | ✅ Improved | Proxmox key seeding |
-| **Privilege Escalation** | ⚠️ UID = full admin | ✅ Controlled | Capability system |
-| **Process Exploitation** | ⚠️ Basic hardening | ✅ Hardened | Systemd directives |
-
----
-
-## Risk Assessment
-
-### Before Audit
-
-**Critical Risks:**
-- Container compromise → Full internal network SSRF
-- Container compromise → Trivial service DoS
-- Container compromise → Rate limit bypass
-- Container compromise → Proxy MITM
-
-**Overall Risk Level:** HIGH
-
-### After Remediation
-
-**Residual Risks:**
-- Proxy binary compromise → SSH key access (unavoidable given architecture)
-- Zero-day in Go runtime or dependencies
-- Social engineering / operator error
-
-**Overall Risk Level:** LOW
-
-**Risk Reduction:** 85%+ reduction in exploitable attack surface
-
----
-
-## Recommendations for Production
-
-### Immediate Actions
-
-1. ✅ Deploy all security fixes (all completed)
-2. ✅ Update socket mounts to read-only
-3. ✅ Configure node allowlists
-4. ✅ Enable monitoring/alerting
-
-### Ongoing Security
-
-1. **Regular audits** - Annual security reviews
-2. **Dependency updates** - Monitor Go/SSH library security advisories
-3. **Log monitoring** - Watch for validation failures and timeouts
-4. **Key rotation** - Use existing rotation script quarterly
-5. **Incident response** - Document and practice response procedures
-
-### Future Enhancements
-
-1. **Strict host key mode** - Require administrator approval for new fingerprints
-2. **TLS for metrics** - Encrypt metrics endpoint
-3. **Advanced rate limiting** - Adaptive throttling based on behavior
-4. **Extended audit logging** - Structured audit logs with retention
-
----
-
-## Conclusion
-
-The pulse-sensor-proxy architecture underwent comprehensive security hardening, addressing all identified vulnerabilities. The system now demonstrates:
-
-- **Defense in Depth:** Multiple layers of security controls
-- **Least Privilege:** Capability-based authorization
-- **Attack Resilience:** DoS-resistant design
-- **SSRF Prevention:** Complete node validation
-- **Container Isolation:** Read-only mounts, range-based limiting
-- **Monitoring:** Comprehensive security telemetry
-
-**Final Security Grade:** A (Excellent)
-
-The proxy is now production-ready for security-sensitive deployments.
-
----
-
-## References
-
-- **Security Changelog:** `docs/SECURITY_CHANGELOG.md`
-- **Hardening Guide:** `docs/PULSE_SENSOR_PROXY_HARDENING.md`
-- **Security Architecture:** `docs/TEMPERATURE_MONITORING_SECURITY.md`
-- **Configuration Guide:** `docs/CONFIGURATION.md`
-
----
-
-## Audit Team
-
-**Lead Auditor:** Claude (Anthropic Sonnet 4.5)
-**Implementation:** OpenAI Codex
-**Methodology:** Collaborative adversarial analysis
-
-**Audit Duration:** 2025-11-07 (single day comprehensive audit)
-**Lines of Code Reviewed:** ~5,000
-**Security Issues Found:** 9
-**Issues Remediated:** 9 (100%)
-
----
-
-**For security concerns or questions:**
-https://github.com/rcourtman/Pulse/issues
diff --git a/docs/SECURITY_AUDIT_2025-12-18.md b/docs/SECURITY_AUDIT_2025-12-18.md
deleted file mode 100644
index 685d56b..0000000
--- a/docs/SECURITY_AUDIT_2025-12-18.md
+++ /dev/null
@@ -1,375 +0,0 @@
-# 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
diff --git a/docs/SECURITY_CHANGELOG.md b/docs/SECURITY_CHANGELOG.md
index ce694ee..f37b151 100644
--- a/docs/SECURITY_CHANGELOG.md
+++ b/docs/SECURITY_CHANGELOG.md
@@ -374,8 +374,6 @@ go build ./cmd/pulse-sensor-proxy
### References
-- **Audit Report:** `docs/SECURITY_AUDIT_2025-11-07.md`
-- **Audit Report:** `docs/SECURITY_AUDIT_2025-12-18.md`
- **Temperature Monitoring Overview:** `docs/security/TEMPERATURE_MONITORING.md`
- **Sensor Proxy Hardening:** `docs/security/SENSOR_PROXY_HARDENING.md`
diff --git a/docs/TEMPERATURE_MONITORING.md b/docs/TEMPERATURE_MONITORING.md
index 190bd00..9edeec2 100644
--- a/docs/TEMPERATURE_MONITORING.md
+++ b/docs/TEMPERATURE_MONITORING.md
@@ -2,7 +2,7 @@
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.
+> **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. In v5, legacy sensor-proxy endpoints are disabled by default unless `PULSE_ENABLE_SENSOR_PROXY=true` is set on the Pulse server.
## Recommended: Pulse Agent
@@ -15,6 +15,23 @@ curl -fsSL http://:7655/install.sh | \
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.
+## Migration: pulse-sensor-proxy → pulse-agent
+
+If you already deployed `pulse-sensor-proxy`, migrate to the agent to avoid proxy maintenance and remove SSH-from-container complexity:
+
+1. Install `lm-sensors` on each Proxmox host (if not already): `apt install lm-sensors && sensors-detect`
+2. Install the agent on each Proxmox host:
+ ```bash
+ curl -fsSL http://:7655/install.sh | \
+ bash -s -- --url http://:7655 --token --enable-proxmox
+ ```
+3. Confirm temperatures are updating in the dashboard.
+4. Disable the proxy service on hosts where it was installed:
+ ```bash
+ sudo systemctl disable --now pulse-sensor-proxy
+ ```
+5. If your Pulse container had a proxy socket mount, remove the mount and remove `PULSE_SENSOR_PROXY_SOCKET` from the Pulse `.env` (for example `/data/.env` in Docker) before restarting Pulse.
+
## 🚀 Quick Start
### 1. Install the agent on Proxmox hosts
diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md
index b9f90ca..22eed8d 100644
--- a/docs/TROUBLESHOOTING.md
+++ b/docs/TROUBLESHOOTING.md
@@ -78,7 +78,11 @@ sudo pulse bootstrap-token
### Correlate Logs with Requests
Every API response has an `X-Request-ID` header. Use it to find the exact log entry:
```bash
-grep "request_id=abc123" /var/log/pulse/pulse.log
+# systemd / Proxmox LXC
+journalctl -u pulse --no-pager | grep "request_id=abc123"
+
+# Docker
+docker logs pulse 2>&1 | grep "request_id=abc123"
```
### Check Permissions (Proxmox)
diff --git a/docs/UNIFIED_AGENT.md b/docs/UNIFIED_AGENT.md
index 09f9c74..3c1da64 100644
--- a/docs/UNIFIED_AGENT.md
+++ b/docs/UNIFIED_AGENT.md
@@ -2,6 +2,8 @@
The unified agent (`pulse-agent`) combines host, Docker, and Kubernetes monitoring into a single binary. It replaces the separate `pulse-host-agent` and `pulse-docker-agent` for simpler deployment and management.
+> Note: In v5, temperature monitoring should be done via `pulse-agent --enable-proxmox`. `pulse-sensor-proxy` is deprecated and retained only for existing installs during the migration window.
+
## Quick Start
Generate an installation command in the UI:
diff --git a/docs/development/MOCK_MODE.md b/docs/development/MOCK_MODE.md
deleted file mode 100644
index 4d4e2ab..0000000
--- a/docs/development/MOCK_MODE.md
+++ /dev/null
@@ -1,39 +0,0 @@
-# 🧪 Mock Mode Development
-
-Develop Pulse without real infrastructure using the mock data pipeline.
-
-## 🚀 Quick Start
-
-```bash
-# Start dev stack
-./scripts/hot-dev.sh
-
-# Toggle mock mode
-npm run mock:on # Enable
-npm run mock:off # Disable
-npm run mock:status # Check status
-```
-
-## ⚙️ Configuration
-Edit `mock.env` (or `mock.env.local` for overrides):
-
-| Variable | Default | Description |
-| :--- | :--- | :--- |
-| `PULSE_MOCK_MODE` | `false` | Enable mock mode. |
-| `PULSE_MOCK_NODES` | `7` | Number of synthetic nodes. |
-| `PULSE_MOCK_VMS_PER_NODE` | `5` | VMs per node. |
-| `PULSE_MOCK_LXCS_PER_NODE` | `8` | Containers per node. |
-| `PULSE_MOCK_RANDOM_METRICS` | `true` | Jitter metrics. |
-| `PULSE_MOCK_STOPPED_PERCENT` | `20` | % of offline guests. |
-| `PULSE_MOCK_TRENDS_SEED_DURATION` | `1h` | Pre-seed backend chart history (improves demo “Trends” immediately). |
-| `PULSE_MOCK_TRENDS_SAMPLE_INTERVAL` | `30s` | Backend chart sampling interval while in mock mode. |
-
-## ℹ️ How it Works
-* **Data**: Swaps `PULSE_DATA_DIR` to `/opt/pulse/tmp/mock-data`.
-* **Restart**: Backend restarts automatically; Frontend hot-reloads.
-* **Reset**: To regenerate data, delete `/opt/pulse/tmp/mock-data` and toggle mock mode on.
-
-## ⚠️ Limitations
-* **Happy Path**: Focuses on standard flows; use real infrastructure for complex edge cases.
-* **Webhooks**: Synthetic payloads only.
-* **Encryption**: Uses local crypto stack (not a sandbox for auth).
diff --git a/docs/security/TEMPERATURE_MONITORING.md b/docs/security/TEMPERATURE_MONITORING.md
index e5e239c..ea83644 100644
--- a/docs/security/TEMPERATURE_MONITORING.md
+++ b/docs/security/TEMPERATURE_MONITORING.md
@@ -5,6 +5,8 @@ This page describes the recommended v5 approach for temperature monitoring and t
For the full sensor-proxy setup guide (socket mounts, HTTP mode, troubleshooting), see:
`docs/TEMPERATURE_MONITORING.md`.
+> **Deprecation notice (v5):** `pulse-sensor-proxy` is deprecated and not recommended for new deployments. Use `pulse-agent --enable-proxmox` for temperature monitoring. The sensor-proxy section below is retained for existing installations during the migration window. In v5, legacy sensor-proxy endpoints are disabled by default unless `PULSE_ENABLE_SENSOR_PROXY=true` is set on the Pulse server.
+
## Recommended: Pulse Agent
The simplest and most feature-rich method is installing the Pulse agent on your Proxmox nodes:
diff --git a/frontend-modern/src/components/Settings/Settings.tsx b/frontend-modern/src/components/Settings/Settings.tsx
index 859f84c..588b0ed 100644
--- a/frontend-modern/src/components/Settings/Settings.tsx
+++ b/frontend-modern/src/components/Settings/Settings.tsx
@@ -15,7 +15,6 @@ import { showSuccess, showError, showWarning } from '@/utils/toast';
import { logger } from '@/utils/logger';
import {
apiFetch,
- apiFetchJSON,
clearApiToken as clearApiClientToken,
getApiToken as getApiClientToken,
setApiToken as setApiClientToken,
@@ -980,9 +979,16 @@ const Settings: Component = (props) => {
const refreshHostProxyStatus = async (notify = false) => {
try {
- const status = (await apiFetchJSON(
- '/api/temperature-proxy/host-status',
- )) as HostProxyStatusResponse;
+ const response = await apiFetch('/api/temperature-proxy/host-status');
+ if (response.status === 410) {
+ // pulse-sensor-proxy is deprecated and disabled by default in v5
+ setHostProxyStatus(null);
+ return;
+ }
+ if (!response.ok) {
+ throw new Error(`Request failed with status ${response.status}`);
+ }
+ const status = (await response.json()) as HostProxyStatusResponse;
setHostProxyStatus(status);
if (notify) {
showSuccess('Host proxy status refreshed', undefined, 2000);
diff --git a/internal/api/diagnostics.go b/internal/api/diagnostics.go
index 94b59c5..c15ff72 100644
--- a/internal/api/diagnostics.go
+++ b/internal/api/diagnostics.go
@@ -443,7 +443,11 @@ func (r *Router) computeDiagnostics(ctx context.Context) DiagnosticsInfo {
socketHostState = r.monitor.SocketProxyHostDiagnostics()
}
- diag.TemperatureProxy = buildTemperatureProxyDiagnostic(r.config, proxySync, socketHostState)
+ if r.config != nil && !r.config.EnableSensorProxy {
+ diag.TemperatureProxy = nil
+ } else {
+ diag.TemperatureProxy = buildTemperatureProxyDiagnostic(r.config, proxySync, socketHostState)
+ }
diag.APITokens = buildAPITokenDiagnostic(r.config, r.monitor)
// Test each configured node
diff --git a/internal/api/router.go b/internal/api/router.go
index a8a756f..eca4ad2 100644
--- a/internal/api/router.go
+++ b/internal/api/router.go
@@ -203,11 +203,11 @@ func (r *Router) setupRoutes() {
r.mux.HandleFunc("/api/agents/host/report", RequireAuth(r.config, RequireScope(config.ScopeHostReport, r.hostAgentHandlers.HandleReport)))
r.mux.HandleFunc("/api/agents/host/lookup", RequireAuth(r.config, RequireScope(config.ScopeHostReport, r.hostAgentHandlers.HandleLookup)))
r.mux.HandleFunc("/api/agents/host/", RequireAdmin(r.config, RequireScope(config.ScopeHostManage, r.hostAgentHandlers.HandleDeleteHost)))
- r.mux.HandleFunc("/api/temperature-proxy/register", r.temperatureProxyHandlers.HandleRegister)
- r.mux.HandleFunc("/api/temperature-proxy/authorized-nodes", r.temperatureProxyHandlers.HandleAuthorizedNodes)
- r.mux.HandleFunc("/api/temperature-proxy/unregister", RequireAdmin(r.config, r.temperatureProxyHandlers.HandleUnregister))
- r.mux.HandleFunc("/api/temperature-proxy/install-command", RequireAdmin(r.config, RequireScope(config.ScopeSettingsWrite, r.handleTemperatureProxyInstallCommand)))
- r.mux.HandleFunc("/api/temperature-proxy/host-status", RequireAdmin(r.config, RequireScope(config.ScopeSettingsRead, r.handleHostProxyStatus)))
+ r.mux.HandleFunc("/api/temperature-proxy/register", r.requireSensorProxyEnabled(r.temperatureProxyHandlers.HandleRegister))
+ r.mux.HandleFunc("/api/temperature-proxy/authorized-nodes", r.requireSensorProxyEnabled(r.temperatureProxyHandlers.HandleAuthorizedNodes))
+ r.mux.HandleFunc("/api/temperature-proxy/unregister", r.requireSensorProxyEnabled(RequireAdmin(r.config, r.temperatureProxyHandlers.HandleUnregister)))
+ r.mux.HandleFunc("/api/temperature-proxy/install-command", r.requireSensorProxyEnabled(RequireAdmin(r.config, RequireScope(config.ScopeSettingsWrite, r.handleTemperatureProxyInstallCommand))))
+ r.mux.HandleFunc("/api/temperature-proxy/host-status", r.requireSensorProxyEnabled(RequireAdmin(r.config, RequireScope(config.ScopeSettingsRead, r.handleHostProxyStatus))))
r.mux.HandleFunc("/api/agents/docker/commands/", RequireAuth(r.config, RequireScope(config.ScopeDockerReport, r.dockerAgentHandlers.HandleCommandAck)))
r.mux.HandleFunc("/api/agents/docker/hosts/", RequireAdmin(r.config, RequireScope(config.ScopeDockerManage, r.dockerAgentHandlers.HandleDockerHostActions)))
r.mux.HandleFunc("/api/agents/kubernetes/clusters/", RequireAdmin(r.config, RequireScope(config.ScopeKubernetesManage, r.kubernetesAgentHandlers.HandleClusterActions)))
@@ -218,12 +218,12 @@ func (r *Router) setupRoutes() {
r.mux.HandleFunc("/api/metrics-store/stats", RequireAuth(r.config, RequireScope(config.ScopeMonitoringRead, r.handleMetricsStoreStats)))
r.mux.HandleFunc("/api/metrics-store/history", RequireAuth(r.config, RequireScope(config.ScopeMonitoringRead, r.handleMetricsHistory)))
r.mux.HandleFunc("/api/diagnostics", RequireAuth(r.config, r.handleDiagnostics))
- r.mux.HandleFunc("/api/diagnostics/temperature-proxy/register-nodes", RequireAdmin(r.config, RequireScope(config.ScopeSettingsWrite, r.handleDiagnosticsRegisterProxyNodes)))
+ r.mux.HandleFunc("/api/diagnostics/temperature-proxy/register-nodes", r.requireSensorProxyEnabled(RequireAdmin(r.config, RequireScope(config.ScopeSettingsWrite, r.handleDiagnosticsRegisterProxyNodes))))
r.mux.HandleFunc("/api/diagnostics/docker/prepare-token", RequireAdmin(r.config, RequireScope(config.ScopeSettingsWrite, r.handleDiagnosticsDockerPrepareToken)))
- r.mux.HandleFunc("/api/install/pulse-sensor-proxy", r.handleDownloadPulseSensorProxy)
- r.mux.HandleFunc("/api/install/install-sensor-proxy.sh", r.handleDownloadInstallerScript)
- r.mux.HandleFunc("/api/install/migrate-sensor-proxy-control-plane.sh", r.handleDownloadMigrationScript)
- r.mux.HandleFunc("/api/install/migrate-temperature-proxy.sh", r.handleDownloadTemperatureProxyMigrationScript)
+ r.mux.HandleFunc("/api/install/pulse-sensor-proxy", r.requireSensorProxyEnabled(r.handleDownloadPulseSensorProxy))
+ r.mux.HandleFunc("/api/install/install-sensor-proxy.sh", r.requireSensorProxyEnabled(r.handleDownloadInstallerScript))
+ r.mux.HandleFunc("/api/install/migrate-sensor-proxy-control-plane.sh", r.requireSensorProxyEnabled(r.handleDownloadMigrationScript))
+ r.mux.HandleFunc("/api/install/migrate-temperature-proxy.sh", r.requireSensorProxyEnabled(r.handleDownloadTemperatureProxyMigrationScript))
r.mux.HandleFunc("/api/install/install-docker.sh", r.handleDownloadDockerInstallerScript)
r.mux.HandleFunc("/api/install/install.sh", r.handleDownloadUnifiedInstallScript)
r.mux.HandleFunc("/api/install/install.ps1", r.handleDownloadUnifiedInstallScriptPS)
@@ -288,6 +288,30 @@ func (r *Router) setupRoutes() {
}
}))
+ // Docker host metadata routes (for managing Docker host custom URLs, e.g., Portainer links)
+ r.mux.HandleFunc("/api/docker/hosts/metadata", RequireAuth(r.config, RequireScope(config.ScopeMonitoringRead, dockerMetadataHandler.HandleGetHostMetadata)))
+ r.mux.HandleFunc("/api/docker/hosts/metadata/", RequireAuth(r.config, func(w http.ResponseWriter, req *http.Request) {
+ switch req.Method {
+ case http.MethodGet:
+ if !ensureScope(w, req, config.ScopeMonitoringRead) {
+ return
+ }
+ dockerMetadataHandler.HandleGetHostMetadata(w, req)
+ case http.MethodPut, http.MethodPost:
+ if !ensureScope(w, req, config.ScopeMonitoringWrite) {
+ return
+ }
+ dockerMetadataHandler.HandleUpdateHostMetadata(w, req)
+ case http.MethodDelete:
+ if !ensureScope(w, req, config.ScopeMonitoringWrite) {
+ return
+ }
+ dockerMetadataHandler.HandleDeleteHostMetadata(w, req)
+ default:
+ http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+ }
+ }))
+
// Host metadata routes
r.mux.HandleFunc("/api/hosts/metadata", RequireAuth(r.config, RequireScope(config.ScopeMonitoringRead, hostMetadataHandler.HandleGetMetadata)))
r.mux.HandleFunc("/api/hosts/metadata/", RequireAuth(r.config, func(w http.ResponseWriter, req *http.Request) {
@@ -1042,7 +1066,7 @@ func (r *Router) setupRoutes() {
r.mux.HandleFunc("/api/system/settings/update", RequireAdmin(r.config, RequireScope(config.ScopeSettingsWrite, r.systemSettingsHandler.HandleUpdateSystemSettings)))
r.mux.HandleFunc("/api/system/ssh-config", r.handleSSHConfig)
r.mux.HandleFunc("/api/system/verify-temperature-ssh", r.handleVerifyTemperatureSSH)
- r.mux.HandleFunc("/api/system/proxy-public-key", r.handleProxyPublicKey)
+ r.mux.HandleFunc("/api/system/proxy-public-key", r.requireSensorProxyEnabled(r.handleProxyPublicKey))
// Old API token endpoints removed - now using /api/security/regenerate-token
// Agent execution server for AI tool use
diff --git a/internal/api/sensor_proxy_gate.go b/internal/api/sensor_proxy_gate.go
new file mode 100644
index 0000000..1761a02
--- /dev/null
+++ b/internal/api/sensor_proxy_gate.go
@@ -0,0 +1,28 @@
+package api
+
+import "net/http"
+
+func (r *Router) isSensorProxyEnabled() bool {
+ return r != nil && r.config != nil && r.config.EnableSensorProxy
+}
+
+func (r *Router) requireSensorProxyEnabled(next http.HandlerFunc) http.HandlerFunc {
+ return func(w http.ResponseWriter, req *http.Request) {
+ if r.isSensorProxyEnabled() {
+ next(w, req)
+ return
+ }
+
+ w.Header().Set("Warning", `299 - "pulse-sensor-proxy is deprecated and disabled by default in v5"`)
+ writeErrorResponse(
+ w,
+ http.StatusGone,
+ "sensor_proxy_disabled",
+ "pulse-sensor-proxy is deprecated and disabled by default in v5",
+ map[string]string{
+ "migration": "Use pulse-agent --enable-proxmox for temperature monitoring.",
+ "enable_env": "Set PULSE_ENABLE_SENSOR_PROXY=true (unsupported legacy) and restart Pulse to re-enable these endpoints.",
+ },
+ )
+ }
+}
diff --git a/internal/config/config.go b/internal/config/config.go
index 8ccff30..90208bd 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -99,6 +99,7 @@ type Config struct {
BackupPollingInterval time.Duration `envconfig:"BACKUP_POLLING_INTERVAL"`
EnableBackupPolling bool `envconfig:"ENABLE_BACKUP_POLLING" default:"true"`
TemperatureMonitoringEnabled bool `json:"temperatureMonitoringEnabled"`
+ EnableSensorProxy bool `envconfig:"PULSE_ENABLE_SENSOR_PROXY" default:"false" json:"-"` // Legacy pulse-sensor-proxy support (deprecated, opt-in)
WebhookBatchDelay time.Duration `envconfig:"WEBHOOK_BATCH_DELAY" default:"10s"`
AdaptivePollingEnabled bool `envconfig:"ADAPTIVE_POLLING_ENABLED" default:"false"`
AdaptivePollingBaseInterval time.Duration `envconfig:"ADAPTIVE_POLLING_BASE_INTERVAL" default:"10s"`
@@ -118,7 +119,6 @@ type Config struct {
MetricsRetentionHourlyDays int `json:"metricsRetentionHourlyDays"` // Hourly averages, default: 7 days
MetricsRetentionDailyDays int `json:"metricsRetentionDailyDays"` // Daily averages, default: 90 days
-
// Logging settings
LogLevel string `envconfig:"LOG_LEVEL" default:"info"`
LogFormat string `envconfig:"LOG_FORMAT" default:"auto"` // "json", "console", or "auto"
@@ -580,6 +580,7 @@ func Load() (*Config, error) {
DiscoveryEnabled: false,
DiscoverySubnet: "auto",
TemperatureMonitoringEnabled: true,
+ EnableSensorProxy: false,
EnvOverrides: make(map[string]bool),
OIDC: NewOIDCConfig(),
// Metrics retention defaults (tiered)
@@ -819,6 +820,22 @@ func Load() (*Config, error) {
}
}
+ if enabledStr := utils.GetenvTrim("PULSE_ENABLE_SENSOR_PROXY"); enabledStr != "" {
+ if enabled, err := strconv.ParseBool(enabledStr); err == nil {
+ cfg.EnableSensorProxy = enabled
+ cfg.EnvOverrides["PULSE_ENABLE_SENSOR_PROXY"] = true
+ if enabled {
+ log.Warn().Msg("Legacy pulse-sensor-proxy support enabled via PULSE_ENABLE_SENSOR_PROXY (deprecated, unsupported)")
+ } else {
+ log.Info().Msg("Legacy pulse-sensor-proxy support disabled via PULSE_ENABLE_SENSOR_PROXY")
+ }
+ } else {
+ log.Warn().
+ Str("value", enabledStr).
+ Msg("Invalid PULSE_ENABLE_SENSOR_PROXY value, ignoring")
+ }
+ }
+
if hideLocalLoginStr := utils.GetenvTrim("PULSE_AUTH_HIDE_LOCAL_LOGIN"); hideLocalLoginStr != "" {
if hide, err := strconv.ParseBool(hideLocalLoginStr); err == nil {
cfg.HideLocalLogin = hide
diff --git a/scripts/generate-release-notes.sh b/scripts/generate-release-notes.sh
index 95c2581..03e5630 100755
--- a/scripts/generate-release-notes.sh
+++ b/scripts/generate-release-notes.sh
@@ -92,7 +92,7 @@ docker pull rcourtman/pulse:v${VERSION}
docker stop pulse && docker rm pulse
docker run -d --name pulse \\
--restart unless-stopped \\
- -p 7655:7655 -p 7656:7656 \\
+ -p 7655:7655 \\
-v /opt/pulse/data:/data \\
rcourtman/pulse:v${VERSION}
\`\`\`
@@ -322,7 +322,7 @@ docker pull rcourtman/pulse:v${VERSION}
docker stop pulse && docker rm pulse
docker run -d --name pulse \
--restart unless-stopped \
- -p 7655:7655 -p 7656:7656 \
+ -p 7655:7655 \
-v /opt/pulse/data:/data \
rcourtman/pulse:v${VERSION}
```
diff --git a/scripts/install-host-agent.ps1 b/scripts/install-host-agent.ps1
index 80423be..25282c7 100644
--- a/scripts/install-host-agent.ps1
+++ b/scripts/install-host-agent.ps1
@@ -15,9 +15,9 @@
# └─────────────────────────────────────────────────────────────────────────────┘
#
# Usage:
-# iwr -useb http://pulse-server:7656/install-host-agent.ps1 | iex
+# iwr -useb http://pulse-server:7655/install-host-agent.ps1 | iex
# OR with parameters:
-# $url = "http://pulse-server:7656"; $token = "your-token"; iwr -useb "$url/install-host-agent.ps1" | iex
+# $url = "http://pulse-server:7655"; $token = "your-token"; iwr -useb "$url/install-host-agent.ps1" | iex
#
# Parameters can be passed via environment variables or script parameters
@@ -248,7 +248,7 @@ if (-not $isAdmin) {
# Interactive prompts if parameters not provided
if (-not $PulseUrl) {
- $PulseUrl = Read-Host "Enter Pulse server URL (e.g., http://pulse.example.com:7656)"
+ $PulseUrl = Read-Host "Enter Pulse server URL (e.g., http://pulse.example.com:7655)"
}
$PulseUrl = $PulseUrl.TrimEnd('/')
diff --git a/scripts/uninstall-host-agent.ps1 b/scripts/uninstall-host-agent.ps1
index caecf29..c355dc4 100644
--- a/scripts/uninstall-host-agent.ps1
+++ b/scripts/uninstall-host-agent.ps1
@@ -1,7 +1,7 @@
# Pulse Host Agent Uninstallation Script for Windows
#
# Usage:
-# iwr -useb http://pulse-server:7656/uninstall-host-agent.ps1 | iex
+# iwr -useb http://pulse-server:7655/uninstall-host-agent.ps1 | iex
#
param(