# ⚙️ 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** | | `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//`. - 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$...' ```
Advanced: Automated Setup (Skip UI) 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_AUTH_USER=admin \ -e PULSE_AUTH_PASS=secret123 \ rcourtman/pulse:latest ```
Advanced: OIDC / SSO 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 `/api/oidc//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 (Pro/legacy Pro+/Cloud) | | `OIDC_CA_BUNDLE` | Custom CA bundle path |
> **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.
Example system.json ```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`. > **Note**: `autoUpdateTime` is stored by the UI, but the systemd timer uses its own schedule.
### 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 | | `autoUpdateCheckInterval` | Update check interval (hours) | | `autoUpdateTime` | UI-stored preferred update time | | `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 that serves **only** agent ingest (`/api/agents/*`), network-isolated from the web UI and the rest of the API. `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 agent check-in 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 move agent check-in onto 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-ingest routes (`/api/agents/*`). Every other path, including the web UI, login, and the management API, 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`, while the web UI and management API remain reachable only on the private `FRONTEND_PORT` listener. ### 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 | `false` | | `PULSE_PROXMOX_GUEST_DOCKER_INVENTORY_VMIDS` | Optional comma-separated VMID allowlist for Proxmox-side LXC Docker inventory; empty means all running Docker-enabled LXCs are eligible when inventory is enabled | *(unset)* | | `PULSE_TELEMETRY` | Anonymous outbound usage telemetry ([details](PRIVACY.md)); set `false` to disable | `true` | ### 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` | | `autoUpdateCheckInterval` | Background update check interval in hours (`0` disables) | `24` | | `autoUpdateTime` | Stored UI preference (systemd timer has its own schedule) | `03:00` | > **Note**: Update settings are stored in `system.json`. Legacy `.env` entries (`UPDATE_CHANNEL`, `AUTO_UPDATE_ENABLED`, `AUTO_UPDATE_CHECK_INTERVAL`, `AUTO_UPDATE_TIME`) are kept in sync for backwards compatibility but are not read at runtime. > > `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. ### 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
Manual Configuration (JSON) ```json { "guestDefaults": { "cpu": { "trigger": 90, "clear": 80 }, "memory": { "trigger": 85, "clear": 72.5 } }, "schedule": { "quietHours": { "enabled": true, "start": "22:00", "end": "06:00" } } } ```
--- ## 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` | | `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`. 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. 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. --- ## 🔒 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) | | `settings:read` | Read configuration | | `settings:write` | Modify configuration | ### 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 host telemetry authentication | | **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 Integration {#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 / Mobile Remote Access (Relay and Above) {#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`.