mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 02:25:56 +00:00
742 lines
37 KiB
Markdown
742 lines
37 KiB
Markdown
# ⚙️ Configuration Guide
|
|
|
|
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** |
|
|
| `.audit-signing.key` | Audit log signing key (Pro/legacy Pro+/Cloud, encrypted) | 🔒 **Sensitive** |
|
|
| `system.json` | General Settings | 📝 Standard |
|
|
| `nodes.enc` | Node Credentials | 🔒 **Encrypted** (AES-256-GCM) |
|
|
| `alerts.json` | Alert Rules | 📝 Standard |
|
|
| `email.enc` | SMTP settings | 🔒 **Encrypted** |
|
|
| `webhooks.enc` | Webhook URLs + headers | 🔒 **Encrypted** |
|
|
| `apprise.enc` | Apprise notification config | 🔒 **Encrypted** |
|
|
| `oidc.enc` | OIDC provider config | 🔒 **Encrypted** |
|
|
| `sso.enc` | SAML/SSO provider config | 🔒 **Encrypted** |
|
|
| `api_tokens.json` | API token records (hashed) | 🔒 **Sensitive** |
|
|
| `ai.enc` | AI settings and credentials | 🔒 **Encrypted** |
|
|
| `ai_findings.json` | AI Patrol findings | 📝 Standard |
|
|
| `ai_patrol_runs.json` | AI Patrol run history | 📝 Standard |
|
|
| `ai_usage_history.json` | AI usage history | 📝 Standard |
|
|
| `ai_chat_sessions.json` | Legacy AI chat sessions (UI sync) | 📝 Standard |
|
|
| `license.enc` | Relay/Pro/legacy Pro+/Cloud license key | 🔒 **Encrypted** |
|
|
| `report_schedules.json` | Scheduled report definitions, recipients, and last-run metadata | 🔒 **Sensitive** (encrypted when data-dir encryption is enabled) |
|
|
| `host_metadata.json` | Host notes, tags, and AI command overrides | 📝 Standard |
|
|
| `docker_metadata.json` | Docker metadata cache | 📝 Standard |
|
|
| `guest_metadata.json` | Guest notes and metadata | 📝 Standard |
|
|
| `agent_profiles.json` | Agent configuration profiles (Pro/legacy Pro+/Cloud) | 📝 Standard |
|
|
| `agent_profile_assignments.json` | Agent profile assignments (Pro/legacy Pro+/Cloud) | 📝 Standard |
|
|
| `profile-versions.json` | Agent profile version history (Pro/legacy Pro+/Cloud) | 📝 Standard |
|
|
| `profile-deployments.json` | Agent profile deployment status (Pro/legacy Pro+/Cloud) | 📝 Standard |
|
|
| `profile-changelog.json` | Agent profile change log (Pro/legacy Pro+/Cloud) | 📝 Standard |
|
|
| `recovery_tokens.json` | Recovery tokens (short-lived) | 🔒 **Sensitive** |
|
|
| `sessions.json` | Persistent sessions (includes OIDC refresh tokens) | 🔒 **Sensitive** |
|
|
| `update-history.jsonl` | Update history log (in-app updates) | 📝 Standard |
|
|
| `metrics.db` | Persistent metrics history (SQLite) | 📝 Standard |
|
|
| `audit.db` | Audit log database (Pro/legacy Pro+/Cloud, SQLite) | 🔒 **Sensitive** |
|
|
| `baselines.json` | AI baseline data for anomaly detection | 📝 Standard |
|
|
| `ai_correlations.json` | AI correlation analysis cache | 📝 Standard |
|
|
| `ai_patterns.json` | AI pattern detection data | 📝 Standard |
|
|
| `ai_remediations.json` | AI remediation suggestions | 📝 Standard |
|
|
| `ai_incidents.json` | AI incident tracking | 📝 Standard |
|
|
| `org.json` | Organization metadata (multi-tenant) | 📝 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:
|
|
- `PULSE_DATA_DIR` sets the base directory for `system.json`, encrypted files, and the bootstrap token.
|
|
- `PULSE_METRICS_DB_PATH` sets only the metrics SQLite database path. Use this
|
|
for tmpfs-backed metrics history without moving secrets or config off the
|
|
persistent data directory.
|
|
|
|
Multi-tenant layout:
|
|
- Default org uses the root data directory for backward compatibility.
|
|
- Non-default orgs store data under `/orgs/<org-id>/`.
|
|
- Migration may create `/orgs/default/` and symlinks in the root data directory.
|
|
|
|
---
|
|
|
|
## 🔐 Authentication (`.env`)
|
|
|
|
This file controls access to Pulse. It is **never** exposed to the UI.
|
|
|
|
```bash
|
|
# /etc/pulse/.env
|
|
|
|
# Admin Credentials (bcrypt hashed; plain text auto-hashes on startup)
|
|
PULSE_AUTH_USER='admin'
|
|
PULSE_AUTH_PASS='$2a$12$...'
|
|
```
|
|
|
|
<details>
|
|
<summary><strong>Advanced: Automated Setup (Skip UI)</strong></summary>
|
|
|
|
You can pre-configure Pulse by setting environment variables. Plain text credentials are automatically hashed on startup.
|
|
|
|
```bash
|
|
# Docker Example
|
|
docker run -d \
|
|
-e PULSE_DEPLOYMENT_METHOD=docker_run \
|
|
-e PULSE_AUTH_USER=admin \
|
|
-e PULSE_AUTH_PASS=secret123 \
|
|
rcourtman/pulse:latest
|
|
```
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>Advanced: OIDC / SSO</strong></summary>
|
|
|
|
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.
|
|
|
|
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 |
|
|
| `OIDC_CLIENT_SECRET` | Client secret |
|
|
| `OIDC_REDIRECT_URL` | Override redirect URL (defaults to `<public-url>/api/oidc/<provider-id>/callback`) |
|
|
| `OIDC_LOGOUT_URL` | Optional logout URL |
|
|
| `OIDC_SCOPES` | Space or comma-separated scopes |
|
|
| `OIDC_USERNAME_CLAIM` | Claim for username (default: `preferred_username`) |
|
|
| `OIDC_EMAIL_CLAIM` | Claim for email (default: `email`) |
|
|
| `OIDC_GROUPS_CLAIM` | Claim for groups |
|
|
| `OIDC_ALLOWED_GROUPS` | Allowed groups (space or comma-separated) |
|
|
| `OIDC_ALLOWED_DOMAINS` | Allowed email domains (space or comma-separated) |
|
|
| `OIDC_ALLOWED_EMAILS` | Allowed emails (space or comma-separated) |
|
|
| `OIDC_GROUP_ROLE_MAPPINGS` | Comma-separated group=role mappings (built-in roles on every plan; custom-role administration requires Pro RBAC) |
|
|
| `OIDC_CA_BUNDLE` | Custom CA bundle path |
|
|
|
|
</details>
|
|
|
|
> **Note**: `API_TOKEN` / `API_TOKENS` in `.env` are legacy and ignored at runtime in v6.
|
|
> Manage API tokens in the UI (`api_tokens.json`) for supported behavior.
|
|
|
|
---
|
|
|
|
## 🖥️ System Settings (`system.json`)
|
|
|
|
Controls runtime behavior like logging, polling intervals, and UI preferences. Legacy port fields in `system.json` are ignored; use `FRONTEND_PORT` instead.
|
|
|
|
<details>
|
|
<summary><strong>Example system.json</strong></summary>
|
|
|
|
```json
|
|
{
|
|
"pvePollingInterval": 10, // Seconds
|
|
"backendPort": 3000, // Legacy (unused)
|
|
"frontendPort": 7655, // Legacy (ignored; use FRONTEND_PORT)
|
|
"logLevel": "info", // debug, info, warn, error
|
|
"autoUpdateEnabled": false, // Enable auto-update checks
|
|
"adaptivePollingEnabled": false, // Smart polling for large clusters
|
|
"allowedOrigins": "", // CORS allowlist (single origin or "*")
|
|
"allowEmbedding": false, // Allow iframe embedding
|
|
"allowedEmbedOrigins": "", // Comma-separated origins for iframe embedding
|
|
"webhookAllowedPrivateCIDRs": "" // Allowlist for private webhook targets
|
|
}
|
|
```
|
|
|
|
> **Note**: `logFormat` is only configurable via the `LOG_FORMAT` environment variable, not in `system.json`.
|
|
</details>
|
|
|
|
### Supported system.json Keys
|
|
|
|
Numeric intervals are **seconds** unless noted otherwise.
|
|
|
|
| Key | Description |
|
|
| ----- | ----------- |
|
|
| `pvePollingInterval` | PVE polling interval |
|
|
| `pbsPollingInterval` | PBS polling interval |
|
|
| `pmgPollingInterval` | PMG polling interval |
|
|
| `backupPollingInterval` | Backup polling interval (`0` = auto) |
|
|
| `backupPollingEnabled` | Enable backup polling |
|
|
| `adaptivePollingEnabled` | Enable adaptive polling |
|
|
| `adaptivePollingBaseInterval` | Base interval for adaptive polling |
|
|
| `adaptivePollingMinInterval` | Minimum adaptive polling interval |
|
|
| `adaptivePollingMaxInterval` | Maximum adaptive polling interval |
|
|
| `connectionTimeout` | API connection timeout |
|
|
| `logLevel` | Server log level (`debug`, `info`, `warn`, `error`) |
|
|
| `allowedOrigins` | CORS allowlist (single origin or `*`) |
|
|
| `allowEmbedding` | Allow iframe embedding |
|
|
| `allowedEmbedOrigins` | Comma-separated `frame-ancestors` allowlist |
|
|
| `webhookAllowedPrivateCIDRs` | Allowlist for private webhook targets |
|
|
| `updateChannel` | Update channel (`stable` or `rc`) |
|
|
| `autoUpdateEnabled` | Allow one-click updates |
|
|
| `publicURL` | Public URL used in links/notifications |
|
|
| `hideLocalLogin` | Hide username/password login form |
|
|
| `temperatureMonitoringEnabled` | Enable temperature monitoring (where supported) |
|
|
| `dnsCacheTimeout` | DNS cache timeout |
|
|
| `sshPort` | Default SSH port for temperature collection |
|
|
| `discoveryEnabled` | Enable auto-discovery |
|
|
| `discoverySubnet` | CIDR or `auto` |
|
|
| `discoveryConfig` | Discovery tuning object (see below) |
|
|
| `theme` | UI theme (`light`, `dark`, or empty for system) |
|
|
| `fullWidthMode` | UI layout preference |
|
|
| `metricsRetentionRawHours` | Raw metrics retention (hours) |
|
|
| `metricsRetentionMinuteHours` | Minute metrics retention (hours) |
|
|
| `metricsRetentionHourlyDays` | Hourly metrics retention (days) |
|
|
| `metricsRetentionDailyDays` | Daily metrics retention (days) |
|
|
| `disableDockerUpdateActions` | Hide Docker update actions in UI |
|
|
| `backendPort` | Legacy (unused) |
|
|
| `frontendPort` | Legacy (ignored; use `FRONTEND_PORT`) |
|
|
|
|
`discoveryConfig` supports:
|
|
- `environmentOverride`, `subnetAllowlist`, `subnetBlocklist`
|
|
- `maxHostsPerScan`, `maxConcurrent`, `enableReverseDns`, `scanGateways`
|
|
- `dialTimeoutMs`, `httpTimeoutMs`
|
|
|
|
### Common Overrides (Environment Variables)
|
|
Environment variables take precedence over `system.json`.
|
|
|
|
| Variable | Description | Default |
|
|
| ---------- | ------------- | --------- |
|
|
| `FRONTEND_PORT` | Public listening port (web UI, API, and agent ingest) | `7655` |
|
|
| `PORT` | **Deprecated** legacy alias for `FRONTEND_PORT`, honored only when `FRONTEND_PORT` is unset. Logs a deprecation warning at startup; switch to `FRONTEND_PORT`. | *(unset)* |
|
|
| `PULSE_AGENT_INGEST_PORT` | Optional dedicated port for the complete agent control plane: reports/config (`/api/agents/*`), command admission (`/api/agent/ws`), version checks, and bootstrap downloads. The web UI and management API stay isolated. `0` = disabled (single port). See [Split-Port Agent Ingest](#split-port-agent-ingest-network-isolation). | `0` |
|
|
| `LOG_LEVEL` | Log verbosity (see below) | `info` |
|
|
| `LOG_FORMAT` | Log output format (`auto`, `json`, `console`) | `auto` |
|
|
| `LOG_FILE` | Log file path (enables file logging) | *(unset)* |
|
|
| `LOG_MAX_SIZE` | Log rotation size (MB) | `100` |
|
|
| `LOG_MAX_AGE` | Keep rotated logs for N days (`0` disables cleanup) | `30` |
|
|
| `LOG_COMPRESS` | Gzip rotated logs | `true` |
|
|
|
|
#### Log Levels
|
|
|
|
| Level | Description |
|
|
| ------- | ------------- |
|
|
| `error` | Only errors and critical issues |
|
|
| `warn` | Errors + warnings (recommended for minimal logging) |
|
|
| `info` | Standard operational messages (startup, connections, alerts) |
|
|
| `debug` | Verbose output including per-guest/storage polling details |
|
|
|
|
> **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_PRO_TRIAL_SIGNUP_URL` | Legacy hosted commercial base URL retained for hosted entitlement refresh compatibility. The path is ignored for refresh and normal self-hosted v6 UI must not surface trial signup. Must be absolute `http(s)` URL. | `https://cloud.pulserelay.pro` |
|
|
| `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)* |
|
|
| `PULSE_AGENT_CONFIG_PUBLIC_KEYS` | Comma-separated base64 Ed25519 public keys (raw 32-byte or PKIX-encoded) trusted by agents. | *(unset)* |
|
|
| `PULSE_AGENT_CONFIG_SIGNATURE_REQUIRED` | Require signed remote config payloads (set on Pulse and agents). | `false` |
|
|
| `ALLOWED_ORIGINS` | CORS allowed origin (`*` or a single origin). Empty = same-origin only. | *(unset)* |
|
|
| `DISCOVERY_ENABLED` | Auto-discover nodes | `false` |
|
|
| `DISCOVERY_SUBNET` | CIDR or `auto` | `auto` |
|
|
| `DISCOVERY_ENVIRONMENT_OVERRIDE` | Force discovery environment (`auto`, `native`, `docker-host`, `docker-bridge`, `lxc-privileged`, `lxc-unprivileged`) | `auto` |
|
|
| `DISCOVERY_SUBNET_ALLOWLIST` | Comma-separated CIDRs allowed for discovery | *(empty)* |
|
|
| `DISCOVERY_SUBNET_BLOCKLIST` | Comma-separated CIDRs excluded from discovery | `169.254.0.0/16` |
|
|
| `DISCOVERY_MAX_HOSTS_PER_SCAN` | Max hosts to scan per run | `1024` |
|
|
| `DISCOVERY_MAX_CONCURRENT` | Max concurrent discovery probes | `50` |
|
|
| `DISCOVERY_ENABLE_REVERSE_DNS` | Enable reverse DNS lookup (`true`/`false`) | `true` |
|
|
| `DISCOVERY_SCAN_GATEWAYS` | Include gateway IPs in discovery (`true`/`false`) | `true` |
|
|
| `DISCOVERY_DIAL_TIMEOUT_MS` | TCP dial timeout (ms) | `1000` |
|
|
| `DISCOVERY_HTTP_TIMEOUT_MS` | HTTP probe timeout (ms) | `2000` |
|
|
| `PULSE_AUTH_HIDE_LOCAL_LOGIN` | Hide username/password form | `false` |
|
|
| `DEMO_MODE` | Enable read-only demo mode | `false` |
|
|
| `PULSE_TRUSTED_PROXY_CIDRS` | Comma-separated IPs/CIDRs trusted to supply `X-Forwarded-For`/`X-Real-IP` | *(unset)* |
|
|
| `PULSE_TRUSTED_NETWORKS` | Comma-separated CIDRs treated as trusted local networks (does not bypass auth) | *(unset)* |
|
|
| `ALLOW_UNPROTECTED_EXPORT` | Allow unauthenticated config export on public networks when no auth is configured (use with caution) | `false` |
|
|
|
|
### Split-Port Agent Ingest (Network Isolation)
|
|
|
|
By default Pulse serves the web UI, the REST API, and the agent control plane together on `FRONTEND_PORT`. For deployments that expose Pulse to monitored hosts across an untrusted network (for example, a managed service provider whose clients' Proxmox nodes reach a central Pulse server over the internet), you can expose the agent control plane on its own dedicated port and keep the web UI and management API on a separate, firewalled port.
|
|
|
|
Set `PULSE_AGENT_INGEST_PORT` to a port other than `FRONTEND_PORT`:
|
|
|
|
```bash
|
|
PULSE_AGENT_INGEST_PORT=7656
|
|
```
|
|
|
|
When enabled:
|
|
|
|
- The dedicated port serves only the agent-owned routes required for a complete lifecycle: `/api/agents/*`, `/api/agent/ws`, `/api/agent/version`, `/api/server/info`, `/install.sh`, `/install.ps1`, and `/download/pulse-agent`. Every other path, including the web UI, login, and management APIs, returns `404`. A host that can reach the agent port cannot pivot to the management interface.
|
|
- The main `FRONTEND_PORT` listener is unchanged and still serves everything (including agent ingest), so existing single-port installs keep working. The dedicated listener is purely additive.
|
|
- The value is validated at startup: it must be between 1 and 65535 and must differ from `FRONTEND_PORT` and the HTTP redirect port. An invalid value is rejected.
|
|
|
|
Expose only `PULSE_AGENT_INGEST_PORT` to your monitored hosts and keep `FRONTEND_PORT` on a private network or behind your firewall/VPN. Point agents at the dedicated port by setting `PULSE_AGENT_CONNECT_URL` to that port's public address, so generated agent install commands send check-ins there:
|
|
|
|
```bash
|
|
PULSE_AGENT_INGEST_PORT=7656
|
|
PULSE_AGENT_CONNECT_URL=https://agents.example.com:7656
|
|
```
|
|
|
|
Agents then post telemetry to `https://agents.example.com:7656/api/agents/agent/report` and establish their command channel at `wss://agents.example.com:7656/api/agent/ws`, while the web UI and management API remain reachable only on the private `FRONTEND_PORT` listener. If command execution is enabled, both routes must traverse the same proxy/firewall path; a successful report does not prove that the WebSocket is admitted.
|
|
|
|
### Proxmox Cluster Node Display Names
|
|
|
|
Nodes discovered through one Proxmox VE cluster connection can have an
|
|
optional Pulse display name. Open **Settings → Infrastructure**, edit the
|
|
Proxmox VE connection, and set **Display name** beside a cluster member.
|
|
|
|
- Display names are presentation only. They do not change the Proxmox node
|
|
name, API address, credentials, TLS fingerprint, routing, or action target.
|
|
- An empty display name uses the current native Proxmox node name.
|
|
- Names are trimmed, may contain Unicode, are limited to 128 characters, and
|
|
cannot contain control characters. Duplicate display names are allowed
|
|
because they are cosmetic; Pulse still uses a separate immutable identity.
|
|
- The override is stored with that Proxmox connection and survives reloads,
|
|
restarts, member address changes, native node renames, and temporary cluster
|
|
membership loss. Confirmed removed members retain their identity metadata so
|
|
a later reappearance can recover the override.
|
|
- Pulse keeps the native Proxmox node name, prior native names, numeric node ID
|
|
when available, and immutable Pulse identity for diagnostics and search.
|
|
These fields are also available to API and mobile clients.
|
|
|
|
For legacy configuration first seen before Pulse records a Proxmox numeric node
|
|
ID, changing both the native node name and every known member address at once
|
|
is intentionally treated as a new member. Pulse does not guess across
|
|
ambiguous evidence because that could transfer an override to the wrong node.
|
|
|
|
### Iframe Embedding (system.json)
|
|
|
|
Embedding is controlled by `system.json` and the UI (**Settings → System → Network**):
|
|
|
|
- `allowEmbedding` (boolean): enables iframe embedding
|
|
- `allowedEmbedOrigins` (comma-separated): restricts `frame-ancestors` when embedding is enabled
|
|
|
|
When `allowEmbedding` is `false`, Pulse sends `X-Frame-Options: DENY` and `frame-ancestors 'none'`.
|
|
|
|
### 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` |
|
|
| `CONNECTION_TIMEOUT` | API connection timeout | `60s` |
|
|
| `BACKUP_POLLING_CYCLES` | Poll cycles between backup checks | `10` |
|
|
| `ENABLE_BACKUP_POLLING` | Enable backup job monitoring | `true` |
|
|
| `BACKUP_POLLING_INTERVAL` | Backup polling frequency | `0` (Auto) |
|
|
| `ENABLE_TEMPERATURE_MONITORING` | Enable temperature monitoring (where supported) | `true` |
|
|
| `SSH_PORT` | SSH port for temperature collection over SSH | `22` |
|
|
| `ADAPTIVE_POLLING_ENABLED` | Enable smart polling for large clusters | `false` |
|
|
| `ADAPTIVE_POLLING_BASE_INTERVAL` | Base interval for adaptive polling | `10s` |
|
|
| `ADAPTIVE_POLLING_MIN_INTERVAL` | Minimum adaptive polling interval | `5s` |
|
|
| `ADAPTIVE_POLLING_MAX_INTERVAL` | Maximum adaptive polling interval | `5m` |
|
|
| `GUEST_METADATA_MIN_REFRESH_INTERVAL` | Minimum refresh for guest metadata | `2m` |
|
|
| `GUEST_METADATA_REFRESH_JITTER` | Jitter for guest metadata refresh | `45s` |
|
|
| `GUEST_METADATA_RETRY_BACKOFF` | Retry backoff for guest metadata | `30s` |
|
|
| `GUEST_METADATA_MAX_CONCURRENT` | Max concurrent guest metadata fetches | `4` |
|
|
| `DNS_CACHE_TIMEOUT` | Cache TTL for DNS lookups | `5m` |
|
|
| `MAX_POLL_TIMEOUT` | Maximum time per polling cycle | `3m` |
|
|
| `PULSE_DISABLE_DOCKER_UPDATE_ACTIONS` | Hide Docker update buttons (read-only mode) | `false` |
|
|
| `PULSE_ENABLE_PROXMOX_GUEST_DOCKER_DETECTION` | Allow Proxmox-side LXC Docker socket hinting with `pct exec` | `false` |
|
|
| `PULSE_ENABLE_PROXMOX_GUEST_DOCKER_INVENTORY` | Allow Proxmox-side minimal LXC Docker inventory collection with `pct exec`; collects Docker host/container summary, not inspect/env/mount/process data. Admins can also toggle this in Settings → System → General; setting the env var locks the toggle | `false` |
|
|
| `PULSE_PROXMOX_GUEST_DOCKER_INVENTORY_VMIDS` | Optional comma-separated VMID allowlist for Proxmox-side LXC Docker discovery; when set, only these guests are socket-probed and inventoried. Empty means all running LXCs are eligible when detection or inventory is enabled | *(unset)* |
|
|
| `PULSE_TELEMETRY` | Outbound usage telemetry ([details](PRIVACY.md)); set `false` to disable | `true` |
|
|
| `PULSE_DEPLOYMENT_METHOD` | Optional closed telemetry label: `docker_compose`, `docker_run`, `container_other`, `systemd`, `binary_other`, or `other`; invalid values are reported only as the safe runtime fallback | Inferred as `container_other` or `binary_other` |
|
|
|
|
### Logging Overrides
|
|
|
|
| Variable | Description | Default |
|
|
| ---------- | ------------- | --------- |
|
|
| `LOG_FILE` | Log file path (empty = stderr only) | *(unset)* |
|
|
| `LOG_MAX_SIZE` | Log file max size (MB) | `100` |
|
|
| `LOG_MAX_AGE` | Log file retention (days, `0` disables cleanup) | `30` |
|
|
| `LOG_COMPRESS` | Compress rotated logs | `true` |
|
|
|
|
|
|
### Update Settings (system.json)
|
|
|
|
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` |
|
|
|
|
> **Note**: Update settings are stored in `system.json`. Legacy `.env` entries (`UPDATE_CHANNEL`, `AUTO_UPDATE_ENABLED`) are kept in sync for backwards compatibility but are not read at runtime. The former `autoUpdateCheckInterval` / `autoUpdateTime` fields were never consumed and are ignored if present; the update schedule lives in the systemd timer.
|
|
>
|
|
> `stable` is the default and recommended production channel. `rc` is an
|
|
> opt-in preview channel. In v6, unattended systemd auto-updates remain
|
|
> `stable`-only even when `updateChannel` is set to `rc`.
|
|
|
|
### Auto-Import (Bootstrap)
|
|
|
|
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) |
|
|
|
|
> **Note**: `PULSE_INIT_CONFIG_URL` is only supported by the hidden `pulse config auto-import` command, not by the server startup auto-import.
|
|
|
|
Configuration import restores server-side agent credentials, but it cannot
|
|
change the primary Pulse URL persisted on remote agents. When restoring onto a
|
|
server with a different address, retarget those agents after import. See
|
|
[Moving Pulse to a new address](UNIFIED_AGENT.md#moving-pulse-to-a-new-address).
|
|
|
|
### Developer/Test Overrides (Environment Variables)
|
|
|
|
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` |
|
|
| `PULSE_DEV_ALLOW_CONTAINER_SSH` | Allow SSH-based temperature collection from containers (dev/test only) | `false` |
|
|
| `PULSE_AI_ALLOW_LOOPBACK` | Allow AI tool HTTP fetches to loopback addresses | `false` |
|
|
| `PULSE_LICENSE_PUBLIC_KEY` | Override embedded license public key (base64, dev only) | *(unset)* |
|
|
| `PULSE_LICENSE_DEV_MODE` | Skip license verification (development only) | `false` |
|
|
|
|
### Metrics Retention (Tiered)
|
|
|
|
Persistent metrics history uses tiered retention windows. These values are stored in `system.json` and can be adjusted for storage vs history depth:
|
|
|
|
- `metricsRetentionRawHours`
|
|
- `metricsRetentionMinuteHours`
|
|
- `metricsRetentionHourlyDays`
|
|
- `metricsRetentionDailyDays`
|
|
|
|
See [METRICS_HISTORY.md](METRICS_HISTORY.md) for details.
|
|
|
|
### Prometheus Metrics Endpoint
|
|
|
|
The `/metrics` listener is separate from the main UI/API listener and binds to loopback by default.
|
|
|
|
| Variable | Description | Default |
|
|
| ---------- | ------------- | --------- |
|
|
| `PULSE_METRICS_PORT` | Metrics listener port | `9091` |
|
|
| `PULSE_METRICS_BIND_ADDRESS` | Metrics listener bind address | `127.0.0.1` |
|
|
| `PULSE_METRICS_TOKEN` | Optional bearer token for `/metrics` | *(empty)* |
|
|
| `PULSE_METRICS_ALLOW_INSECURE_REMOTE` | Explicit opt-in to serve a metrics bearer token over non-loopback plaintext HTTP | `false` |
|
|
|
|
For remote scraping with `PULSE_METRICS_TOKEN`, prefer a local scraper, tunnel, VPN-private path, or TLS/mTLS reverse proxy. Pulse refuses non-loopback plaintext token scraping unless `PULSE_METRICS_ALLOW_INSECURE_REMOTE=true` is set.
|
|
|
|
---
|
|
|
|
## 🔔 Alerts (`alerts.json`)
|
|
|
|
Pulse uses a powerful alerting engine with hysteresis (separate trigger/clear thresholds) to prevent flapping.
|
|
|
|
**Managed via UI**: Alerts → Thresholds
|
|
|
|
### VM and container powered-off tolerance
|
|
|
|
Open **Alerts → Thresholds → Alert intent & grace** to configure how long a
|
|
Proxmox VM or LXC container may remain stopped before Pulse raises its
|
|
powered-off alert.
|
|
|
|
- Leave the VM/container default blank to inherit the existing policy. An
|
|
installation with no applicable policy keeps the legacy two-poll behavior.
|
|
- Set it to `0` to alert on the first authoritative stopped observation.
|
|
- Set a positive number of seconds (for example, `300`) to tolerate a short
|
|
stop regardless of polling cadence. Per-resource values override the
|
|
VM/container default; a blank per-resource value inherits it.
|
|
- **Extend offline grace during Proxmox backups** applies only while Pulse has
|
|
fresh matching evidence of an active backup. The maximum deferral is a hard
|
|
cap, so a stale or stuck backup cannot hide a sustained outage.
|
|
|
|
This tolerance delays activation; it does not disable powered-off monitoring.
|
|
Use the existing guest offline-alert toggle when a guest should never produce
|
|
powered-off alerts.
|
|
|
|
<details>
|
|
<summary><strong>Manual Configuration (JSON)</strong></summary>
|
|
|
|
```json
|
|
{
|
|
"guestDefaults": {
|
|
"cpu": { "trigger": 90, "clear": 80 },
|
|
"memory": { "trigger": 85, "clear": 72.5 }
|
|
},
|
|
"schedule": {
|
|
"quietHours": {
|
|
"enabled": true,
|
|
"start": "22:00",
|
|
"end": "06:00"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
</details>
|
|
|
|
---
|
|
|
|
## Availability Checks (`availability_targets.enc`)
|
|
|
|
Availability checks are agentless probes for devices and services where Pulse
|
|
cannot install an agent or does not need full machine telemetry. Use them for
|
|
simple ping monitoring, TCP service checks, and HTTP/HTTPS status checks.
|
|
|
|
**Managed via UI**: Settings -> Monitoring -> Availability checks
|
|
|
|
Supported protocols:
|
|
|
|
| Protocol | Use case | Required fields |
|
|
| ---------- | ---------- | ---------------- |
|
|
| `icmp` | Ping-only reachability for devices, computers, and appliances | `address` |
|
|
| `ping` | API input alias for `icmp`; saved targets return `icmp` | `address` |
|
|
| `tcp` | A reachable port such as MQTT, SSH, or a custom service | `address`, `port` |
|
|
| `udp` | A request/response UDP service, or an open-or-filtered observation | `address`, `port`, UDP result policy |
|
|
| `http` / `https` | Web UI or health endpoint availability | `address`, optional `port`, optional `path` |
|
|
|
|
Saved targets include `name`, `targetKind` (`machine`, `service`, or
|
|
`device`), `address`, `protocol`, `enabled`, polling interval, timeout,
|
|
failure threshold, and an optional `linkedResourceId`. UDP targets select
|
|
`response_required` (send up to 512 UTF-8 bytes and require a response) or
|
|
`open_or_filtered` (silence is indeterminate, not proof of reachability); an
|
|
optional expected response must match exactly. HTTPS targets monitor
|
|
certificate validity by default and warn 30 days before expiry unless the
|
|
target overrides or disables that check. ICMP ping is the default probe for
|
|
new targets. Availability targets publish `network-endpoint` resources and can
|
|
raise downtime alerts after the configured failure threshold and certificate
|
|
alerts inside the configured expiry window.
|
|
|
|
Example API payload for simple ping monitoring:
|
|
|
|
```json
|
|
{
|
|
"name": "Garage temperature sensor",
|
|
"targetKind": "device",
|
|
"address": "garage-sensor.local",
|
|
"protocol": "ping",
|
|
"enabled": true
|
|
}
|
|
```
|
|
|
|
Pulse stores and returns that target as `protocol: "icmp"` so dashboards,
|
|
alerts, and resource projections keep one canonical protocol value.
|
|
|
|
### External probes (Pro)
|
|
|
|
By default every availability check runs from the Pulse server itself, which
|
|
means a check cannot report the one failure that matters most: the site
|
|
running Pulse losing its connectivity. With the Pro `external_probe`
|
|
entitlement, a check can instead be assigned to any connected Pulse agent
|
|
(`probeAgentId` in the API, "Run from" in the UI) — for example an agent on a
|
|
cloud VM or at another site. The assigned agent receives the check through
|
|
its signed agent configuration, runs it on the configured interval, and
|
|
reports results back with its reports; results are only accepted from the
|
|
currently assigned agent. An assigned check is not also run locally. If no
|
|
report arrives for several intervals the check shows as indeterminate
|
|
("no recent report from probe agent"). Freshness uses the Pulse server's receipt
|
|
time, so clock drift on the probe cannot create or hide a disconnect. After at
|
|
least five minutes without a report, Pulse raises one warning per disconnected
|
|
probe through the normal notification routes, even when that probe owns several
|
|
checks. The incident is keyed to the probe agent rather than an arbitrary check,
|
|
so changing assignments does not reopen it. If the agent heartbeat is also
|
|
offline, the existing
|
|
host-offline alert owns the incident instead of producing a duplicate probe
|
|
warning. A paired Pulse Mobile client receives a privacy-safe
|
|
`external_probe_offline` Relay push linked to that canonical alert. If the whole
|
|
Pulse instance becomes unreachable, Relay's independent five-minute
|
|
instance-offline push covers the dark-site case. If the entitlement lapses, the
|
|
check automatically resumes running from the Pulse server. Checks without an
|
|
assignment are unaffected and remain available in every edition.
|
|
|
|
### ICMP probe privileges
|
|
|
|
ICMP probes run the system `ping` binary, which needs the `CAP_NET_RAW`
|
|
capability. The systemd unit written by the installer hardens the service
|
|
with `NoNewPrivileges=true`, which strips ping's setuid bit and file
|
|
capabilities, so the unit also grants the capability directly with
|
|
`AmbientCapabilities=CAP_NET_RAW`. Units written by older versions of the
|
|
installer lack that line, and ICMP probes fail with
|
|
`icmp probe failed: ping: socktype: SOCK_RAW ... missing cap_net_raw+p capability`.
|
|
|
|
To fix an existing install, either re-run the install script (it rewrites
|
|
the unit) or add the capability as an override:
|
|
|
|
```bash
|
|
systemctl edit pulse # pulse-backend on ProxmoxVE community-script installs
|
|
```
|
|
|
|
```ini
|
|
[Service]
|
|
AmbientCapabilities=CAP_NET_RAW
|
|
```
|
|
|
|
Then `systemctl daemon-reload && systemctl restart pulse`.
|
|
|
|
Docker installs are unaffected: Docker's default capability set includes
|
|
`NET_RAW`. If you run the container with `--cap-drop=ALL`, add
|
|
`--cap-add=NET_RAW` to keep ICMP probes working. TCP and HTTP/HTTPS probes
|
|
need no special privileges.
|
|
|
|
---
|
|
|
|
## 🔒 HTTPS / TLS
|
|
|
|
Enable HTTPS by providing certificate files via environment variables.
|
|
|
|
```bash
|
|
# Systemd
|
|
HTTPS_ENABLED=true
|
|
TLS_CERT_FILE=/etc/pulse/cert.pem
|
|
TLS_KEY_FILE=/etc/pulse/key.pem
|
|
|
|
# Docker
|
|
docker run --init -e HTTPS_ENABLED=true \
|
|
-v /path/to/certs:/certs \
|
|
-e TLS_CERT_FILE=/certs/cert.pem \
|
|
-e TLS_KEY_FILE=/certs/key.pem ...
|
|
```
|
|
|
|
> **Important (Docker with HTTPS)**: Always use `--init` (or `init: true` in docker-compose) when enabling HTTPS. The Alpine-based healthcheck uses busybox `wget`, which spawns `ssl_client` subprocesses. Without an init process to reap them, these become zombie processes over time.
|
|
|
|
---
|
|
|
|
## 🛡️ Security Best Practices
|
|
|
|
1. **Permissions**: Ensure `.env` and `nodes.enc` are `600` (read/write by owner only).
|
|
2. **Backup hygiene**: Back up `.env` separately from `system.json`.
|
|
3. **Tokens**: Use scoped API tokens for agents instead of the admin password.
|
|
|
|
---
|
|
|
|
## 🔑 API Tokens
|
|
|
|
API tokens provide scoped, revocable access to Pulse. Manage tokens in **Settings → Security → API Tokens**.
|
|
|
|
The token shown during first-run setup is the primary automation API token for
|
|
that Pulse instance. It is separate from your web login password and is meant
|
|
for agents, scripts, integrations, kiosks, and temporary setup handoffs. Tokens
|
|
are shown once; later token rows show only identifying hints such as prefix,
|
|
suffix, label, scopes, and last-used metadata.
|
|
|
|
Revoking a token is safe for Pulse itself, but it immediately breaks any agent,
|
|
script, kiosk, or integration still using that token. When a consumer needs to
|
|
stay online, create and install a replacement token first, then revoke the old
|
|
one. An agent whose token has been revoked stops authenticating until it is
|
|
reinstalled or reconfigured with a valid token.
|
|
|
|
### Token Scopes
|
|
|
|
| Scope | Description |
|
|
| ------- | ------------- |
|
|
| `*` (Full access) | All permissions (legacy, not recommended) |
|
|
| `monitoring:read` | View dashboards, metrics, alerts |
|
|
| `monitoring:write` | Acknowledge/silence alerts |
|
|
| `docker:report` | Docker / Podman agent telemetry submission |
|
|
| `docker:manage` | Docker / Podman container lifecycle actions (restart, stop) |
|
|
| `kubernetes:report` | Kubernetes agent telemetry submission |
|
|
| `kubernetes:manage` | Kubernetes cluster management |
|
|
| `agent:report` | Agent host telemetry submission |
|
|
| `agent:config:read` | Read agent config payloads |
|
|
| `agent:manage` | Manage registered agents (unlink/delete/config) |
|
|
| `agent:exec` | Establish agent command WebSocket connections |
|
|
| `ai:chat` | Use Pulse Assistant chat and read knowledge |
|
|
| `ai:execute` | Use governed Patrol plans, approvals, actions, and history |
|
|
| `settings:read` | Read configuration |
|
|
| `settings:write` | Modify configuration |
|
|
| `audit:read` | Read audit events, verification results, summaries, and exports |
|
|
|
|
### Presets
|
|
|
|
The UI offers quick presets for common use cases:
|
|
|
|
| Preset | Scopes | Use Case |
|
|
| -------- | -------- | ---------- |
|
|
| **Kiosk / Dashboard** | `monitoring:read` | Read-only dashboard displays |
|
|
| **Agent host** | `agent:report`, `agent:config:read`, `agent:manage` | Agent telemetry, configuration fetch, and uninstall cleanup |
|
|
| **Docker / Podman report** | `docker:report` | Docker / Podman agent (read-only) |
|
|
| **Docker / Podman manage** | `docker:report`, `docker:manage` | Docker / Podman agent with actions |
|
|
| **Settings read** | `settings:read` | Read-only config access |
|
|
| **Settings admin** | `settings:read`, `settings:write` | Full config access |
|
|
|
|
### Kiosk Mode
|
|
|
|
For unattended displays (wall monitors, dashboards), use a kiosk token to avoid cookie persistence issues:
|
|
|
|
1. Go to **Settings → Security → API Tokens**
|
|
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
|
|
```
|
|
|
|
**Kiosk tokens:**
|
|
- Grant read-only dashboard access (`monitoring:read` scope)
|
|
- Hide the Settings tab automatically
|
|
- Work without cookies (token in URL)
|
|
- Can be revoked anytime from the UI
|
|
|
|
> **Security note**: URL tokens appear in browser history and server logs. Use only for read-only dashboard access on trusted networks.
|
|
|
|
---
|
|
|
|
## TrueNAS
|
|
|
|
Pulse v6 supports first-class TrueNAS SCALE and CORE monitoring.
|
|
|
|
### Adding a TrueNAS Instance
|
|
|
|
1. Go to **Settings → TrueNAS**.
|
|
2. Click **Add Connection**.
|
|
3. Enter the URL (e.g., `https://truenas.local`) and an API key.
|
|
4. Click **Test Connection** to verify, then **Save**.
|
|
|
|
### Creating a TrueNAS API Key
|
|
|
|
On your TrueNAS system:
|
|
1. Navigate to the TrueNAS UI → **Settings → API Keys**.
|
|
2. Click **Add** and create a new read-only key.
|
|
3. Copy the key value and paste it into Pulse.
|
|
|
|
### What Gets Monitored
|
|
|
|
| Data | Where it appears |
|
|
|---|---|
|
|
| System info (CPU, memory, uptime) | Infrastructure page |
|
|
| Virtual machines | TrueNAS Overview |
|
|
| Apps | TrueNAS Overview |
|
|
| ZFS Pools & datasets | Storage page |
|
|
| Physical disks | Storage page |
|
|
| ZFS Snapshots | Recovery page |
|
|
| Replication tasks | Recovery page |
|
|
| TrueNAS alerts | Alerts page |
|
|
|
|
TrueNAS connections are stored encrypted in `truenas.enc`.
|
|
|
|
---
|
|
|
|
## Relay
|
|
|
|
The relay protocol provides end-to-end encrypted remote access foundations for Pulse mobile connectivity.
|
|
|
|
> Supported Pulse Mobile clients pair here using the generated QR code or deep link once relay is enabled for this instance.
|
|
|
|
### Configuration
|
|
|
|
1. Go to **Settings → Relay**.
|
|
2. Toggle relay **On**.
|
|
3. Use the **QR Code** or **Deep Link** to pair a supported Pulse Mobile client.
|
|
|
|
### Environment Overrides
|
|
|
|
For headless / container deployments that need to bootstrap relay without
|
|
going through the UI, two environment variables override the persisted
|
|
`relay.enc` values at load time:
|
|
|
|
| Variable | Description | Default |
|
|
|---|---|---|
|
|
| `PULSE_RELAY_ENABLED` | Enable/disable relay (`true`/`false`/`yes`/`no`/`1`/`0`). Unset or unrecognized values leave the file value untouched. | *(unset)* |
|
|
| `PULSE_RELAY_SERVER` | Override relay server URL. Must be a valid `ws://` or `wss://` URL with no userinfo, query, or fragment. Invalid values are logged and ignored. | `wss://relay.pulserelay.pro/ws/instance` |
|
|
|
|
Precedence: env vars beat the file. If you set `PULSE_RELAY_ENABLED=true`,
|
|
saving the relay form in **Settings → Relay** will then persist the
|
|
env-effective state to disk, so removing the env var later does not
|
|
automatically revert relay back to its previous file-stored state — clear
|
|
relay in the UI as well if you want to fully disable it.
|
|
|
|
### Security
|
|
|
|
- All data is encrypted end-to-end using ECDH key exchange.
|
|
- The relay server never sees plaintext monitoring data.
|
|
- Each mobile session has its own encryption channel.
|
|
- Requires a valid Relay, Pro, legacy Pro+, or Cloud license (gated by the `relay` feature key).
|
|
|
|
Relay config is stored encrypted in `relay.enc`.
|