diff --git a/docs/FAQ.md b/docs/FAQ.md index 0a63961e8c..dc2a7fe2c6 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -15,11 +15,26 @@ See [INSTALL.md](INSTALL.md) for all options (Docker Compose, Kubernetes, system Go to **Settings → Infrastructure → Install on a host** for systems that should run the unified agent directly. -- **Recommended (agent setup)**: copy the generated install command and run it on the target host. +- **Agent setup**: use the generated command on the target host, entering the + token only at its private prompt, never in the command itself. Alternatively, + follow the [private-file preparation](UNIFIED_AGENT.md#private-file-installation-linux-macos-and-nas). - **Manual/API-backed platforms**: use **Settings → Infrastructure** (**Add infrastructure**) for systems such as Proxmox, PBS, PMG, or TrueNAS that connect over an API instead of running the agent locally. If you want Pulse to find servers automatically, open **Settings → Infrastructure**, select **Configure discovery** (it reads **Settings** once discovery is on), turn on **Automatic scanning** in the Discovery settings dialog, and then review discovered servers in the same section. +### Why does the copied agent command say no installation was attempted? + +That message describes a stopped preparation step, not the health of an agent +already installed. If it follows `stty: command not found`, the private token +prompt cannot prepare the terminal; having an interactive shell does not supply +the missing tool. Do not remove the privacy check, put a token in the command, +generate another token or uninstall the existing agent to get past it. + +Use the [agent command failure guide](UNIFIED_AGENT.md#copied-agent-command-fails-before-installation) +to distinguish the prompt, preflight and removal paths. Keep the original error +and existing state; a failed new command does not explain an earlier outage or +automatic-update failure. + ### How do I change the port? Distinguish Pulse's web listener from a container's published host port. diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md index 00b3f1e3b9..ea17e5d042 100644 --- a/docs/TROUBLESHOOTING.md +++ b/docs/TROUBLESHOOTING.md @@ -24,6 +24,10 @@ establish safe recovery. [agent authentication and reporting evidence](UNIFIED_AGENT.md#agent-lookup-returns-http-401-or-403) before replacing a token or reinstalling. Installer success is not an accepted report. +- **Copied agent install or removal command failed:** distinguish its + [preparation, private prompt and lifecycle action](UNIFIED_AGENT.md#copied-agent-command-fails-before-installation). + A stopped new command does not diagnose the existing agent. Keep state and + credentials; do not repeat removal or installation to obtain an error. - **Guest readings are missing or disagree:** distinguish [current, retained and unavailable disks](VM_DISK_MONITORING.md#current-retained-and-unavailable-disk-readings) from [guest memory pressure](TROUBLESHOOTING.md#guest-memory-is-high-but-available-memory-is-plentiful). diff --git a/docs/UNIFIED_AGENT.md b/docs/UNIFIED_AGENT.md index 6833ce0997..391ecce817 100644 --- a/docs/UNIFIED_AGENT.md +++ b/docs/UNIFIED_AGENT.md @@ -1040,6 +1040,49 @@ Set `--health-addr=""` or `PULSE_HEALTH_ADDR=off` to disable the health/metrics ## Troubleshooting +### Copied agent command fails before installation + +The copied Unix command prepares a download and, for installation, runs a +`--preflight-only` check before entering a private token prompt. Passing +preflight is **not installation** or accepted reporting. Read the original +error to identify which step stopped; do not run the command again to find out. + +- **`stty: command not found`, followed by a terminal-required message:** the + command's environment cannot run the tool used to prepare the private prompt. + A real terminal can still produce this error. It does not establish that the + token is invalid, that the target platform cannot run an agent, or that an + existing agent was removed. +- **A terminal-required or interrupted-input message without that error:** + token entry did not complete safely. Keep it separate from an installer's + later result; do not assume a missing tool from the terminal message alone. +- **`Safe-profile migration is unsupported on this platform`:** this concerns + the Linux safe-profile migration path. `--safe-profile-inspect` is a + standalone **read-only** action, not removal; `--uninstall` is a separate, + destructive action. The inspection can produce this error on an unsupported + platform such as QNAP. The error alone does not prove that removal ran. Do + not combine those flags or request a broader-privilege fallback. + +**Stop rather than bypass the private prompt.** Do not enable token echo, put a +secret in command arguments or an environment assignment, weaken TLS, generate +a replacement token or delete installed identity, connection files or logs. +For a planned first installation where the prompt is unavailable, the +[private-file installation](#private-file-installation-linux-macos-and-nas) +uses a private editor and `--token-file`, with the same download trust and +collector choices. It is not an instruction to repeat a failed recovery attempt. + +For an existing unavailable or outdated agent, retain that original failure +separately and use [reporting checks](#agent-lookup-returns-http-401-or-403) or +[update guidance](#auto-update). If removal was intentional, read its +[state-preservation and result checks](#uninstall) before another action. A +failed new command does not establish the cause of the earlier outage or +automatic-update failure. + +When reporting a command failure, share only the original failure stage and +manually redacted error. If the action is consequential, name only the flag +already used (`--safe-profile-inspect` or `--uninstall`), or say **unknown**. +Do not post the full copied command, a token, its file contents or service +configuration, and do not repeat installation or removal to complete a report. + ### Collect agent logs safely Prefer existing observations from the original failure. On a **Linux systemd diff --git a/frontend-modern/public/docs/FAQ.md b/frontend-modern/public/docs/FAQ.md index 0a63961e8c..dc2a7fe2c6 100644 --- a/frontend-modern/public/docs/FAQ.md +++ b/frontend-modern/public/docs/FAQ.md @@ -15,11 +15,26 @@ See [INSTALL.md](INSTALL.md) for all options (Docker Compose, Kubernetes, system Go to **Settings → Infrastructure → Install on a host** for systems that should run the unified agent directly. -- **Recommended (agent setup)**: copy the generated install command and run it on the target host. +- **Agent setup**: use the generated command on the target host, entering the + token only at its private prompt, never in the command itself. Alternatively, + follow the [private-file preparation](UNIFIED_AGENT.md#private-file-installation-linux-macos-and-nas). - **Manual/API-backed platforms**: use **Settings → Infrastructure** (**Add infrastructure**) for systems such as Proxmox, PBS, PMG, or TrueNAS that connect over an API instead of running the agent locally. If you want Pulse to find servers automatically, open **Settings → Infrastructure**, select **Configure discovery** (it reads **Settings** once discovery is on), turn on **Automatic scanning** in the Discovery settings dialog, and then review discovered servers in the same section. +### Why does the copied agent command say no installation was attempted? + +That message describes a stopped preparation step, not the health of an agent +already installed. If it follows `stty: command not found`, the private token +prompt cannot prepare the terminal; having an interactive shell does not supply +the missing tool. Do not remove the privacy check, put a token in the command, +generate another token or uninstall the existing agent to get past it. + +Use the [agent command failure guide](UNIFIED_AGENT.md#copied-agent-command-fails-before-installation) +to distinguish the prompt, preflight and removal paths. Keep the original error +and existing state; a failed new command does not explain an earlier outage or +automatic-update failure. + ### How do I change the port? Distinguish Pulse's web listener from a container's published host port. diff --git a/frontend-modern/public/docs/TROUBLESHOOTING.md b/frontend-modern/public/docs/TROUBLESHOOTING.md index 00b3f1e3b9..ea17e5d042 100644 --- a/frontend-modern/public/docs/TROUBLESHOOTING.md +++ b/frontend-modern/public/docs/TROUBLESHOOTING.md @@ -24,6 +24,10 @@ establish safe recovery. [agent authentication and reporting evidence](UNIFIED_AGENT.md#agent-lookup-returns-http-401-or-403) before replacing a token or reinstalling. Installer success is not an accepted report. +- **Copied agent install or removal command failed:** distinguish its + [preparation, private prompt and lifecycle action](UNIFIED_AGENT.md#copied-agent-command-fails-before-installation). + A stopped new command does not diagnose the existing agent. Keep state and + credentials; do not repeat removal or installation to obtain an error. - **Guest readings are missing or disagree:** distinguish [current, retained and unavailable disks](VM_DISK_MONITORING.md#current-retained-and-unavailable-disk-readings) from [guest memory pressure](TROUBLESHOOTING.md#guest-memory-is-high-but-available-memory-is-plentiful). diff --git a/frontend-modern/public/docs/UNIFIED_AGENT.md b/frontend-modern/public/docs/UNIFIED_AGENT.md index 6833ce0997..391ecce817 100644 --- a/frontend-modern/public/docs/UNIFIED_AGENT.md +++ b/frontend-modern/public/docs/UNIFIED_AGENT.md @@ -1040,6 +1040,49 @@ Set `--health-addr=""` or `PULSE_HEALTH_ADDR=off` to disable the health/metrics ## Troubleshooting +### Copied agent command fails before installation + +The copied Unix command prepares a download and, for installation, runs a +`--preflight-only` check before entering a private token prompt. Passing +preflight is **not installation** or accepted reporting. Read the original +error to identify which step stopped; do not run the command again to find out. + +- **`stty: command not found`, followed by a terminal-required message:** the + command's environment cannot run the tool used to prepare the private prompt. + A real terminal can still produce this error. It does not establish that the + token is invalid, that the target platform cannot run an agent, or that an + existing agent was removed. +- **A terminal-required or interrupted-input message without that error:** + token entry did not complete safely. Keep it separate from an installer's + later result; do not assume a missing tool from the terminal message alone. +- **`Safe-profile migration is unsupported on this platform`:** this concerns + the Linux safe-profile migration path. `--safe-profile-inspect` is a + standalone **read-only** action, not removal; `--uninstall` is a separate, + destructive action. The inspection can produce this error on an unsupported + platform such as QNAP. The error alone does not prove that removal ran. Do + not combine those flags or request a broader-privilege fallback. + +**Stop rather than bypass the private prompt.** Do not enable token echo, put a +secret in command arguments or an environment assignment, weaken TLS, generate +a replacement token or delete installed identity, connection files or logs. +For a planned first installation where the prompt is unavailable, the +[private-file installation](#private-file-installation-linux-macos-and-nas) +uses a private editor and `--token-file`, with the same download trust and +collector choices. It is not an instruction to repeat a failed recovery attempt. + +For an existing unavailable or outdated agent, retain that original failure +separately and use [reporting checks](#agent-lookup-returns-http-401-or-403) or +[update guidance](#auto-update). If removal was intentional, read its +[state-preservation and result checks](#uninstall) before another action. A +failed new command does not establish the cause of the earlier outage or +automatic-update failure. + +When reporting a command failure, share only the original failure stage and +manually redacted error. If the action is consequential, name only the flag +already used (`--safe-profile-inspect` or `--uninstall`), or say **unknown**. +Do not post the full copied command, a token, its file contents or service +configuration, and do not repeat installation or removal to complete a report. + ### Collect agent logs safely Prefer existing observations from the original failure. On a **Linux systemd diff --git a/frontend-modern/src/features/docs/__tests__/unifiedAgentBootstrapGuide.test.ts b/frontend-modern/src/features/docs/__tests__/unifiedAgentBootstrapGuide.test.ts index 498eab8734..64c5d2dabf 100644 --- a/frontend-modern/src/features/docs/__tests__/unifiedAgentBootstrapGuide.test.ts +++ b/frontend-modern/src/features/docs/__tests__/unifiedAgentBootstrapGuide.test.ts @@ -7,13 +7,14 @@ import { renderDocMarkdown } from '../docMarkdown'; const frontendRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../..'); const source = readFileSync(path.join(frontendRoot, '../docs/UNIFIED_AGENT.md'), 'utf8'); -function setupSection() { +function docSection(markdown: string, docPath: string, id: string) { const article = document.createElement('article'); - article.innerHTML = renderDocMarkdown(source, 'UNIFIED_AGENT'); - const heading = article.querySelector('#private-file-installation-linux-macos-and-nas')!; + article.innerHTML = renderDocMarkdown(markdown, docPath); + const heading = article.querySelector(`#${id}`); + expect(heading, `${docPath}#${id} must be a real rendered destination`).not.toBeNull(); const section = document.createElement('section'); for ( - let node = heading.nextElementSibling; + let node = heading!.nextElementSibling; node && !/^H[23]$/.test(node.tagName); node = node.nextElementSibling ) { @@ -22,6 +23,10 @@ function setupSection() { return section; } +function setupSection() { + return docSection(source, 'UNIFIED_AGENT', 'private-file-installation-linux-macos-and-nas'); +} + describe('Unified Agent private-file setup help', () => { it('renders preparation and download as self-contained fences before installation', () => { const section = setupSection(); @@ -61,3 +66,86 @@ describe('Unified Agent private-file setup help', () => { ); }); }); + +describe('Stopped agent-command help', () => { + const failureId = 'copied-agent-command-fails-before-installation'; + const faq = readFileSync(path.join(frontendRoot, '../docs/FAQ.md'), 'utf8'); + const troubleshooting = readFileSync( + path.join(frontendRoot, '../docs/TROUBLESHOOTING.md'), + 'utf8', + ); + const normalizedText = (section: HTMLElement) => section.textContent!.replace(/\s+/g, ' '); + + it('makes the same failure destination reachable from setup and troubleshooting', () => { + for (const [name, markdown] of [ + ['FAQ', faq], + ['TROUBLESHOOTING', troubleshooting], + ]) { + const article = document.createElement('article'); + article.innerHTML = renderDocMarkdown(markdown, name); + expect( + article.querySelector(`a[data-doc-link][href="/docs/UNIFIED_AGENT#${failureId}"]`), + ).not.toBeNull(); + expect(readFileSync(path.join(frontendRoot, `public/docs/${name}.md`), 'utf8')).toBe( + markdown, + ); + } + const setup = docSection(faq, 'FAQ', 'how-do-i-add-a-node'); + expect( + setup.querySelector( + 'a[href="/docs/UNIFIED_AGENT#private-file-installation-linux-macos-and-nas"]', + ), + ).not.toBeNull(); + expect(normalizedText(setup)).toContain('token only at its private prompt'); + }); + + it('distinguishes stopped prompt, read-only inspection and destructive removal', () => { + const section = docSection(source, 'UNIFIED_AGENT', failureId); + const text = normalizedText(section); + expect(section.querySelectorAll(':scope > ul > li')).toHaveLength(3); + for (const fact of [ + 'Passing preflight is not installation', + 'A real terminal can still produce this error', + 'do not assume a missing tool from the terminal message alone', + 'standalone read-only action, not removal', + 'The error alone does not prove that removal ran', + 'failed new command does not establish the cause of the earlier outage', + ]) { + expect(text).toContain(fact); + } + const producer = readFileSync( + path.join(frontendRoot, 'src/utils/agentInstallCommand.ts'), + 'utf8', + ); + expect(producer).toContain('--preflight-only'); + expect(producer).toContain('--token-file "$token_file"'); + const installer = readFileSync(path.join(frontendRoot, '../scripts/install.sh'), 'utf8'); + expect(installer).toContain('--safe-profile-inspect is a standalone read-only action'); + expect(installer).toContain('Safe-profile migration is unsupported on this platform'); + }); + + it('links to existing private preparation and recovery without another executable recipe', () => { + const section = docSection(source, 'UNIFIED_AGENT', failureId); + const article = document.createElement('article'); + article.innerHTML = renderDocMarkdown(source, 'UNIFIED_AGENT'); + for (const id of [ + 'private-file-installation-linux-macos-and-nas', + 'agent-lookup-returns-http-401-or-403', + 'auto-update', + 'uninstall', + ]) { + expect(section.querySelector(`a[href="#${id}"]`)).not.toBeNull(); + expect(article.querySelector(`#${id}`)).not.toBeNull(); + } + expect(section.querySelector('pre')).toBeNull(); + for (const boundary of [ + 'Stop rather than bypass the private prompt', + 'not an instruction to repeat a failed recovery attempt', + 'name only the flag already used', + 'Do not post the full copied command', + 'do not repeat installation or removal to complete a report', + ]) { + expect(normalizedText(section)).toContain(boundary); + } + }); +});