diff --git a/README.md b/README.md index 362991a20..92d8e67d4 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ bash install.sh --version "${PULSE_VERSION}" rm -f install.sh install.sh.sshsig ``` -Note: this installs the Pulse **server**. Agent installs use the command generated in **Settings → Unified Agents → Installation commands** (served from `/install.sh` on your Pulse server). +Note: this installs the Pulse **server**. Agent installs and v5-to-v6 agent upgrades use the command generated in **Settings → Infrastructure → Install on a host** (served from `/install.sh` on your Pulse server). ### Option 2: Docker ```bash diff --git a/SECURITY.md b/SECURITY.md index 4d8963339..6f85540b7 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -43,8 +43,8 @@ environment where `PULSE_DOCKER=true`/`/.dockerenv` is detected. Preferred option (no SSH keys, no proxy wiring): -1. Install the unified agent (`pulse-agent`) on each Proxmox host with Proxmox integration enabled. - - Use the UI to generate an install command in **Settings → Agents → Installation commands**, or run: +1. Install or upgrade the unified agent (`pulse-agent`) on each Proxmox host with Proxmox integration enabled. + - Use the UI to generate an install or upgrade command in **Settings → Infrastructure → Install on a host**, or run: ```bash curl -fsSL http://pulse.example.com:7655/install.sh | \ sudo bash -s -- --url http://pulse.example.com:7655 --token --enable-proxmox @@ -105,7 +105,7 @@ ls /home/pulse/.ssh/id_ed25519* 2>/dev/null && echo "⚠️ SSH keys present" Verify temperature collection is agent-based: -- UI: **Settings → Agents** shows each Proxmox host connected and reporting. +- UI: the Proxmox or Machines page shows the host as agent-backed after the agent reports. - On each Proxmox host: ```bash systemctl status pulse-agent diff --git a/docs/CENTRALIZED_MANAGEMENT.md b/docs/CENTRALIZED_MANAGEMENT.md index 377af3c01..da1c90730 100644 --- a/docs/CENTRALIZED_MANAGEMENT.md +++ b/docs/CENTRALIZED_MANAGEMENT.md @@ -2,7 +2,7 @@ Pro, legacy Pro+, and Cloud support centralized management of `pulse-agent` configurations, allowing administrators to define "Configuration Profiles" and assign them to specific installed agents. This enables bulk updates and consistent configuration across your fleet without manually editing configuration files on each host. -Profiles are managed in the UI: **Settings → Unified Agents → Agent Profiles**. +Profiles are managed in the UI from **Settings → Infrastructure → Install on a host → Manage agent profiles**. ## Concepts @@ -34,7 +34,7 @@ The following settings can be controlled remotely via profiles: Notes: - `interval` accepts a duration string. If you send a JSON number, it is interpreted as seconds. - Docker auto-detection can still enable Docker monitoring if the agent is not explicitly configured. To force-disable Docker, set `PULSE_ENABLE_DOCKER=false` or install with `--enable-docker=false` on the host. That local disable also blocks remote profiles from turning Docker/Podman monitoring back on. -- `commandsEnabled` (AI command execution) is controlled separately per agent in **Settings → Unified Agents** and is applied live on report. It is not part of profile settings. +- `commandsEnabled` (AI command execution) is controlled separately per agent from the Infrastructure agent controls and is applied live on report. It is not part of profile settings. ## API Usage diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index 720d1833f..b15aed62d 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -435,6 +435,18 @@ docker run --init -e HTTPS_ENABLED=true \ 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 | diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 84241409c..e9a96277f 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -214,7 +214,7 @@ services: To disable registry checks entirely, set `PULSE_DISABLE_DOCKER_UPDATE_CHECKS=true` on the **agent**. -You can also toggle "Hide Docker Update Buttons" from the UI: **Settings → Unified Agents** (Docker Settings card). +You can also toggle "Hide Docker Update Buttons" from the UI in **Settings → System → General** under **Docker / Podman updates**. --- diff --git a/docs/PBS.md b/docs/PBS.md index 48a103185..d13036479 100644 --- a/docs/PBS.md +++ b/docs/PBS.md @@ -56,12 +56,12 @@ The agent will: Use this when you can run a command on the PBS host but do not want to install the agent. From Pulse's Settings page: -1. Go to **Settings → Unified Agents** -2. Click **Add Node** -3. Open **Advanced** and select **API Only** -4. Enter your PBS server's URL -5. Click copy to get the setup command -6. Run the command on your PBS server +1. Go to **Settings → Infrastructure**. +2. Click **Add infrastructure**. +3. Choose **Proxmox Backup Server**. +4. Use the API-only setup path and enter your PBS server's URL. +5. Click copy to get the setup command. +6. Run the command on your PBS server. Example (what the UI generates): ```bash diff --git a/docs/UNIFIED_AGENT.md b/docs/UNIFIED_AGENT.md index eeb14973b..a6bd9f1e6 100644 --- a/docs/UNIFIED_AGENT.md +++ b/docs/UNIFIED_AGENT.md @@ -26,6 +26,17 @@ Generate an installation command in the UI: Choose a target profile in that screen when you want explicit install flags for Docker, Kubernetes, Proxmox VE, or Proxmox Backup Server. +The same generated command is also the supported v5-to-v6 agent upgrade path. +Run it on the host that already has the v5 `pulse-agent` service to replace the +binary and service configuration in place; do not uninstall the old service +first unless you are intentionally removing that host from Pulse. + +An installed agent is configured for one Pulse URL and one token. Do not point +one running service at both a v5 server and a v6 server. After the upgrade, +check the relevant platform page or **Machines** view once the agent has +reported, and confirm the host-local version with `pulse-agent --version` if +the UI has not received a fresh report yet. + ### Linux (systemd) ```bash curl -fsSL http://:7655/install.sh | \ @@ -274,7 +285,7 @@ Behavior: - The agent fetches remote config on startup from `/api/agents/agent/{agent_id}/config`. - Profile settings override local flags/env for supported keys. - Profile changes take effect on the next agent restart. -- Command execution (`commandsEnabled`) is controlled per agent in **Settings → Unified Agents** and can change live. +- Command execution (`commandsEnabled`) is controlled per agent from the Infrastructure agent controls and can change live. - Remote config responses can be signed with `PULSE_AGENT_CONFIG_SIGNING_KEY` (base64 Ed25519 private key). - To require signed payloads, set `PULSE_AGENT_CONFIG_SIGNATURE_REQUIRED=true` on Pulse and agents. - If you use a custom signing key, set `PULSE_AGENT_CONFIG_PUBLIC_KEYS` on agents to trust the matching public key. diff --git a/docs/UPGRADE_v6.md b/docs/UPGRADE_v6.md index b53485c2d..0df38b664 100644 --- a/docs/UPGRADE_v6.md +++ b/docs/UPGRADE_v6.md @@ -28,7 +28,7 @@ If you prefer CLI, use the installed update helper for the target version: sudo /bin/update --version vX.Y.Z ``` -`/bin/update` is installed by the supported systemd and Proxmox LXC server installer. If your host does not have it yet, follow the signed server-installer flow in [INSTALL.md](INSTALL.md). Agent updates still use the `/install.sh` command generated in **Settings → Infrastructure → Install on a host**. +`/bin/update` is installed by the supported systemd and Proxmox LXC server installer. If your host does not have it yet, follow the signed server-installer flow in [INSTALL.md](INSTALL.md). Agent updates and v5-to-v6 agent upgrades still use the `/install.sh` command generated in **Settings → Infrastructure → Install on a host**; that screen is for both first installs and in-place agent upgrades. Operator note for builds after `v6.0.0-rc.2`: the historical Pulse update signer was not recovered. Hosts pinned to the `rc.2` trust root should not @@ -66,14 +66,15 @@ No. Upgrade the existing Pulse server installation in place. ### Do I need to uninstall my existing Pulse Unified Agents first? -No. Use the unified installer to upgrade existing agent deployments in place. +No. Use the unified installer to upgrade existing agent deployments in place. Generate the current command from **Settings → Infrastructure → Install on a host**, then run it on the host that already has the v5 agent service. You do not need to remove the old service first. ### Does upgrading the Pulse server to v6 automatically upgrade my agents? No. The server upgrade and the Unified Agent upgrade are separate operations. After the server is on v6, use the generated install or upgrade command from **Settings → Infrastructure → Install on a host** when you want to move agents -to v6. +to v6. A v5 agent can be missing from v6 Reporting until it has upgraded, +authenticated, and sent its first v6 report. ### Will an upgraded v5 agent keep the same identity in v6? @@ -85,6 +86,24 @@ identity rather than creating a duplicate record during the upgrade. No. Existing installed agents are expected to continue through the v6 compatibility boundary for legacy persisted agent scopes. +If you create a replacement token during the upgrade, install or reconfigure the +agent with the replacement before revoking the old token. Revoking the token +currently used by an agent stops that agent from authenticating until it is +reinstalled or reconfigured with a valid token. + +### Where do I check installed agent versions in v6? + +After an agent reports to v6, check the relevant platform page or **Machines** +view for the agent-backed host and version/status details. Version and outdated +agent notices appear only after the agent has successfully reported; they are +not an offline inventory of every v5 service that existed before the server +upgrade. On the host itself, confirm the local binary with: + +```bash +pulse-agent --version +systemctl status pulse-agent +``` + ### Can one installed Pulse Unified Agent report to both a Pulse v5 instance and a Pulse v6 instance at the same time? Not as a supported in-place setup. A running Unified Agent installation is diff --git a/docs/WEBHOOKS.md b/docs/WEBHOOKS.md index 3ff02b2d5..a56fc75a0 100644 --- a/docs/WEBHOOKS.md +++ b/docs/WEBHOOKS.md @@ -6,7 +6,7 @@ Pulse includes built-in templates for popular services and a generic JSON templa 1. Go to **Alerts → Notification Destinations**. 2. Click **Add Webhook**. -3. Select service type and paste the URL. +3. Click the current service label (Generic by default) to open the service picker, choose the destination type, and paste the URL. ## 📝 Service URLs @@ -51,6 +51,7 @@ For generic webhooks, use Go templates to format the JSON payload. - **Telegram templates**: `{{.ChatID}}` is populated from the URL query string. - **PagerDuty**: set `routing_key` as a custom field (or header) in the webhook config. - **Pushover**: add `token` and `user` custom fields (required). Legacy `app_token` and `user_token` inputs are migrated automatically. +- **ntfy**: choose **ntfy** in the service picker before entering the topic URL. Leave the service as Generic only when you want to send a custom JSON payload. **Example Payload:** ```json diff --git a/docs/release-control/v6/internal/subsystems/deployment-installability.md b/docs/release-control/v6/internal/subsystems/deployment-installability.md index 67b5e6766..591aaf75b 100644 --- a/docs/release-control/v6/internal/subsystems/deployment-installability.md +++ b/docs/release-control/v6/internal/subsystems/deployment-installability.md @@ -739,6 +739,12 @@ posture for install and support guidance: it describes activation, recovery, and BYOK/local AI setup, while explicitly keeping general in-app trials, trial-return callbacks, and hosted AI quickstart acquisition out of the ordinary upgrade path. +That same upgrade guidance and the current shipped release notes must describe +v5-to-v6 agent upgrades through the current Infrastructure install surface: +`Settings → Infrastructure → Install on a host` is the supported path for both +first installs and in-place agent upgrades, and v6 may only show agent +version/status details after the upgraded agent authenticates and sends a fresh +report rather than from an offline inventory of pre-upgrade v5 services. That same release-confidence lane now also owns the shipped Helm chart path, so release automation, packaged chart metadata, and chart-runtime smoke no diff --git a/docs/release-control/v6/internal/subsystems/security-privacy.md b/docs/release-control/v6/internal/subsystems/security-privacy.md index afaa7e26d..ae15d1df8 100644 --- a/docs/release-control/v6/internal/subsystems/security-privacy.md +++ b/docs/release-control/v6/internal/subsystems/security-privacy.md @@ -461,6 +461,12 @@ not bounce the operator back to GitHub `main` for section references that the running build already owns locally. Their Relay security section must also use the current Relay-and-higher entitlement boundary instead of stale Pro-only license wording. +Agent-based Proxmox hardening guidance in those same security docs must also +point operators to the current Infrastructure install or upgrade command +surface and to post-report verification on the relevant platform page or +Machines view. It must not revive the retired Settings Agents install-command +route or imply that v6 can prove upgraded-agent state before the agent has +authenticated and reported. That same governed settings trust boundary now also includes `frontend-modern/src/components/Settings/SecurityOverviewPanel.tsx`, `frontend-modern/src/components/Settings/QuickSecuritySetup.tsx`, diff --git a/docs/releases/RELEASE_NOTES_v6_RC1.md b/docs/releases/RELEASE_NOTES_v6_RC1.md index 5700b251e..5af750d75 100644 --- a/docs/releases/RELEASE_NOTES_v6_RC1.md +++ b/docs/releases/RELEASE_NOTES_v6_RC1.md @@ -41,7 +41,11 @@ After upgrading the server, update existing agents separately using the command `Settings -> Infrastructure -> Install on a host` -That is the supported v5-to-v6 crossover path for agent testing. +That is the supported v5-to-v6 crossover path for agent testing. The same +screen is used for first installs and in-place upgrades of existing v5 agents. +Version/status details appear in v6 only after an upgraded agent authenticates +and sends a fresh report, so the UI is not an offline inventory of every v5 +service that existed before the server upgrade. ### Do I need to uninstall existing v5 agents before updating them? diff --git a/frontend-modern/public/docs/CONFIGURATION.md b/frontend-modern/public/docs/CONFIGURATION.md index 720d1833f..b15aed62d 100644 --- a/frontend-modern/public/docs/CONFIGURATION.md +++ b/frontend-modern/public/docs/CONFIGURATION.md @@ -435,6 +435,18 @@ docker run --init -e HTTPS_ENABLED=true \ 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 | diff --git a/frontend-modern/public/docs/SECURITY.md b/frontend-modern/public/docs/SECURITY.md index 4d8963339..6f85540b7 100644 --- a/frontend-modern/public/docs/SECURITY.md +++ b/frontend-modern/public/docs/SECURITY.md @@ -43,8 +43,8 @@ environment where `PULSE_DOCKER=true`/`/.dockerenv` is detected. Preferred option (no SSH keys, no proxy wiring): -1. Install the unified agent (`pulse-agent`) on each Proxmox host with Proxmox integration enabled. - - Use the UI to generate an install command in **Settings → Agents → Installation commands**, or run: +1. Install or upgrade the unified agent (`pulse-agent`) on each Proxmox host with Proxmox integration enabled. + - Use the UI to generate an install or upgrade command in **Settings → Infrastructure → Install on a host**, or run: ```bash curl -fsSL http://pulse.example.com:7655/install.sh | \ sudo bash -s -- --url http://pulse.example.com:7655 --token --enable-proxmox @@ -105,7 +105,7 @@ ls /home/pulse/.ssh/id_ed25519* 2>/dev/null && echo "⚠️ SSH keys present" Verify temperature collection is agent-based: -- UI: **Settings → Agents** shows each Proxmox host connected and reporting. +- UI: the Proxmox or Machines page shows the host as agent-backed after the agent reports. - On each Proxmox host: ```bash systemctl status pulse-agent diff --git a/frontend-modern/src/content/help/alerts.ts b/frontend-modern/src/content/help/alerts.ts index bdff17ea9..6e2902142 100644 --- a/frontend-modern/src/content/help/alerts.ts +++ b/frontend-modern/src/content/help/alerts.ts @@ -35,15 +35,4 @@ export const alertsHelpContent: HelpContent[] = [ ], addedInVersion: 'v4.0.0', }, - { - id: 'alerts.thresholds.perGuest', - title: 'Per-Guest Threshold Overrides', - description: - 'Each VM or container can have custom threshold settings that override the defaults.\n\n' + - 'Use this to set different thresholds for:\n' + - '- Database servers (higher memory thresholds)\n' + - '- Build servers (higher CPU thresholds)\n' + - '- Development VMs (more relaxed thresholds)', - addedInVersion: 'v4.2.0', - }, ]; diff --git a/scripts/release_control/documentation_currentness_test.py b/scripts/release_control/documentation_currentness_test.py index 44948f195..bf1fbfac4 100644 --- a/scripts/release_control/documentation_currentness_test.py +++ b/scripts/release_control/documentation_currentness_test.py @@ -183,6 +183,68 @@ class DocumentationCurrentnessTest(unittest.TestCase): for phrase in forbidden_phrases: self.assertNotIn(phrase, content, msg=f"{rel} contains stale paid-cap wording: {phrase}") + def test_current_agent_docs_point_to_infrastructure_install_flow(self) -> None: + current_docs = ( + "README.md", + "SECURITY.md", + "docs/CENTRALIZED_MANAGEMENT.md", + "docs/DOCKER.md", + "docs/PBS.md", + "docs/UNIFIED_AGENT.md", + "docs/UPGRADE_v6.md", + "docs/releases/RELEASE_NOTES_v6_RC1.md", + "frontend-modern/public/docs/SECURITY.md", + ) + retired_agent_settings_paths = ( + "Settings → Unified Agents", + "Settings -> Unified Agents", + "Settings → Agents → Installation commands", + "Settings -> Agents -> Installation commands", + ) + for rel in current_docs: + content = read(rel) + for retired_path in retired_agent_settings_paths: + self.assertNotIn(retired_path, content, msg=f"{rel} still points at {retired_path}") + + root_readme = read("README.md") + upgrade_doc = read("docs/UPGRADE_v6.md") + unified_agent_doc = read("docs/UNIFIED_AGENT.md") + self.assertIn("Settings → Infrastructure → Install on a host", root_readme) + self.assertIn("v5-to-v6 agent upgrades", root_readme) + self.assertIn("first installs and in-place agent upgrades", upgrade_doc) + self.assertIn("supported v5-to-v6 agent upgrade path", unified_agent_doc) + self.assertIn("Version and outdated", upgrade_doc) + self.assertIn("only after the agent has successfully reported", upgrade_doc) + + def test_api_token_docs_explain_primary_token_and_revocation_safety(self) -> None: + config_doc = read("docs/CONFIGURATION.md") + public_config_doc = read("frontend-modern/public/docs/CONFIGURATION.md") + + expected_fragments = ( + "primary automation API token", + "separate from your web login password", + "Revoking a token is safe for Pulse itself", + "immediately breaks any agent", + "script, kiosk, or integration still using that token", + "create and install a replacement token first", + ) + for content in (config_doc, public_config_doc): + for fragment in expected_fragments: + self.assertIn(fragment, content) + + def test_webhook_docs_make_ntfy_service_picker_discoverable(self) -> None: + webhook_doc = read("docs/WEBHOOKS.md") + + self.assertIn("Click the current service label (Generic by default) to open the service picker", webhook_doc) + self.assertIn("choose **ntfy** in the service picker before entering the topic URL", webhook_doc) + + def test_alert_help_no_longer_documents_custom_threshold_overrides(self) -> None: + alerts_help = read("frontend-modern/src/content/help/alerts.ts") + + self.assertNotIn("alerts.thresholds.perGuest", alerts_help) + self.assertNotIn("Per-Guest Threshold Overrides", alerts_help) + self.assertNotIn("custom threshold settings that override the defaults", alerts_help) + if __name__ == "__main__": unittest.main() diff --git a/scripts/release_control/render_release_body_test.py b/scripts/release_control/render_release_body_test.py index dafd4aa11..7a5abc342 100644 --- a/scripts/release_control/render_release_body_test.py +++ b/scripts/release_control/render_release_body_test.py @@ -195,6 +195,18 @@ Old metadata section. self.assertIn("operator-state", changelog) self.assertIn("Pulse Mobile pairing for handoff", support_pack) + def test_shipped_rc1_notes_document_current_agent_upgrade_surface(self) -> None: + repo_root = Path(__file__).resolve().parents[2] + release_notes = (repo_root / "docs/releases/RELEASE_NOTES_v6_RC1.md").read_text( + encoding="utf-8" + ) + + self.assertIn("Settings -> Infrastructure -> Install on a host", release_notes) + self.assertIn("first installs and in-place upgrades", release_notes) + self.assertIn("after an upgraded agent authenticates", release_notes) + self.assertNotIn("Settings -> Agents -> Installation commands", release_notes) + self.assertNotIn("Settings → Agents → Installation commands", release_notes) + def test_agent_paradigm_release_notes_blurb_documents_distribution_path(self) -> None: """The agent-paradigm source draft must keep its honest scope: an integrator reading the blurb sees a published distribution diff --git a/scripts/tests/test_telemetry_adoption_report.py b/scripts/tests/test_telemetry_adoption_report.py index bd7aca9db..3fad94154 100644 --- a/scripts/tests/test_telemetry_adoption_report.py +++ b/scripts/tests/test_telemetry_adoption_report.py @@ -10,6 +10,7 @@ import unittest SCRIPT_DIR = Path(__file__).resolve().parents[1] +REPO_ROOT = SCRIPT_DIR.parent sys.path.insert(0, str(SCRIPT_DIR)) import telemetry_adoption_report as report @@ -94,6 +95,20 @@ class TelemetryAdoptionReportTest(unittest.TestCase): "6.0.0-rc.2", ) + def test_security_docs_use_current_agent_install_surface(self) -> None: + security_docs = ( + REPO_ROOT / "SECURITY.md", + REPO_ROOT / "frontend-modern/public/docs/SECURITY.md", + ) + + for path in security_docs: + with self.subTest(path=path.relative_to(REPO_ROOT)): + content = path.read_text(encoding="utf-8") + self.assertIn("Settings → Infrastructure → Install on a host", content) + self.assertIn("Proxmox or Machines page", content) + self.assertNotIn("Settings → Agents → Installation commands", content) + self.assertNotIn("Settings -> Agents -> Installation commands", content) + def test_summarize_rows_uses_latest_install_state_and_splits_release_validation(self) -> None: now = datetime.now(timezone.utc).replace(microsecond=0) rows = [