diff --git a/ci/platform-matrix.json b/ci/platform-matrix.json index 8b4ac2b0f9..4e2276b039 100644 --- a/ci/platform-matrix.json +++ b/ci/platform-matrix.json @@ -1,7 +1,7 @@ { "$comment": "SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\nSPDX-License-Identifier: Apache-2.0\n\nSingle source of truth for NemoClaw launch claims and platform support. Covers platforms, inference providers, supported agents, messaging integrations, and deployment paths. Scripts read this to generate README and docs tables. QA/CI update platform/provider rows; the engineering owner reviews other rows. Docs are derived.", "version": "1.1", - "updated": "2026-07-17", + "updated": "2026-07-21", "project_status": { "stage": "alpha", @@ -271,7 +271,7 @@ { "name": "Hosted SaaS / managed NemoClaw", "status": "unsupported", - "notes": "There is no managed offering. Supported deployment paths are Local CLI onboard, Remote GPU with Brev CLI, and Brev web UI." + "notes": "There is no managed offering. Supported deployment paths are local CLI onboarding and headless Linux server onboarding." }, { "name": "Native provider integrations not in the Providers table", @@ -325,14 +325,9 @@ "notes": "Run `$$nemoclaw onboard` on a tested platform with Docker available locally. Primary path." }, { - "name": "Remote GPU with Brev CLI", + "name": "Headless Linux server", "status": "caveated", - "notes": "Legacy compatibility wrapper provisions a Brev VM, installs Docker + NVIDIA Container Toolkit, and runs `$$nemoclaw onboard` on that host. Defaults to GCP; override with `NEMOCLAW_BREV_PROVIDER`. The preferred path is the standard installer followed by `$$nemoclaw onboard` after the VM is reachable." - }, - { - "name": "Brev web UI", - "status": "tested", - "notes": "Browser-driven launcher provisions a Brev-managed Linux VM with Docker, the OpenShell runtime, a NemoClaw sandbox running OpenClaw, inference routing, and the OpenClaw dashboard." + "notes": "Provision a tested Linux host, connect over SSH, run the standard installer and `$$nemoclaw onboard`, and keep dashboards bound to loopback behind SSH port forwarding. Automatic recovery after a host reboot is not guaranteed; follow the documented manual recovery flow." } ] } diff --git a/docs/about/overview.mdx b/docs/about/overview.mdx index 0996e8a561..d8af765c71 100644 --- a/docs/about/overview.mdx +++ b/docs/about/overview.mdx @@ -82,7 +82,7 @@ Use NemoClaw for these use cases. | Always-on assistant | Run a sandboxed agent with controlled network access and operator-approved egress. | | Terminal coding harness | Run `dcode` inside an OpenShell sandbox with host-owned inference credentials and a managed terminal workflow. | | Sandboxed testing | Test agent behavior in a locked-down environment before granting broader permissions. | -| Remote GPU deployment | Deploy a sandboxed agent to a remote GPU instance for persistent operation. | +| Headless server deployment | Run a sandboxed agent on a remote Linux server through SSH. Refer to [Deploy to a Headless Server](../deployment/deploy-to-headless-server). | ## Next Steps diff --git a/docs/changelog/2026-07-08.mdx b/docs/changelog/2026-07-08.mdx index 167af4a61a..84d679ad17 100644 --- a/docs/changelog/2026-07-08.mdx +++ b/docs/changelog/2026-07-08.mdx @@ -34,7 +34,7 @@ NemoClaw v0.0.78 adds opt-in thread-scoped auto-approval and policy-routed repos For more information, refer to [Install OpenClaw Plugins](/user-guide/openclaw/manage-sandboxes/install-openclaw-plugins), [Recover and Rebuild Sandboxes](/user-guide/openclaw/manage-sandboxes/operate-sandboxes/recover-and-rebuild-sandboxes), and [Extension Taxonomy and SDK Readiness](/user-guide/openclaw/reference/extension-taxonomy-sdk-readiness). - Remote dashboard and shutdown flows now provide copyable SSH port-forward hints, recognize a live untracked loopback forward before rolling back onboarding, align the Hermes WebUI with the resolved host dashboard port, and make the deprecated full stop attempt agent-owned host-forward cleanup before safely releasing an unshared, ownership-verified OpenShell gateway port. Supervisor-owned Hermes runtime processes remain under sandbox control, and OpenClaw Slack and compact-QR WhatsApp runtime hooks compose safely when both are enabled. - For more information, refer to [Deploy to a Remote GPU Host](/user-guide/openclaw/deployment/deploy-to-remote-gpu), [NemoClaw Quickstart with Hermes](/user-guide/hermes/get-started/quickstart), [Choose Messaging Channels](/user-guide/openclaw/manage-sandboxes/messaging-channels/choose-messaging-channels), and [NemoClaw CLI Commands Reference](/user-guide/openclaw/reference/commands). + For more information, refer to [Deploy to a Headless Server](/user-guide/openclaw/deployment/deploy-to-headless-server), [NemoClaw Quickstart with Hermes](/user-guide/hermes/get-started/quickstart), [Choose Messaging Channels](/user-guide/openclaw/manage-sandboxes/messaging-channels/choose-messaging-channels), and [NemoClaw CLI Commands Reference](/user-guide/openclaw/reference/commands). - Starter prompts now bind the local credential form to an authenticated one-shot helper with immutable commit and SHA-256 pins, explicit isolated or account-home execution profiles, denial of ambient process-control variables, and a preview, edit, and confirm flow. This keeps credential collection behind verified helper and approved-command boundaries. For more information, refer to [NemoClaw Quickstart with OpenClaw](/user-guide/openclaw/get-started/quickstart) and [Use NemoClaw Docs with Your Coding Agents](/user-guide/openclaw/resources/agent-skills). diff --git a/docs/deployment/brev-web-ui.mdx b/docs/deployment/brev-web-ui.mdx deleted file mode 100644 index 58553d4e95..0000000000 --- a/docs/deployment/brev-web-ui.mdx +++ /dev/null @@ -1,164 +0,0 @@ ---- -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -title: "Launch NemoClaw with the Brev Web UI" -sidebar-title: "Brev Web UI" -description: "Launch a hosted NemoClaw sandbox from the Brev web interface without installing the CLI or using a local GPU." -description-agent: "Guides users through deploying NemoClaw with the Brev web UI. Use when a user wants to try NemoClaw without installing the CLI, or asks how to get started on Brev." -keywords: ["nemoclaw brev web ui", "nemoclaw getting started", "brev quickstart", "nvidia nemotron agent"] -content: - type: "get_started" ---- -Use the Brev web UI to launch a hosted NemoClaw sandbox from your browser. -Brev provisions a remote VM, configures inference, starts OpenClaw inside an OpenShell sandbox, and opens the OpenClaw dashboard. - - -Use this guide when you want to try NemoClaw without installing the CLI or using a local GPU. -If you want to manage the remote host from a terminal, refer to [Deploy to a Remote GPU Instance](deploy-to-remote-gpu). - - -## What This Flow Creates - -The Brev web flow creates these resources: - -- A Brev-managed Linux VM. -- Docker and the OpenShell runtime on that VM. -- A NemoClaw sandbox running OpenClaw. -- Inference routing for the provider you select during setup. -- A browser-accessible OpenClaw dashboard. - -## Prerequisites - -- An NVIDIA Brev account at [brev.nvidia.com](https://brev.nvidia.com). -- An NVIDIA API key from [build.nvidia.com](https://build.nvidia.com/settings/api-keys) if you use the default NVIDIA Cloud provider. - -You do not need to install local software for this flow. - -## Get Your NVIDIA API Key - -If you already have an NVIDIA API key, skip this section. -Otherwise, follow these steps to generate a new key: - -1. Go to [build.nvidia.com](https://build.nvidia.com). -2. Sign in or create an account. -3. Click your profile icon in the upper-right corner. -4. Select **API Keys**. -5. Click **Generate API Key**. -6. Copy the key. It starts with `nvapi-`. - -Keep this key ready for the next step. - -## Launch NemoClaw from Brev - -Use the [NemoClaw Brev launchable](https://brev.nvidia.com/launchable/deploy/now?launchableID=env-3Azt0aYgVNFEuz7opyx3gscmowS) to launch a NemoClaw sandbox from your browser. - -1. Open the [NemoClaw Brev launchable](https://brev.nvidia.com/launchable/deploy/now?launchableID=env-3Azt0aYgVNFEuz7opyx3gscmowS) and sign in if prompted. -2. Review the instance type, cloud provider, and estimated hourly cost on the NemoClaw setup page. -3. Click **Deploy NemoClaw**. - -The deployment panel on the right shows progress while Brev deploys the CPU instance and prepares VM mode. -Keep this page open until the deployment completes. -When the panel shows the **NemoClaw** button, click it to open the agent setup page. - -## Configure Your Agent - -The setup page walks you through three stages: **Configure**, **Setup**, and **Launch**. - -### Configure - -The Configure stage opens the **Connect to AI** screen. -Use the NVIDIA Cloud provider shown on this screen. - -1. Leave **NVIDIA Cloud** selected. -2. Paste your `nvapi-` API key. -3. Click **Create Agent**. - - -The **Show Other Providers** dropdown appears below the **NVIDIA Cloud** card. -Click it to expand the provider list. -The expanded list includes **OpenAI**, **Anthropic**, and **Google Gemini**. -For these providers, get the API key from the provider console before you create the agent. - - -### Setup - -NemoClaw configures the remote host and sandbox automatically. -This stage usually takes about 5 minutes. - -During setup, NemoClaw installs the runtime, prepares the sandboxed agent environment, and configures inference routing for the provider you selected. - -### Launch - -When setup finishes, Brev shows the following confirmation: - -```text -AGENT CREATED SUCCESSFULLY -Your agent is running in a secure sandbox and ready to use. - -Agent: agent -Model: nemotron-3-super-120b -Provider: NVIDIA Cloud -``` - -Click **Chat With Agent** to open the OpenClaw dashboard. - - -The dashboard might initially show a **Pairing required** warning. -This means the gateway is still completing pairing in the background. -Wait a few minutes for pairing to finish automatically. -Refresh the dashboard to check whether the warning has cleared and the dashboard has connected. -If pairing does not finish, go to the **Overview** page in the OpenClaw UI, find the **Gateway Access** panel, and click **Connect**. - - -## Start a Chat - -Use the dashboard chat box to send your first message: - -```text -Hello! What can you do for me? What skills do you have available? -``` - -The agent reads its workspace files and introduces itself. -The starter workspace includes these example skills: - -- **Weather** gets current weather and forecasts. -- **Healthcheck** runs security audit and hardening checks. -- **Skill-Creator** creates new custom skills. - -## Personalize Agent Memory - -The agent starts with an empty `USER.md` file. -Ask the agent to add details that help it personalize future responses. - -In the chat, type the following: - -```text -Please update my USER.md file with the following: -Name: [your name] -Timezone: [your timezone, such as "America/New_York"] -Notes: [what you are working on] -``` - -The agent writes this information to its workspace so it can use it across sessions on the same sandbox. - -## Stop Your Instance When Done - -Brev continues billing while the instance runs. -Stop the instance when you finish experimenting. - -1. Go back to [brev.nvidia.com](https://brev.nvidia.com). -2. Click **GPUs** in the navigation bar. -3. Find your NemoClaw instance. -4. Click **Stop**. - -Check the Brev UI for the current hourly price before leaving the instance running. - -## Next Steps - -After your agent is running, explore these related tasks: - -- [Choose Messaging Channels](../manage-sandboxes/messaging-channels/choose-messaging-channels) to learn how to connect Telegram, Slack, or Discord. -- [Switch Inference Providers](../inference/manage-inference/switch-providers) to learn how to change the model provider after setup. -- [Monitor Sandbox Activity](../monitoring/monitor-sandbox-activity) to learn how to inspect sandbox health and logs. -- [Deploy to a Remote GPU Instance](deploy-to-remote-gpu) to learn how to deploy NemoClaw to a remote GPU instance using the CLI. -- [Troubleshooting](../reference/troubleshooting) to learn how to fix common setup and runtime issues. diff --git a/docs/deployment/deploy-to-headless-server.mdx b/docs/deployment/deploy-to-headless-server.mdx new file mode 100644 index 0000000000..79c7ef6937 --- /dev/null +++ b/docs/deployment/deploy-to-headless-server.mdx @@ -0,0 +1,515 @@ +--- +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +title: "Deploy NemoClaw to a Headless Server" +sidebar-title: "Deploy to a Headless Server" +description: "Install, verify, operate, and recover NemoClaw on a remote Linux server through SSH." +description-agent: "Explains unattended NemoClaw onboarding, loopback dashboard access, readiness checks, state boundaries, updates, and manual reboot recovery on a remote Linux server. Use when deploying NemoClaw to a headless host, VPS, or server through SSH." +keywords: ["nemoclaw headless server", "nemoclaw vps", "nemoclaw ssh deployment"] +content: + type: "how_to" +skill: + priority: 20 +--- +Run NemoClaw on a remote Linux server through SSH without exposing the OpenShell gateway or dashboard to the network. +This guide covers unattended onboarding, verified readiness, routine updates, and manual recovery after a host reboot. + + +A Linux VM that you provision through Brev is one example of a headless server. +These instructions also apply to Linux hosts on other clouds, VPS services, or on-premises infrastructure. +NemoClaw setup starts after server provisioning and does not depend on Brev or its web UI. + + + +NemoClaw does not guarantee that Docker, the OpenShell gateway, sandboxes, tunnels, or host forwards start automatically after a host reboot. +Use the [manual recovery sequence](#recover-after-a-host-reboot) after each reboot. +Do not install an unofficial service unit as a substitute for this sequence. + + +## Check the Server + +Use a Linux host that meets the supported [NemoClaw prerequisites](../get-started/prerequisites). +The primary tested server path is Linux with Docker. + +| Resource | Minimum | Recommended | +|---|---:|---:| +| CPU | 4 vCPU | 4 or more vCPU | +| RAM | 8 GB | 16 GB | +| Free disk | 20 GB | 40 GB | + +The image build, Docker daemon, and OpenShell gateway can exhaust a smaller host during onboarding. +If the host has less than 8 GB of RAM, configure at least 8 GB of swap before onboarding. + +Run these checks from the remote host: + +```bash +uname -m +. /etc/os-release +printf '%s %s\n' "$ID" "$VERSION_ID" +docker info +docker_root=$(docker info --format '{{.DockerRootDir}}') +df -h "$HOME" "$docker_root" +free -h +swapon --show +``` + +`docker info` must succeed for the same account that runs NemoClaw. +Membership in the `docker` group grants root-level control of the Docker daemon, so grant it only to trusted accounts. + +The host firewall must allow the outbound DNS, HTTPS, image-registry, package-registry, and inference-provider traffic selected during onboarding. +The OpenShell policy controls traffic from the sandbox and does not replace the host firewall. +Keep inbound dashboard and OpenShell gateway ports closed when you use SSH forwarding. + +## Keep Remote Access on Loopback + +The OpenShell gateway binds to `127.0.0.1` by default. +Dashboard and API forwards also stay on loopback outside WSL unless you explicitly change the bind setting. + +Connect to the server from your workstation: + +```bash +ssh @ +``` + + + +After onboarding, keep the server-side forward on loopback and create a second SSH tunnel from your workstation. +The default dashboard port is `18789`, but NemoClaw can select the next free port through `18799`. +Use the port printed by `$$nemoclaw headless-agent dashboard-url`. + +```bash +ssh -N -L 18789:127.0.0.1:18789 @ +``` + +Then open the loopback URL printed by `$$nemoclaw headless-agent dashboard-url --quiet` on your workstation. +Replace both `18789` values when NemoClaw selected another port. + + + + +Deep Agents Code is a terminal runtime and has no dashboard port. +Use `$$nemoclaw headless-agent connect` through the SSH session, then run `dcode` inside the sandbox. + + + +Do not open port `8080` for remote access. +Do not bind the dashboard to every interface when an SSH tunnel meets the access requirement. + +## Protect a Long Onboarding Run + +Run onboarding inside a `tmux` or `screen` session so an SSH disconnect does not terminate the host process. +To start a `tmux` session, run: + +```bash +tmux new-session -s nemoclaw-onboard +``` + +Detach with `Ctrl-b`, then `d` while onboarding continues. +After you reconnect through SSH, reattach to the session: + +```bash +tmux attach-session -t nemoclaw-onboard +``` + +To use `screen` instead, start a session: + +```bash +screen -S nemoclaw-onboard +``` + +Detach with `Ctrl-a`, then `d` while onboarding continues. +After you reconnect through SSH, reattach to the session: + +```bash +screen -r nemoclaw-onboard +``` + +Do not enable shell tracing with `set -x` in a session that contains credentials. +Do not save the session transcript when it can contain a dashboard URL or token. + +If the onboarding process exited after it saved a resumable session, export the same required credential variables and resume it: + +```bash +NEMOCLAW_NON_INTERACTIVE=1 \ +NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE=1 \ +$$nemoclaw onboard --resume --yes-i-accept-third-party-software --yes +``` + +`--resume` uses the provider, model, sandbox name, agent, and completed non-secret choices from the saved session. +Raw credentials are not stored in the onboarding session. +If resume reports a missing credential variable, inject that variable again and repeat the command. +Use `--fresh` only when you intend to discard the saved onboarding session and start again. + +## Run Unattended Onboarding + +Select a reviewed NemoClaw commit and set its full 40-character SHA before unattended installation. +The example uses that SHA in both the immutable bootstrap URL and `NEMOCLAW_INSTALL_REF`, so the bootstrap and cloned installer payload come from the same repository state. +Do not use the mutable `lkg` or `latest` references as the primary install source for a persistent server. +Inject provider credentials from your secret manager into the host environment before you run this example. +The example fails before the network install if the commit SHA or `NVIDIA_INFERENCE_API_KEY` is missing or invalid. + + + +```bash +export NEMOCLAW_AGENT=openclaw +``` + + + + +```bash +export NEMOCLAW_AGENT=hermes +``` + + + + +```bash +export NEMOCLAW_AGENT=langchain-deepagents-code +``` + + + +```bash +export NEMOCLAW_INSTALL_REF="" +: "${NVIDIA_INFERENCE_API_KEY:?Inject NVIDIA_INFERENCE_API_KEY from a secret store}" +[[ "$NEMOCLAW_INSTALL_REF" =~ ^[0-9a-f]{40}$ ]] || { + echo "NEMOCLAW_INSTALL_REF must be a reviewed full commit SHA" >&2 + exit 1 +} +export NEMOCLAW_PROVIDER=build +export NEMOCLAW_SANDBOX_NAME=headless-agent +export NEMOCLAW_POLICY_TIER=balanced + +curl -fsSL "https://raw.githubusercontent.com/NVIDIA/NemoClaw/${NEMOCLAW_INSTALL_REF}/install.sh" | \ + NEMOCLAW_INSTALL_REF="$NEMOCLAW_INSTALL_REF" \ + NEMOCLAW_NON_INTERACTIVE=1 \ + NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE=1 \ + NEMOCLAW_AGENT="$NEMOCLAW_AGENT" \ + NEMOCLAW_PROVIDER="$NEMOCLAW_PROVIDER" \ + NVIDIA_INFERENCE_API_KEY="$NVIDIA_INFERENCE_API_KEY" \ + NEMOCLAW_SANDBOX_NAME="$NEMOCLAW_SANDBOX_NAME" \ + NEMOCLAW_POLICY_TIER="$NEMOCLAW_POLICY_TIER" \ + NEMOCLAW_WEB_SEARCH_PROVIDER=none \ + bash +``` + +Pass every onboarding `NEMOCLAW_*` value on the `bash` side of the pipeline so the downloaded installer can read it. +The commit pin also appears in the bootstrap URL so no mutable tag selects the code that enters the pipeline. +Do not put a credential before `curl`, in a command-line argument, or in a committed script. +Unset the credential from the interactive shell after onboarding completes: + +```bash +unset NVIDIA_INFERENCE_API_KEY +``` + +Use the matching credential variable when you select another provider. +Refer to the [CLI commands reference](../reference/commands#nemoclaw-onboard) for provider-specific variables and accepted values. + +| Variable | Requirement | Secret | Purpose | +|---|---|---|---| +| `NEMOCLAW_NON_INTERACTIVE=1` | Required for unattended use | No | Disables interactive onboarding prompts. | +| `NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE=1` | Required for unattended use | No | Records explicit acceptance for the current run. | +| `NEMOCLAW_AGENT` | Required when the agent must not use the default | No | Selects `openclaw`, `hermes`, or `langchain-deepagents-code`. | +| `NEMOCLAW_PROVIDER` | Required for a deterministic provider selection | No | Selects the inference provider. | +| Provider credential | Required for providers that authenticate | Yes | Registers the credential with the OpenShell gateway. | +| `NEMOCLAW_SANDBOX_NAME` | Required for a deterministic sandbox name | No | Names the sandbox and its host registry entry. | +| `NEMOCLAW_POLICY_TIER` | Optional, default `balanced` | No | Selects the initial policy tier. | +| `NEMOCLAW_WEB_SEARCH_PROVIDER` | Optional | No | Selects a supported search provider or `none`. | +| `NEMOCLAW_INSTALL_REF` | Required for this unattended server flow | No | Selects the reviewed full commit SHA used by both the bootstrap URL and installer. | +| `NEMOCLAW_INSTALL_TAG` | Optional convenience path, default `lkg` | No | Selects a tag only when `NEMOCLAW_INSTALL_REF` is unset. Mutable tags are not the primary persistent-server path. | + +## Verify Readiness + +Do not use process presence as the sandbox-ready signal. +The authoritative OpenShell signal is the exact row for `headless-agent` in phase `Ready` or `Running`. +The substring `NotReady` is not a ready state. + +Run each verification on the remote host: + +```bash +openshell sandbox list +$$nemoclaw headless-agent status +$$nemoclaw headless-agent connect --probe-only +``` + +`$$nemoclaw headless-agent status` exits nonzero when the sandbox, gateway, local container, or authoritative inference route is not verified. +Its main `Inference` line probes `https://inference.local/v1/models` from inside the sandbox. +HTTP `200` through `499` reports `reachable`, while HTTP `500` through `599` reports `unhealthy`. + +`connect --probe-only` waits up to 300 seconds by default for a cold sandbox to become ready. +It then verifies or repairs the in-sandbox agent process and host forwards without opening a shell. +It does not restart or replace the shared host OpenShell gateway. + +Readiness requires all of these results: + +- The exact OpenShell sandbox row is `Ready` or `Running`. +- `$$nemoclaw headless-agent status` exits with status `0` and reports the inference route as `reachable`. +- `$$nemoclaw headless-agent connect --probe-only` exits with status `0`. + +## Access the Dashboard and API + +Retrieve dashboard URLs and API tokens only when you need them. +Do not write either value to logs, shell history, support bundles, or version control. + + + +Print the complete authenticated dashboard URL: + +```bash +$$nemoclaw headless-agent dashboard-url --quiet +``` + +Use the raw gateway token only for automation that cannot use the tokenized dashboard URL. +This example authenticates the supported Control UI configuration endpoint on the server loopback interface: + +```bash +TOKEN=$($$nemoclaw headless-agent gateway-token --quiet) +curl -fsS -H "Authorization: Bearer $TOKEN" \ + "http://127.0.0.1:18789/__openclaw/control-ui-config.json" +unset TOKEN +``` + +An unauthenticated request to this endpoint returns `401`. +The static path `controlui.bootstrap.config.json` does not exist and returns `404`. + + + + +Print the Hermes dashboard URL: + +```bash +$$nemoclaw headless-agent dashboard-url --quiet +``` + +The Hermes OpenAI-compatible API uses the loopback forward on port `8642`. +For a Hermes sandbox, `gateway-token` is agent-aware and retrieves `API_SERVER_KEY` through the registered `bearer_token` web-auth contract. +Use it as a bearer token, then clear the shell variable: + +```bash +TOKEN=$($$nemoclaw headless-agent gateway-token --quiet) +curl -fsS -H "Authorization: Bearer $TOKEN" \ + "http://127.0.0.1:8642/v1/models" +unset TOKEN +``` + + + + +Deep Agents Code does not expose a dashboard URL or gateway token. +Model traffic uses the OpenShell-managed `inference.local` route. + + + + + +OpenClaw generates a new gateway token each time the sandbox container starts. +Retrieve the dashboard URL or token again after the container restarts or a replacement sandbox is created. + + + + +Hermes preserves its `API_SERVER_KEY` when the same sandbox container restarts. +A replacement sandbox generates a new `API_SERVER_KEY`. +Retrieve the dashboard URL or token again after a replacement sandbox is created. + + + +## Understand Credential and State Boundaries + +NemoClaw separates provider credentials, host metadata, and sandbox state. + +| Boundary | Stored data | Rebuild behavior | +|---|---|---| +| OpenShell gateway | Provider credentials and provider registrations | Reused when the gateway and exact provider binding remain available. Raw values cannot be read back. | +| `~/.nemoclaw/` on the host | Sandbox registry, provider names, policy metadata, and onboarding session state | Preserved by normal updates. The directory contains metadata, not provider credential values. | +| Agent configuration in the sandbox | Generated inference routes, OpenShell resolver placeholders, and agent-specific settings | Regenerated from host registry and OpenShell state. Generated files are not a credential store. | +| Manifest-defined sandbox state | Agent workspace, memory, skills, and agent-specific durable files | Snapshotted and restored according to the selected agent manifest. | +| Arbitrary environment and profile edits | Direct shell exports and edits outside the manifest contract | Not guaranteed. Export host variables again and use documented host commands for durable configuration. | + +NemoClaw holds an environment-supplied provider credential in memory while it registers the value with OpenShell. +The sandbox receives a resolver placeholder, and OpenShell substitutes the raw value at egress. +For details, refer to [Credential Storage](../security/credential-storage). + +Install a declarative agent skill through the supported host command: + +```bash +$$nemoclaw headless-agent skill install ./my-skill/ +``` + +The skill directory must contain `SKILL.md` with a `name` field in its YAML frontmatter. +Do not assume that packages, shell exports, or profile edits made by a skill survive a rebuild. + +## Add a Least-Privilege Policy + +Use an additive custom preset when the sandbox needs a destination that the current policy does not allow. +Scope the host, port, method, path, and executable to the smallest required set. + +Save a reviewed preset as `./presets/internal-status.yaml`, preview it, then apply it without a prompt: + +```bash +$$nemoclaw headless-agent policy-add --from-file ./presets/internal-status.yaml --dry-run +$$nemoclaw headless-agent policy-add --from-file ./presets/internal-status.yaml --yes +$$nemoclaw headless-agent policy-list +``` + +`--yes` skips the confirmation prompt but does not skip schema, destination, or SSRF validation. +NemoClaw records the full validated YAML content in the sandbox registry. +Snapshot restore and rebuild replay that recorded preset even when the original host file is unavailable. +Keep the source YAML in your configuration repository so operators can review and change it. +For the preset schema and removal workflow, refer to [Network Policies](../reference/network-policies). + +## Plan for Updates and Rebuilds + +Create a named snapshot before host maintenance or a manual update: + +```bash +$$nemoclaw headless-agent snapshot create --name before-maintenance +export NEMOCLAW_INSTALL_REF="" +[[ "$NEMOCLAW_INSTALL_REF" =~ ^[0-9a-f]{40}$ ]] || { + echo "NEMOCLAW_INSTALL_REF must be a reviewed full commit SHA" >&2 + exit 1 +} +curl -fsSL "https://raw.githubusercontent.com/NVIDIA/NemoClaw/${NEMOCLAW_INSTALL_REF}/install.sh" | \ + NEMOCLAW_INSTALL_REF="$NEMOCLAW_INSTALL_REF" \ + bash +$$nemoclaw upgrade-sandboxes --check +``` + +Use a newly reviewed commit SHA for each planned update instead of relying on the mutable installer default. +The installer requires current backups before it changes an existing managed installation. +Use `$$nemoclaw headless-agent rebuild` when you need the current agent image while preserving supported state. + +| Item | Same-container restart | Snapshot and restore | Rebuild or sandbox upgrade | +|---|---|---|---| +| Provider configuration | Preserved | Provider names are recorded, but raw credentials are not in the snapshot | Regenerated from registry and OpenShell provider state | +| Custom preset YAML applied with `policy-add` | Preserved in registry | Stored content is included in snapshot metadata | Replayed from stored content | +| Manifest-defined user and agent state | Preserved | Preserved | Preserved when backup and restore succeed | +| Arbitrary files outside manifest state | Usually remain in the same writable layer | Not preserved | Not preserved | +| Manually installed system or global packages | Usually remain in the same writable layer | Not preserved | Not preserved | +| Direct edits to generated profile, config, or environment files | May remain until regeneration | Agent-specific and usually excluded or filtered | Regenerated or filtered by the current manifest | + +| OpenClaw gateway token | Rotated when the container starts | Not captured; a replacement sandbox generates a new token | Rotated for the replacement sandbox | + + +| Hermes `API_SERVER_KEY` | Preserved | Not captured; a replacement sandbox generates a new token | Rotated for the replacement sandbox | + +| Host tunnel process | Not applicable to a container restart | Not preserved | Not preserved | +| Dashboard, API, messaging, and agent forwards | Preserved only while their host processes remain active | Re-established during supported recovery | Re-established and verified after rebuild | + +Snapshot only the state that the current agent manifest declares. +Download any required file outside that contract before a destructive operation. +Refer to [Understand Sandbox State](../manage-sandboxes/state-and-backups/understand-sandbox-state) and [Create and Restore Snapshots](../manage-sandboxes/state-and-backups/create-and-restore-snapshots) for agent-specific exclusions. + +## Recover After a Host Reboot + +Use this sequence after every reboot until NemoClaw documents an automatic boot-persistence contract. + +Start Docker first: + +```bash +sudo systemctl start docker +docker info +``` + +Ask NemoClaw to select the sandbox's recorded OpenShell gateway and report the current failure layer: + +```bash +$$nemoclaw headless-agent status +``` + +If status reports that the sandbox container exists but is stopped, start it: + +```bash +$$nemoclaw headless-agent start +``` + +Wait for authoritative readiness and repair sandbox-scoped processes and forwards: + +```bash +openshell sandbox list +$$nemoclaw headless-agent connect --probe-only +$$nemoclaw headless-agent status +``` + + + +If the sandbox is ready but the in-sandbox agent gateway or host forward remains unhealthy, run: + +```bash +$$nemoclaw headless-agent recover +``` + +`recover`, `start`, and `connect --probe-only` do not restart the shared host OpenShell gateway. +If they report a host gateway RPC error, follow the printed `openshell status`, named gateway start, or onboarding guidance. + + + + +Deep Agents Code has no in-sandbox gateway to recover. +If status reports a degraded terminal runtime after the sandbox becomes ready, rebuild the sandbox. + + + +If the registry entry remains but the sandbox container is missing, rebuild from recorded metadata and the latest valid snapshot: + +```bash +$$nemoclaw headless-agent rebuild --yes +``` + +Do not destroy the registry entry before this recovery attempt because rebuild needs that metadata. +If you intentionally deleted the sandbox and want a new installation, destroy the stale registry entry and run onboarding again. +For failure-specific recovery boundaries, refer to [Recover and Rebuild Sandboxes](../manage-sandboxes/operate-sandboxes/recover-and-rebuild-sandboxes). + + + +Restart any operator-managed SSH or cloud tunnel after the sandbox and forwards pass verification. + + + +## Troubleshoot a Headless Deployment + +Use the failure layer from `$$nemoclaw headless-agent status` before you choose a recovery action. + +### Onboarding Was Interrupted + +Reattach to the `tmux` or `screen` session first. +If the process exited with a resumable session, inject the required credentials and use `onboard --resume`. +Do not use `--fresh` unless discarding the saved choices and progress is intentional. + +### The Sandbox Is Missing or Not Ready + +Run `openshell sandbox list` and inspect the exact row for `headless-agent`. +`NotReady` does not satisfy readiness. +Run `$$nemoclaw headless-agent status`, then use its `start`, `connect --probe-only`, or `rebuild --yes` guidance. + +### Inference Returns HTTP 5xx + +An HTTP status from `500` through `599` makes the authoritative `inference.local` route unhealthy. +Check the configured provider and host egress, then run: + +```bash +$$nemoclaw headless-agent doctor +$$nemoclaw headless-agent logs --tail 200 +$$nemoclaw headless-agent status +``` + +Do not treat a running agent process as proof that inference works. + + + +### Dashboard or Token Retrieval Fails + +Run `$$nemoclaw headless-agent status` and `connect --probe-only` before retrieving the URL or token again. +The token command exits nonzero when the sandbox is not registered, not running, or cannot expose its agent-specific token. +Do not paste a token into diagnostics. + + + +For Docker, DNS, port, memory, provider, and recovery errors, refer to [Troubleshooting](../reference/troubleshooting). + +## Related Topics + +- [Update Sandboxes](../manage-sandboxes/operate-sandboxes/update-sandboxes) explains the maintained-release update path. +- [Credential Storage](../security/credential-storage) explains the OpenShell provider boundary. +- [CLI Commands Reference](../reference/commands) lists every command and environment variable. diff --git a/docs/deployment/deploy-to-remote-gpu.mdx b/docs/deployment/deploy-to-remote-gpu.mdx deleted file mode 100644 index 5c63de5964..0000000000 --- a/docs/deployment/deploy-to-remote-gpu.mdx +++ /dev/null @@ -1,221 +0,0 @@ ---- -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -title: "Deploy NemoClaw to a Remote GPU Instance" -sidebar-title: "Deploy to Remote GPU Instances" -description: "Run NemoClaw on a remote GPU instance and understand the legacy Brev compatibility flow." -description-agent: "Explains how to run NemoClaw on a remote GPU instance, including the deprecated Brev compatibility path and the preferred installer plus onboard flow. Use when deploying NemoClaw to a remote VM, onboarding a Brev instance, or migrating away from the legacy `nemoclaw deploy` wrapper." -keywords: ["deploy nemoclaw remote gpu", "nemoclaw brev cloud deployment"] -content: - type: "how_to" -skill: - priority: 10 ---- -Run NemoClaw on a remote GPU instance through [Brev](https://brev.nvidia.com). -Prefer provisioning the VM first, running the standard NemoClaw installer on that host, and then running `nemoclaw onboard`. - -## Prerequisites - -- Access to a remote GPU VM that can run Docker and the NVIDIA Container Toolkit. -- The [Brev CLI](https://brev.nvidia.com) installed and authenticated if you provision the VM with Brev. -- A provider credential for the inference backend you want to use during onboarding. -- `HF_TOKEN` or `HUGGING_FACE_HUB_TOKEN` exported when your remote vLLM or Hugging Face workflow needs access to gated models. -- NemoClaw installed locally if you plan to use the deprecated `nemoclaw deploy` wrapper. Otherwise, install NemoClaw directly on the remote host after provisioning it. - -## Preferred Deployment Path - -Provision the remote GPU VM first, then run the normal installer and onboard flow on that VM. -For Brev, `` is the instance name and SSH alias created by the Brev CLI. -For another cloud provider, replace the provisioning and SSH commands with that provider's console or CLI workflow. - -```bash -# On your local machine -brev create -``` - -If `brev` is missing or unauthenticated, install or log in to the Brev CLI first, or provision the VM through your cloud console and connect with `ssh @`. - -For Brev, create a dashboard tunnel before you connect to the VM. -Open the instance in the Brev console, go to the **Access** tab, and add a tunnel for port `18789`. -Copy the generated tunnel URL. - - -Brev tunnel URLs are non-loopback origins. -When `CHAT_UI_URL` points at one, NemoClaw disables OpenClaw device pairing in the generated sandbox configuration because browser-only remote users cannot complete terminal-based pairing. -Avoid exposing the dashboard on internet-reachable or shared-network deployments unless you intend that access. - - -List instances from your local machine: - -```bash -brev ls --json -``` - -Connect to the remote VM: - -```bash -brev ssh -``` - -Set any remote-only environment variables on the VM before onboarding. -For example, set the browser origin if you will open the dashboard through a Brev public URL, raise the first-run readiness budget on cold cloud hosts, and then run the installer: - -```bash -export CHAT_UI_URL="" -export NEMOCLAW_SANDBOX_READY_TIMEOUT=600 -curl -fsSL https://www.nvidia.com/nemoclaw.sh | bash -``` - -Use the origin from the Brev tunnel URL. -For example, if the copied URL is `https://example.host/path`, set `CHAT_UI_URL=https://example.host`. -If NemoClaw is already installed on the VM, run `nemoclaw onboard` instead of the installer after exporting the variables. - -After successful onboarding, NemoClaw prints output that reports a ready sandbox and the next command to connect: - -```text -✓ Sandbox '' is ready -Next: nemoclaw connect -``` - -## Legacy Brev Compatibility - - -The `nemoclaw deploy` command is deprecated. -Prefer provisioning the remote host separately, then running the standard NemoClaw installer and `nemoclaw onboard` on that host. - - -Use the legacy compatibility wrapper only when you need the older Brev-specific bootstrap flow. - -```bash -nemoclaw deploy -``` - -Replace `` with a name for your remote instance, for example `my-gpu-box`. -The sandbox created on the remote VM uses `NEMOCLAW_SANDBOX_NAME`, or `my-assistant` when the variable is unset. -Sandbox names must be lowercase, start with a letter, contain only letters, numbers, and internal hyphens, and end with a letter or number. -The deploy wrapper validates the sandbox name before it provisions the Brev instance, opens SSH, or starts the remote installer. - -The legacy compatibility flow performs the following steps on the VM: - -1. Installs Docker and the NVIDIA Container Toolkit if a GPU is present. -2. Installs the OpenShell CLI. -3. Runs `nemoclaw onboard` (the setup wizard) to create the gateway, register providers, and launch the sandbox. -4. Starts optional host auxiliary services, such as the cloudflared tunnel, when `cloudflared` is available. Onboarding configures channel messaging, and the channels run through OpenShell-managed processes, not through `nemoclaw tunnel start`. - -By default, the compatibility wrapper asks Brev to provision on `gcp`. Override this with `NEMOCLAW_BREV_PROVIDER` if you need a different Brev cloud provider. -If you export `HF_TOKEN` or `HUGGING_FACE_HUB_TOKEN`, the wrapper forwards those values to the VM so remote setup can pull gated Hugging Face model repositories. - -## Connect to the Remote Sandbox - -After onboarding finishes, run the host CLI on the remote VM: - -```bash -nemoclaw connect -``` - -If you used the deprecated Brev compatibility wrapper, the wrapper opens an interactive shell inside the remote sandbox. -To reconnect through that legacy flow, run `nemoclaw deploy ` again. - -## Monitor the Remote Sandbox - -Connect to the instance with SSH and run the OpenShell TUI on the remote VM to monitor activity and approve network requests: - -```bash -ssh 'openshell term' -``` - -## Verify Inference - -Run a test agent prompt from the remote VM host: - -```bash -nemoclaw exec -- openclaw agent --agent main -m "Hello from the remote sandbox" --session-id test -``` - -## Remote Dashboard Access - -The NemoClaw dashboard validates the browser origin against an allowlist baked into the sandbox image at build time. -By default, the allowlist only contains `http://127.0.0.1:18789`. -When you access the dashboard from a remote browser, for example through a Brev tunnel URL or an SSH port-forward, set `CHAT_UI_URL` to the origin the browser uses before running `nemoclaw onboard` on the remote VM. -For Brev, create a tunnel for port `18789` in the instance **Access** tab and copy the generated URL origin: - -```bash -export CHAT_UI_URL="" -nemoclaw onboard -``` - -For SSH port-forwarding, the origin is typically the default `http://127.0.0.1:18789`, so you do not need extra configuration. -Forward the dashboard port from your workstation, substituting the port NemoClaw printed in the install summary (`18789` by default, or the next free port such as `18790`): - -```bash -ssh -L 18789:127.0.0.1:18789 @ -``` - -When you run `nemoclaw` over SSH, the install summary and `nemoclaw dashboard-url` print this command for you, filled in with your remote username and the forwarded dashboard port. -The host stays a `` placeholder that you replace with the address you SSH to, because NemoClaw cannot reliably recover it through an SSH config alias, NAT, or a jump host. -Then open the dashboard URL on your workstation. - - -On Brev, set `CHAT_UI_URL` in the launchable environment configuration so the installer can read it when it builds the sandbox image. -If you do not set `CHAT_UI_URL` on a headless host, the compatibility wrapper prints a warning. - -`NEMOCLAW_DISABLE_DEVICE_AUTH` is also evaluated at image build time. -When `CHAT_UI_URL` points at a non-loopback origin, NemoClaw disables OpenClaw device pairing in the generated sandbox configuration because browser-only remote users cannot complete terminal-based pairing. -Any device that can reach the configured dashboard origin can connect without pairing, so avoid exposing that origin on internet-reachable or shared-network deployments. - - -## First-Run Readiness Budget - -On a remote GPU host, the first `nemoclaw onboard` usually performs the slowest lifecycle work. -The host builds the sandbox image locally and uploads it into the OpenShell gateway, which can stream hundreds of MiB over the VM's link before the readiness wait starts. -The post-create readiness wait defaults to 180 seconds (`NEMOCLAW_SANDBOX_READY_TIMEOUT`), which fits warm-cache, workstation-class onboarding but can be too short for: - -- DGX Station first runs with large quantized models (70B+ parameter footprints, NVFP4 weights). -- Cloud VMs where the local image-build cache is cold and the upload runs over the public network. -- Hosts enabling a web search provider on the first run because the provider and egress policy stack add boot work. - -Raise the budget before re-running onboard: - -```bash -export NEMOCLAW_SANDBOX_READY_TIMEOUT=600 -nemoclaw onboard -``` - -If onboard ends with `Sandbox '' was created but did not become ready within 180s`, onboard first deletes the partially created sandbox, so the next attempt with the raised budget starts from a clean state. -For the inference-probe budget that runs earlier in onboarding, refer to [Configure Inference Timeouts](../inference/manage-inference/configure-inference-timeouts). - -## Proxy Configuration - -NemoClaw routes sandbox traffic through a gateway proxy that defaults to `10.200.0.1:3128`. -If your network requires a different proxy, set `NEMOCLAW_PROXY_HOST` and `NEMOCLAW_PROXY_PORT` before onboarding: - -```bash -export NEMOCLAW_PROXY_HOST=proxy.example.com -export NEMOCLAW_PROXY_PORT=8080 -nemoclaw onboard -``` - -NemoClaw bakes these values into the sandbox image at build time. -NemoClaw also forwards them into the runtime container during sandbox creation, so `/tmp/nemoclaw-proxy-env.sh` uses the same host and port that the image build used. -NemoClaw accepts only alphanumeric characters, dots, hyphens, and colons for the host. -The port must be numeric (0-65535). -Changing the proxy after onboarding requires re-running `nemoclaw onboard`. - -## GPU Configuration - -The deprecated Brev compatibility wrapper uses the `NEMOCLAW_GPU` environment variable to select the GPU type. -The default value is `a2-highgpu-1g:nvidia-tesla-a100:1`. -That value is specific to GCP-backed Brev instances. -Other Brev providers or cloud consoles use different GPU type strings. -Set this variable before running the deprecated wrapper to use a different GPU configuration: - -```bash -export NEMOCLAW_GPU="a2-highgpu-1g:nvidia-tesla-a100:2" -nemoclaw deploy -``` - -## Related Topics - -- [Choose Messaging Channels](../manage-sandboxes/messaging-channels/choose-messaging-channels) to connect Telegram, Discord, or Slack through OpenShell-managed channel messaging. -- [Monitor Sandbox Activity](../monitoring/monitor-sandbox-activity) for sandbox monitoring tools. -- [`nemoclaw deploy`](../reference/commands#nemoclaw-deploy) for the full `deploy` command reference. diff --git a/docs/get-started/quickstart.mdx b/docs/get-started/quickstart.mdx index 3c48e87d82..b3a1d613d2 100644 --- a/docs/get-started/quickstart.mdx +++ b/docs/get-started/quickstart.mdx @@ -181,7 +181,7 @@ Use these details when your first-run path needs more control. The complete dashboard URL contains a gateway token fragment that authenticates the browser session. Treat an authenticated dashboard URL as a password. - Refer to [Remote Dashboard Access](../deployment/deploy-to-remote-gpu#remote-dashboard-access) for Brev tunnels and other remote-access options. + For remote access through SSH port forwarding and recovery guidance, refer to [Deploy to a Headless Server](../deployment/deploy-to-headless-server). @@ -437,7 +437,7 @@ Use these details when your first-run path needs more control. ``` Run the SSH command in a second terminal on your workstation and substitute the port printed by NemoClaw. - For Brev tunnels or binding the dashboard to all interfaces instead of forwarding, refer to [Remote Dashboard Access](../deployment/deploy-to-remote-gpu#remote-dashboard-access). + For remote access through SSH port forwarding and recovery guidance, refer to [Deploy to a Headless Server](../deployment/deploy-to-headless-server). diff --git a/docs/index.yml b/docs/index.yml index 481b7a8d6e..96dca1e1df 100644 --- a/docs/index.yml +++ b/docs/index.yml @@ -285,12 +285,9 @@ navigation: slug: deployment collapsed: open-by-default contents: - - page: "Deploy to Remote GPU Instances" - path: deployment/deploy-to-remote-gpu.mdx - slug: deploy-to-remote-gpu - - page: "Brev Web UI" - path: deployment/brev-web-ui.mdx - slug: brev-web-ui + - page: "Deploy to a Headless Server" + path: _build/agent-variants/deployment/deploy-to-headless-server.openclaw.generated.mdx + slug: deploy-to-headless-server - section: "Monitoring" slug: monitoring collapsed: open-by-default @@ -569,6 +566,13 @@ navigation: - page: "Transfer State Manually" path: _build/agent-variants/manage-sandboxes/transfer-state-manually.deepagents.generated.mdx slug: transfer-state-manually + - section: "Deployment" + slug: deployment + collapsed: open-by-default + contents: + - page: "Deploy to a Headless Server" + path: _build/agent-variants/deployment/deploy-to-headless-server.deepagents.generated.mdx + slug: deploy-to-headless-server - section: "Security" slug: security collapsed: open-by-default @@ -886,6 +890,13 @@ navigation: - page: "Integration Policy Examples" path: _build/agent-variants/network-policy/integration-policy-examples.hermes.generated.mdx slug: integration-policy-examples + - section: "Deployment" + slug: deployment + collapsed: open-by-default + contents: + - page: "Deploy to a Headless Server" + path: _build/agent-variants/deployment/deploy-to-headless-server.hermes.generated.mdx + slug: deploy-to-headless-server - section: "Monitoring" slug: monitoring collapsed: open-by-default diff --git a/docs/network-policy/approve-network-requests.mdx b/docs/network-policy/approve-network-requests.mdx index 636d2212ba..62078e92c3 100644 --- a/docs/network-policy/approve-network-requests.mdx +++ b/docs/network-policy/approve-network-requests.mdx @@ -38,7 +38,7 @@ openshell term Connect to the remote host first. Replace `` with the SSH host or alias where your NemoClaw sandbox is running. -Use a host that resolves from your terminal, such as a Brev SSH alias. +Use a host that resolves from your terminal, such as an SSH alias from your client configuration. ```bash ssh diff --git a/docs/reference/commands.mdx b/docs/reference/commands.mdx index d73dc07902..dbf11b96cd 100644 --- a/docs/reference/commands.mdx +++ b/docs/reference/commands.mdx @@ -2794,7 +2794,7 @@ Run this on the host where the sandbox is running. openshell term ``` -For a remote Brev instance, SSH to the instance and run `openshell term` there, or use a port-forward to the gateway. +For a remote server, connect through SSH and run `openshell term` on that server. @@ -3673,11 +3673,11 @@ The following flags change defaults for commands that manage existing sandboxes. | `NEMOCLAW_UNINSTALL_DESTROY_USER_DATA` | `1` to opt in | Acknowledges data loss during `$$nemoclaw uninstall` and removes the otherwise-preserved entries (`rebuild-backups/`, `backups/`, `sandboxes.json`) under `~/.nemoclaw/`. Equivalent to passing the `--destroy-user-data` flag; the global `Proceed?` confirmation still applies unless `--yes` is also passed. | -### Remote Deployment +### Deprecated Brev Deployment -The following variables seed defaults for `$$nemoclaw deploy` and `$$nemoclaw onboard --remote`, which provision a sandbox on a Brev instance. -Each has a flag equivalent on `deploy`; the env var lets non-interactive runs skip the prompt. -For narrative how-to coverage of `NEMOCLAW_BREV_PROVIDER` and `NEMOCLAW_GPU`, refer to [Deploy to Remote GPU](../deployment/deploy-to-remote-gpu). +The following variables configure the deprecated `$$nemoclaw deploy` compatibility command. +The maintained remote-server path does not use these variables. +For the current remote-server deployment path, refer to [Deploy to a Headless Server](../deployment/deploy-to-headless-server). | Variable | Default | Effect | |----------|---------|--------| diff --git a/docs/reference/enterprise-readiness.mdx b/docs/reference/enterprise-readiness.mdx index 6d7ce2e216..0dd746dde7 100644 --- a/docs/reference/enterprise-readiness.mdx +++ b/docs/reference/enterprise-readiness.mdx @@ -77,15 +77,11 @@ Each row links to deeper documentation and, when a concrete fix is in progress, | Credential isolation | Supported | Inference credentials stay on the host and never enter the sandbox; the agent reaches models through `inference.local`. CLI output redaction adds defense in depth, and OpenClaw sandboxes also run a memory secret scanner. Refer to [Credential Storage](../security/credential-storage) and [Security Best Practices](../security/best-practices). | | Upgrades and lifecycle | Supported with caveats | Upgrade NemoClaw, then run `$$nemoclaw rebuild` to recreate the sandbox with the current image while backing up and restoring state. Do not run `openclaw update` inside the sandbox; the agent is image-pinned. Known gaps include config restore after rebuild ([#5202](https://github.com/NVIDIA/NemoClaw/issues/5202)) and version attachment flexibility ([#2217](https://github.com/NVIDIA/NemoClaw/issues/2217)). | | Backup and restore | Supported | Create snapshots and restore workspace and agent state with the snapshot and backup commands. Refer to [Create and Restore Snapshots](../manage-sandboxes/state-and-backups/create-and-restore-snapshots). | -| Remote and cloud deployment | Supported with caveats | Provision the host, run the installer, and run `$$nemoclaw onboard`; the `$$nemoclaw deploy` Brev wrapper is deprecated. Remote dashboard origins can disable device pairing, so avoid exposing them on shared networks. Brev rough edges are tracked in [#3959](https://github.com/NVIDIA/NemoClaw/issues/3959) and [#3365](https://github.com/NVIDIA/NemoClaw/issues/3365). | +| Remote and cloud deployment | Supported with caveats | Provision a tested Linux host, connect over SSH, run the installer, and run `$$nemoclaw onboard`. Keep dashboards bound to loopback and access them through SSH port forwarding. Automatic recovery after a host reboot is not guaranteed; follow the manual recovery flow in [Deploy to a Headless Server](../deployment/deploy-to-headless-server). | -For remote deployment specifics, refer to [Deploy to Remote GPU Instances](../deployment/deploy-to-remote-gpu) and [Brev Web UI](../deployment/brev-web-ui). For container-level hardening beyond the entrypoint defaults, refer to [Sandbox Hardening](../manage-sandboxes/configure-sandboxes/review-sandbox-hardening). - -For remote deployment and Brev specifics, refer to the Brev section of the [Troubleshooting](troubleshooting#brev) guide. - ## Admin and Control-Plane Capabilities diff --git a/docs/reference/platform-support.mdx b/docs/reference/platform-support.mdx index 742b3af349..b8d2f05c83 100644 --- a/docs/reference/platform-support.mdx +++ b/docs/reference/platform-support.mdx @@ -149,8 +149,7 @@ Pick the row that matches the target environment. | Path | Status | Notes | |------|--------|-------| | Local CLI onboard | Tested | Run `$$nemoclaw onboard` on a tested platform with Docker available locally. Primary path. | -| Remote GPU with Brev CLI | Tested with limitations | Legacy compatibility wrapper provisions a Brev VM, installs Docker + NVIDIA Container Toolkit, and runs `$$nemoclaw onboard` on that host. Defaults to GCP; override with `NEMOCLAW_BREV_PROVIDER`. The preferred path is the standard installer followed by `$$nemoclaw onboard` after the VM is reachable. | -| Brev web UI | Tested | Browser-driven launcher provisions a Brev-managed Linux VM with Docker, the OpenShell runtime, a NemoClaw sandbox running OpenClaw, inference routing, and the OpenClaw dashboard. | +| Headless Linux server | Tested with limitations | Provision a tested Linux host, connect over SSH, run the standard installer and `$$nemoclaw onboard`, and keep dashboards bound to loopback behind SSH port forwarding. Automatic recovery after a host reboot is not guaranteed; follow the documented manual recovery flow. | {/* deployment-status:end */} ## Out of Scope and Not Supported @@ -170,7 +169,7 @@ They are listed here so launch material, sales conversations, and support triage | Non-NVIDIA GPUs (AMD/ROCm, Intel Arc, Apple Metal) | Unsupported | Local vLLM and NIM paths assert NVIDIA CDI presence with `assertCdiNvidiaGpuSpecPresent` (`src/lib/onboard/fatal-runtime-preflight.ts`). NemoClaw does not install non-NVIDIA accelerator drivers. | | Other LangChain, AutoGen, CrewAI, or non-listed agent harnesses | Unsupported | LangChain Deep Agents Code is the only integrated LangChain-family harness (see the Agents section above; status `Experimental`). Other LangChain harnesses, AutoGen, CrewAI, and any agent runtime not listed in the Agents table are not integrated. Bringing more harnesses is tracked as a research epic (see open issue #4861) but is not on the current roadmap. | | Multi-user host sharing | Unsupported | Sandboxes are scoped to a single host user. NemoClaw treats multi-user hosts as a risk and warns at onboard; see `docs/security/openclaw-controls.mdx` Multi-user detection. | -| Hosted SaaS / managed NemoClaw | Unsupported | There is no managed offering. Supported deployment paths are Local CLI onboard, Remote GPU with Brev CLI, and Brev web UI. | +| Hosted SaaS / managed NemoClaw | Unsupported | There is no managed offering. Supported deployment paths are local CLI onboarding and headless Linux server onboarding. | | Native provider integrations not in the Providers table | Unsupported | Vertex AI, Azure OpenAI, SageMaker, Together.ai, Replicate, and HuggingFace Inference Endpoints are not first-class onboarding entries. AWS Bedrock works through the `compatible-anthropic-endpoint` adapter (`src/lib/onboard/bedrock-runtime.ts`). | | Production SLA or guaranteed response times | Unsupported | NemoClaw is an early-preview alpha project. Maintainers respond on a best-effort basis. No SLA is offered. | {/* out-of-scope:end */} diff --git a/docs/reference/troubleshooting.mdx b/docs/reference/troubleshooting.mdx index 2062a70edf..6cf19c4be5 100644 --- a/docs/reference/troubleshooting.mdx +++ b/docs/reference/troubleshooting.mdx @@ -1763,7 +1763,7 @@ Do not probe with `su -s /bin/sh gateway ...`: `su` does not initialize the gate A NemoClaw sandbox has two intentional permission states for `/sandbox/.openclaw`; `700/600` is not one of them: - **Mutable default:** `/sandbox/.openclaw` is `2770 sandbox:sandbox` and `openclaw.json` is `660 sandbox:sandbox`. Both the sandbox user and the gateway (same `sandbox` group, different UID) can write config, so control-UI toggles persist. -- **Host-locked state:** `openclaw.json` is read-only for in-sandbox writers and the config dir is owned by `root`, with the immutable bit set where available. No in-sandbox writes are expected; use the host-side `nemoclaw config set` flow described in [`openclaw config set` fails with a permission error on Brev](#openclaw-config-set-fails-with-a-permission-error-on-brev). +- **Host-locked state:** `openclaw.json` is read-only for in-sandbox writers and the config dir is owned by `root`, with the immutable bit set where available. No in-sandbox writes are expected; use the host-side `$$nemoclaw config set` flow described in [`openclaw config set` or `unset` is blocked inside the sandbox](#openclaw-config-set-or-unset-is-blocked-inside-the-sandbox). - **`700/600` (drift):** the layout that upstream `openclaw doctor --fix` imposes inside a mutable sandbox. It is not a supported NemoClaw state; recover with `$$nemoclaw doctor --fix` or a sandbox restart. ### Discord bot logs in, but the channel still does not work @@ -2551,93 +2551,6 @@ Podman is not a tested runtime. OpenShell officially documents Docker-based runtimes only. If you encounter issues with Podman, switch to a tested runtime (Docker Engine, Docker Desktop, or Colima) and rerun onboarding. - - -## Brev - -For Brev setup instructions, refer to [Brev Web UI](../deployment/brev-web-ui). - -### Most OpenClaw skills show as blocked - -After deploying NemoClaw on Brev, the Skills page in the OpenClaw gateway dashboard shows most bundled skills with a `blocked` status. -Only three skills are available by default: `healthcheck`, `skill-creator`, and `weather`. - -Skills are blocked for one of three reasons. - -- The skill requires a macOS-only binary (`memo`, `remindctl`, `grizzly`, and similar) that is not available on the Linux (GCP) instance Brev provisions. -- The skill requires a CLI binary that is not pre-installed in the sandbox image, such as `gh` for the GitHub skill. -- The skill requires API credentials that have not been configured, such as a Notion API key or Discord bot token. - -Skills that require macOS-only binaries cannot be enabled on Brev. -Skills that require additional CLI binaries require a custom sandbox image rebuild. - -For credentials, use the supported host-side setup flow. -Rerun onboarding for inference or web search credentials, or use `$$nemoclaw channels add ` for messaging channels. -To add a binary to the sandbox image, update the sandbox `Dockerfile.base` to install the required package, then rebuild: - -```bash -$$nemoclaw rebuild -``` - -After the rebuild completes, return to the Skills page to confirm the skill status has changed from `blocked` to `ready`. - -### `openclaw config set` fails with a permission error on Brev - -When the sandbox config has been locked from the host, `openclaw.json` is owned by root and mounted read-only inside the sandbox. -Running `openclaw config set` inside the sandbox then returns: - -```text -EACCES: permission denied, open '/sandbox/.openclaw/openclaw.json' -``` - -In the default sandbox state, `openclaw.json` is writable by the sandbox user. -If you see this error, use the host-side config command instead: - -```bash -$$nemoclaw config set --key --value '' --restart -``` - -Refer to [Commands](commands) for the full list of supported configuration keys. - -### OpenClaw dashboard is unreachable after extended uptime on Brev - -After leaving NemoClaw running for an extended period on Brev, the OpenClaw dashboard may return `ERR_CONNECTION_RESET` or fail to load in the browser. -The agent may still respond on messaging channels such as Telegram or Slack while the dashboard is unreachable. - - - -Take a snapshot before running onboard to protect your workspace files. - -```bash -$$nemoclaw snapshot create -``` - - - -Re-run onboarding to restore dashboard connectivity: - -```bash -$$nemoclaw onboard -``` - -Depending on current sandbox state, onboarding may prompt before recreating resources. - -### Skill install buttons do not work on Brev - -Clicking **Install** on a skill in the OpenClaw gateway dashboard on Brev shows no response or fails silently. - -Skill installation runs against the sandbox environment. -Installing packages on the Brev host does not make them available inside the sandbox. -To install a skill dependency, add it to the sandbox image and rebuild: - -```bash -$$nemoclaw rebuild -``` - -After the rebuild completes, return to the Skills page to confirm the skill is ready. - - - ## Hermes diff --git a/fern/docs.yml b/fern/docs.yml index 13b0e8cbae..d5b5caa652 100644 --- a/fern/docs.yml +++ b/fern/docs.yml @@ -417,6 +417,88 @@ redirects: destination: "/nemoclaw/user-guide/openclaw/get-started/additional-setup/windows-preparation" - source: "/nemoclaw/get-started/prerequisites/windows-preparation/index.html" destination: "/nemoclaw/user-guide/openclaw/get-started/additional-setup/windows-preparation" + # Retired Brev-specific deployment guides now resolve directly to the shared + # headless-server guide. Keep HTML forms before the generic HTML rules. + - source: "/nemoclaw/latest/user-guide/openclaw/deployment/deploy-to-remote-gpu" + destination: "/nemoclaw/latest/user-guide/openclaw/deployment/deploy-to-headless-server" + - source: "/nemoclaw/latest/user-guide/openclaw/deployment/deploy-to-remote-gpu.html" + destination: "/nemoclaw/latest/user-guide/openclaw/deployment/deploy-to-headless-server" + - source: "/nemoclaw/latest/user-guide/openclaw/deployment/deploy-to-remote-gpu/index.html" + destination: "/nemoclaw/latest/user-guide/openclaw/deployment/deploy-to-headless-server" + - source: "/nemoclaw/latest/user-guide/openclaw/deployment/deploy-to-remote-gpu.md" + destination: "/nemoclaw/latest/user-guide/openclaw/deployment/deploy-to-headless-server.md" + - source: "/nemoclaw/latest/user-guide/openclaw/deployment/deploy-to-remote-gpu.mdx" + destination: "/nemoclaw/latest/user-guide/openclaw/deployment/deploy-to-headless-server.mdx" + - source: "/nemoclaw/user-guide/openclaw/deployment/deploy-to-remote-gpu" + destination: "/nemoclaw/user-guide/openclaw/deployment/deploy-to-headless-server" + - source: "/nemoclaw/user-guide/openclaw/deployment/deploy-to-remote-gpu.html" + destination: "/nemoclaw/user-guide/openclaw/deployment/deploy-to-headless-server" + - source: "/nemoclaw/user-guide/openclaw/deployment/deploy-to-remote-gpu/index.html" + destination: "/nemoclaw/user-guide/openclaw/deployment/deploy-to-headless-server" + - source: "/nemoclaw/user-guide/openclaw/deployment/deploy-to-remote-gpu.md" + destination: "/nemoclaw/user-guide/openclaw/deployment/deploy-to-headless-server.md" + - source: "/nemoclaw/user-guide/openclaw/deployment/deploy-to-remote-gpu.mdx" + destination: "/nemoclaw/user-guide/openclaw/deployment/deploy-to-headless-server.mdx" + - source: "/nemoclaw/latest/deployment/deploy-to-remote-gpu" + destination: "/nemoclaw/latest/user-guide/openclaw/deployment/deploy-to-headless-server" + - source: "/nemoclaw/latest/deployment/deploy-to-remote-gpu.html" + destination: "/nemoclaw/latest/user-guide/openclaw/deployment/deploy-to-headless-server" + - source: "/nemoclaw/latest/deployment/deploy-to-remote-gpu/index.html" + destination: "/nemoclaw/latest/user-guide/openclaw/deployment/deploy-to-headless-server" + - source: "/nemoclaw/latest/deployment/deploy-to-remote-gpu.md" + destination: "/nemoclaw/latest/user-guide/openclaw/deployment/deploy-to-headless-server.md" + - source: "/nemoclaw/latest/deployment/deploy-to-remote-gpu.mdx" + destination: "/nemoclaw/latest/user-guide/openclaw/deployment/deploy-to-headless-server.mdx" + - source: "/nemoclaw/deployment/deploy-to-remote-gpu" + destination: "/nemoclaw/user-guide/openclaw/deployment/deploy-to-headless-server" + - source: "/nemoclaw/deployment/deploy-to-remote-gpu.html" + destination: "/nemoclaw/user-guide/openclaw/deployment/deploy-to-headless-server" + - source: "/nemoclaw/deployment/deploy-to-remote-gpu/index.html" + destination: "/nemoclaw/user-guide/openclaw/deployment/deploy-to-headless-server" + - source: "/nemoclaw/deployment/deploy-to-remote-gpu.md" + destination: "/nemoclaw/user-guide/openclaw/deployment/deploy-to-headless-server.md" + - source: "/nemoclaw/deployment/deploy-to-remote-gpu.mdx" + destination: "/nemoclaw/user-guide/openclaw/deployment/deploy-to-headless-server.mdx" + - source: "/nemoclaw/latest/user-guide/openclaw/deployment/brev-web-ui" + destination: "/nemoclaw/latest/user-guide/openclaw/deployment/deploy-to-headless-server" + - source: "/nemoclaw/latest/user-guide/openclaw/deployment/brev-web-ui.html" + destination: "/nemoclaw/latest/user-guide/openclaw/deployment/deploy-to-headless-server" + - source: "/nemoclaw/latest/user-guide/openclaw/deployment/brev-web-ui/index.html" + destination: "/nemoclaw/latest/user-guide/openclaw/deployment/deploy-to-headless-server" + - source: "/nemoclaw/latest/user-guide/openclaw/deployment/brev-web-ui.md" + destination: "/nemoclaw/latest/user-guide/openclaw/deployment/deploy-to-headless-server.md" + - source: "/nemoclaw/latest/user-guide/openclaw/deployment/brev-web-ui.mdx" + destination: "/nemoclaw/latest/user-guide/openclaw/deployment/deploy-to-headless-server.mdx" + - source: "/nemoclaw/user-guide/openclaw/deployment/brev-web-ui" + destination: "/nemoclaw/user-guide/openclaw/deployment/deploy-to-headless-server" + - source: "/nemoclaw/user-guide/openclaw/deployment/brev-web-ui.html" + destination: "/nemoclaw/user-guide/openclaw/deployment/deploy-to-headless-server" + - source: "/nemoclaw/user-guide/openclaw/deployment/brev-web-ui/index.html" + destination: "/nemoclaw/user-guide/openclaw/deployment/deploy-to-headless-server" + - source: "/nemoclaw/user-guide/openclaw/deployment/brev-web-ui.md" + destination: "/nemoclaw/user-guide/openclaw/deployment/deploy-to-headless-server.md" + - source: "/nemoclaw/user-guide/openclaw/deployment/brev-web-ui.mdx" + destination: "/nemoclaw/user-guide/openclaw/deployment/deploy-to-headless-server.mdx" + - source: "/nemoclaw/latest/deployment/brev-web-ui" + destination: "/nemoclaw/latest/user-guide/openclaw/deployment/deploy-to-headless-server" + - source: "/nemoclaw/latest/deployment/brev-web-ui.html" + destination: "/nemoclaw/latest/user-guide/openclaw/deployment/deploy-to-headless-server" + - source: "/nemoclaw/latest/deployment/brev-web-ui/index.html" + destination: "/nemoclaw/latest/user-guide/openclaw/deployment/deploy-to-headless-server" + - source: "/nemoclaw/latest/deployment/brev-web-ui.md" + destination: "/nemoclaw/latest/user-guide/openclaw/deployment/deploy-to-headless-server.md" + - source: "/nemoclaw/latest/deployment/brev-web-ui.mdx" + destination: "/nemoclaw/latest/user-guide/openclaw/deployment/deploy-to-headless-server.mdx" + - source: "/nemoclaw/deployment/brev-web-ui" + destination: "/nemoclaw/user-guide/openclaw/deployment/deploy-to-headless-server" + - source: "/nemoclaw/deployment/brev-web-ui.html" + destination: "/nemoclaw/user-guide/openclaw/deployment/deploy-to-headless-server" + - source: "/nemoclaw/deployment/brev-web-ui/index.html" + destination: "/nemoclaw/user-guide/openclaw/deployment/deploy-to-headless-server" + - source: "/nemoclaw/deployment/brev-web-ui.md" + destination: "/nemoclaw/user-guide/openclaw/deployment/deploy-to-headless-server.md" + - source: "/nemoclaw/deployment/brev-web-ui.mdx" + destination: "/nemoclaw/user-guide/openclaw/deployment/deploy-to-headless-server.mdx" - source: "/nemoclaw/latest/:path*/index.html" destination: "/nemoclaw/latest/:path*" - source: "/nemoclaw/:path*/index.html" diff --git a/scripts/check-docs-published-routes.mts b/scripts/check-docs-published-routes.mts index 88806dfd35..279c75241f 100644 --- a/scripts/check-docs-published-routes.mts +++ b/scripts/check-docs-published-routes.mts @@ -289,7 +289,7 @@ export function findMissingDirectLegacyReleaseNotesRedirects( } /** - * Validate static inference and Manage Sandboxes redirect destinations against the published route map. + * Validate static inference, deployment, and Manage Sandboxes redirect destinations against the published route map. * Variant placeholders are expanded independently so one unsupported agent route * cannot hide behind a redirect that works for the other variants. */ @@ -302,7 +302,7 @@ export function findBrokenPublishedRedirects( }; const violations: RedirectViolation[] = []; for (const redirect of config.redirects ?? []) { - const guardedRedirect = ["/inference", "/manage-sandboxes", "/release-notes"].some( + const guardedRedirect = ["/inference", "/deployment", "/manage-sandboxes", "/release-notes"].some( (segment) => redirect.source.includes(segment) || redirect.destination.includes(segment), ); if ( diff --git a/src/lib/gateway-token-command.ts b/src/lib/gateway-token-command.ts index a8fb5d7826..482d574135 100644 --- a/src/lib/gateway-token-command.ts +++ b/src/lib/gateway-token-command.ts @@ -2,8 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 /** - * `nemoclaw gateway-token` -- print the OpenClaw gateway auth token - * for a running sandbox to stdout so automation can capture it. + * `nemoclaw gateway-token` -- print the running sandbox agent's auth + * token to stdout so automation can capture it. This is the OpenClaw gateway + * token or a bearer-token agent's web-auth key, such as Hermes' API_SERVER_KEY. * * Output contract (intended to be pipe-friendly): * stdout: the token, followed by a single newline. diff --git a/test/check-docs-published-routes.test.ts b/test/check-docs-published-routes.test.ts index 5ae2e6cbe6..b91c40181f 100644 --- a/test/check-docs-published-routes.test.ts +++ b/test/check-docs-published-routes.test.ts @@ -1,11 +1,12 @@ // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import path from "node:path"; import { describe, expect, it } from "vitest"; +import { parse } from "yaml"; import { buildPublishedRouteIndex, @@ -58,6 +59,14 @@ navigation: slug: release-notes `; +const repoRoot = path.join(import.meta.dirname, ".."); +const fernYaml = readFileSync(path.join(repoRoot, "fern", "docs.yml"), "utf8"); +const fernRedirects = ( + parse(fernYaml) as { + redirects?: Array<{ source: string; destination: string }>; + } +).redirects; + function withDocsSource(source: string, run: (docsDir: string) => void): void { const docsDir = mkdtempSync(path.join(tmpdir(), "nemoclaw-doc-routes-")); try { @@ -434,6 +443,72 @@ describe("Manage Sandboxes extension routes", () => { }); }); +describe("headless server deployment routes", () => { + const index = buildPublishedRouteIndex(); + + it("publishes the guide for every agent variant (#7180)", () => { + for (const variant of ["openclaw", "hermes", "deepagents"]) { + expect(index.routes.has(`/user-guide/${variant}/deployment/deploy-to-headless-server`)).toBe( + true, + ); + } + }); + + it("resolves every guide link against generated published routes (#7180)", () => { + expect(findBrokenPublishedRoutes("deployment/deploy-to-headless-server.mdx", index)).toEqual( + [], + ); + }); + + it("retires Brev-specific deployment pages in favor of the shared guide (#7180)", () => { + expect(index.routes.has("/user-guide/openclaw/deployment/deploy-to-remote-gpu")).toBe(false); + expect(index.routes.has("/user-guide/openclaw/deployment/brev-web-ui")).toBe(false); + }); + + it("redirects every retired Brev deployment route directly to the shared guide (#7180)", () => { + const redirects = fernRedirects ?? []; + const destinations = new Map(redirects.map(({ source, destination }) => [source, destination])); + const redirectIndexes = new Map(redirects.map(({ source }, index) => [source, index])); + + for (const retiredSlug of ["deploy-to-remote-gpu", "brev-web-ui"]) { + for (const [sourceBase, destinationBase] of [ + [ + `/nemoclaw/latest/user-guide/openclaw/deployment/${retiredSlug}`, + "/nemoclaw/latest/user-guide/openclaw/deployment/deploy-to-headless-server", + ], + [ + `/nemoclaw/user-guide/openclaw/deployment/${retiredSlug}`, + "/nemoclaw/user-guide/openclaw/deployment/deploy-to-headless-server", + ], + [ + `/nemoclaw/latest/deployment/${retiredSlug}`, + "/nemoclaw/latest/user-guide/openclaw/deployment/deploy-to-headless-server", + ], + [ + `/nemoclaw/deployment/${retiredSlug}`, + "/nemoclaw/user-guide/openclaw/deployment/deploy-to-headless-server", + ], + ]) { + expect(destinations.get(sourceBase)).toBe(destinationBase); + expect(destinations.get(`${sourceBase}.html`)).toBe(destinationBase); + expect(destinations.get(`${sourceBase}/index.html`)).toBe(destinationBase); + expect(destinations.get(`${sourceBase}.md`)).toBe(`${destinationBase}.md`); + expect(destinations.get(`${sourceBase}.mdx`)).toBe(`${destinationBase}.mdx`); + + expect(redirectIndexes.get(`${sourceBase}.html`)).toBeLessThan( + redirectIndexes.get("/nemoclaw/:path*.html") ?? -1, + ); + const genericIndexSource = sourceBase.startsWith("/nemoclaw/latest/") + ? "/nemoclaw/latest/:path*/index.html" + : "/nemoclaw/:path*/index.html"; + expect(redirectIndexes.get(`${sourceBase}/index.html`)).toBeLessThan( + redirectIndexes.get(genericIndexSource) ?? -1, + ); + } + } + }); +}); + describe("native changelog legacy routes", () => { it("redirects every retired Release Notes route directly to the changelog", () => { expect(findMissingDirectLegacyReleaseNotesRedirects()).toEqual([]); diff --git a/test/headless-server-docs.test.ts b/test/headless-server-docs.test.ts new file mode 100644 index 0000000000..ccafa3b6f3 --- /dev/null +++ b/test/headless-server-docs.test.ts @@ -0,0 +1,141 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import assert from "node:assert/strict"; +import fs from "node:fs"; +import path from "node:path"; + +import { describe, expect, it } from "vitest"; + +import { renderAgentVariantPage } from "../scripts/sync-agent-variant-docs.mts"; +import { DEFAULT_INSTALL_REF } from "../src/lib/domain/installer/ref"; + +const repoRoot = path.join(import.meta.dirname, ".."); +const guidePath = path.join(repoRoot, "docs", "deployment", "deploy-to-headless-server.mdx"); +const guide = fs.readFileSync(guidePath, "utf-8"); + +function sectionBetween(content: string, startHeading: string, endHeading: string): string { + const startIndex = content.indexOf(startHeading); + const endIndex = content.indexOf(endHeading); + assert( + startIndex >= 0 && endIndex > startIndex, + `invalid documentation section: ${startHeading} -> ${endHeading}`, + ); + return content.slice(startIndex, endIndex); +} + +const unattendedGuide = sectionBetween( + guide, + "## Run Unattended Onboarding", + "## Verify Readiness", +); +const overview = fs.readFileSync(path.join(repoRoot, "docs", "about", "overview.mdx"), "utf-8"); +const commands = fs.readFileSync(path.join(repoRoot, "docs", "reference", "commands.mdx"), "utf-8"); +const openclawGuide = renderAgentVariantPage(guide, "openclaw", { sourcePath: guidePath }); +const hermesGuide = renderAgentVariantPage(guide, "hermes", { sourcePath: guidePath }); +const deepAgentsGuide = renderAgentVariantPage(guide, "deepagents", { sourcePath: guidePath }); + +describe("headless server deployment guide contracts", () => { + it("distinguishes provider provisioning from headless operation (#7180)", () => { + expect(guide).toContain("Headless Describes Operation, Not a Provider"); + expect(guide).toContain("A Linux VM that you provision through Brev is one example"); + expect(guide).toContain("does not depend on Brev or its web UI"); + }); + + it("pins unattended onboarding to a reviewed immutable commit (#7180)", () => { + expect(DEFAULT_INSTALL_REF).toBe("lkg"); + expect(unattendedGuide).toContain( + 'export NEMOCLAW_INSTALL_REF=""', + ); + expect(unattendedGuide).toContain("^[0-9a-f]{40}$"); + expect(unattendedGuide).toContain( + "https://raw.githubusercontent.com/NVIDIA/NemoClaw/${NEMOCLAW_INSTALL_REF}/install.sh", + ); + expect(unattendedGuide).toContain('NEMOCLAW_INSTALL_REF="$NEMOCLAW_INSTALL_REF"'); + expect(guide).not.toContain("https://www.nvidia.com/nemoclaw.sh"); + expect(unattendedGuide).toContain("NEMOCLAW_NON_INTERACTIVE=1"); + expect(unattendedGuide).toContain("NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE=1"); + expect(unattendedGuide).toContain('NEMOCLAW_AGENT="$NEMOCLAW_AGENT"'); + expect(unattendedGuide).toContain('NEMOCLAW_PROVIDER="$NEMOCLAW_PROVIDER"'); + expect(unattendedGuide).toContain('NVIDIA_INFERENCE_API_KEY="$NVIDIA_INFERENCE_API_KEY"'); + expect(unattendedGuide).toContain('NEMOCLAW_SANDBOX_NAME="$NEMOCLAW_SANDBOX_NAME"'); + }); + + it("keeps the supported noninteractive policy and skill commands copyable (#7180)", () => { + expect(guide).toContain( + "$$nemoclaw headless-agent policy-add --from-file ./presets/internal-status.yaml --yes", + ); + expect(guide).toContain("$$nemoclaw headless-agent skill install ./my-skill/"); + }); + + it("separates tmux and screen session creation from later reattachment (#7180)", () => { + expect(guide).toContain("tmux new-session -s nemoclaw-onboard\n```"); + expect(guide).toContain("tmux attach-session -t nemoclaw-onboard\n```"); + expect(guide).toContain("screen -S nemoclaw-onboard\n```"); + expect(guide).toContain("screen -r nemoclaw-onboard\n```"); + expect(guide).not.toContain( + "tmux new-session -s nemoclaw-onboard\ntmux attach-session -t nemoclaw-onboard", + ); + }); + + it("uses authoritative readiness and manual reboot recovery signals (#7180)", () => { + expect(guide).toContain("openshell sandbox list"); + expect(guide).toContain("The substring `NotReady` is not a ready state."); + expect(guide).toContain("$$nemoclaw headless-agent connect --probe-only"); + expect(guide).toContain("$$nemoclaw headless-agent status"); + expect(guide).toContain("$$nemoclaw headless-agent start"); + expect(guide).toContain("does not guarantee"); + }); + + it("retrieves dashboard and API secrets through supported commands (#7180)", () => { + expect(guide).toContain("$$nemoclaw headless-agent dashboard-url --quiet"); + expect(guide).toContain("TOKEN=$($$nemoclaw headless-agent gateway-token --quiet)"); + expect(guide).toContain('curl -fsS -H "Authorization: Bearer $TOKEN"'); + expect(guide.match(/unset TOKEN/gu)).toHaveLength(2); + }); + + it("keeps dashboard access and token lifecycles specific to each agent (#7180)", () => { + expect(openclawGuide).toContain("OpenClaw generates a new gateway token each time"); + expect(openclawGuide).toContain( + "| OpenClaw gateway token | Rotated when the container starts |", + ); + expect(openclawGuide).not.toContain("Hermes preserves its `API_SERVER_KEY`"); + + expect(hermesGuide).toContain( + "Hermes preserves its `API_SERVER_KEY` when the same sandbox container restarts.", + ); + expect(hermesGuide).toContain( + "`gateway-token` is agent-aware and retrieves `API_SERVER_KEY` through the registered `bearer_token` web-auth contract.", + ); + expect(hermesGuide).toContain("TOKEN=$(nemohermes headless-agent gateway-token --quiet)"); + expect(hermesGuide).toContain("| Hermes `API_SERVER_KEY` | Preserved |"); + expect(hermesGuide).not.toContain("OpenClaw generates a new gateway token each time"); + + expect(deepAgentsGuide).not.toContain("### Dashboard or Token Retrieval Fails"); + expect(deepAgentsGuide).not.toContain("gateway-token --quiet"); + expect(deepAgentsGuide).not.toContain("OpenClaw gateway token | Rotated"); + expect(deepAgentsGuide).not.toContain("Hermes `API_SERVER_KEY` | Preserved"); + }); + + it("documents rebuild survival and arbitrary environment boundaries (#7180)", () => { + expect(guide).toContain( + "| Item | Same-container restart | Snapshot and restore | Rebuild or sandbox upgrade |", + ); + expect(guide).toContain("| Custom preset YAML applied with `policy-add` |"); + expect(guide).toContain("| Arbitrary files outside manifest state |"); + expect(guide).toContain("| Manually installed system or global packages |"); + expect(guide).toContain("| Direct edits to generated profile, config, or environment files |"); + expect(guide).toContain("| Host tunnel process |"); + }); + + it("does not retain the retired Brev-specific deployment flow (#7180)", () => { + expect(guide).not.toContain("## Launch NemoClaw from Brev"); + expect(guide).not.toContain("## Configure Your Agent"); + expect(guide).not.toContain("brev.nvidia.com/launchable"); + expect(overview).toContain("| Headless server deployment |"); + expect(overview).not.toContain("| Remote GPU deployment |"); + expect(commands).toContain("### Deprecated Brev Deployment"); + expect(commands).not.toContain("onboard --remote"); + expect(commands).not.toContain("For a remote Brev instance"); + }); +});