From 52fd4553d7ea7886bf5e57e657db6f95ead8f810 Mon Sep 17 00:00:00 2001 From: Miyoung Choi Date: Fri, 29 May 2026 14:41:36 -0700 Subject: [PATCH 1/3] docs: d2s refactoring start --- .../nemoclaw-user-agent-skills/SKILL.md | 82 ++- .../references/agent-skills.md | 85 --- .../SKILL.md | 222 +++++++- .../references/use-local-inference-details.md | 216 -------- .../nemoclaw-user-configure-security/SKILL.md | 510 ++++++++++++++++- .../references/best-practices.md | 511 ------------------ .../references/credential-storage.md | 2 +- .../references/openclaw-controls.md | 2 +- .../nemoclaw-user-deploy-remote/SKILL.md | 2 +- .../skills/nemoclaw-user-get-started/SKILL.md | 171 +++++- .../references/quickstart-details.md | 169 ------ .../nemoclaw-user-manage-policy/SKILL.md | 13 +- .../customize-network-policy-details.md | 13 - .../nemoclaw-user-manage-sandboxes/SKILL.md | 26 +- .../references/lifecycle-details.md | 26 - .../skills/nemoclaw-user-overview/SKILL.md | 92 +++- .../references/ecosystem.md | 94 ---- .../references/how-it-works.md | 2 +- .../references/overview.md | 2 +- .../skills/nemoclaw-user-reference/SKILL.md | 258 ++++++++- .../references/architecture.md | 260 --------- docs/CONTRIBUTING.md | 16 +- scripts/docs-to-skills.py | 463 ++++------------ test/docs-to-skills.test.py | 70 +++ 24 files changed, 1549 insertions(+), 1758 deletions(-) delete mode 100644 .agents/skills/nemoclaw-user-agent-skills/references/agent-skills.md delete mode 100644 .agents/skills/nemoclaw-user-configure-inference/references/use-local-inference-details.md delete mode 100644 .agents/skills/nemoclaw-user-configure-security/references/best-practices.md delete mode 100644 .agents/skills/nemoclaw-user-get-started/references/quickstart-details.md delete mode 100644 .agents/skills/nemoclaw-user-manage-policy/references/customize-network-policy-details.md delete mode 100644 .agents/skills/nemoclaw-user-manage-sandboxes/references/lifecycle-details.md delete mode 100644 .agents/skills/nemoclaw-user-overview/references/ecosystem.md delete mode 100644 .agents/skills/nemoclaw-user-reference/references/architecture.md create mode 100644 test/docs-to-skills.test.py diff --git a/.agents/skills/nemoclaw-user-agent-skills/SKILL.md b/.agents/skills/nemoclaw-user-agent-skills/SKILL.md index 15e5211e28..1d87704186 100644 --- a/.agents/skills/nemoclaw-user-agent-skills/SKILL.md +++ b/.agents/skills/nemoclaw-user-agent-skills/SKILL.md @@ -9,6 +9,84 @@ license: "Apache-2.0" # NemoClaw Agent Skills for Your AI Coding Assistant -## References +NemoClaw ships agent skills that are generated directly from this documentation. +Each skill is a converted version of one or more doc pages, structured so AI coding assistants can consume it as context. +This means you can interact with the full NemoClaw documentation as skills inside your agent chat session, instead of reading the docs separately. -- **Load [references/agent-skills.md](references/agent-skills.md)** when users ask about AI agent support, coding assistant integration, or the .agents/skills/ directory. Describes the agent skills shipped with NemoClaw and how to access them by cloning the repository. +Ask your assistant a question about NemoClaw and it responds with the same guidance found in these docs, adapted to your current situation. +Skills cover installation, inference configuration, network policy management, monitoring, deployment, security, workspace management, and the CLI reference. + +**Note:** + +If you are a contributor and have cloned the full NemoClaw repository, the full set of skills including contributor and maintainer skills are already available at the project root. +Open the `NemoClaw` directory in your coding assistant and the skills load automatically. +This page is for users who installed NemoClaw with the installer and do not have a local clone. + +## Get the Skills + +Fetch only the skills from the NemoClaw repository without downloading the full source tree. + +```console +$ git clone --filter=blob:none --no-checkout https://github.com/NVIDIA/NemoClaw.git +$ cd NemoClaw +$ git sparse-checkout set --no-cone '/.agents/skills/nemoclaw-user-*/**' '/.agents/skills/nemoclaw-skills-guide/**' '/.claude/**' '/AGENTS.md' '/CLAUDE.md' +$ git checkout +``` + +Open the `NemoClaw` directory in your AI coding assistant. +The assistant discovers the skills in `.agents/skills/` and uses them to answer NemoClaw questions with project-specific guidance. + +You can keep the skills inside the cloned directory or copy `.agents/skills/` to a global location (such as `~/.cursor/skills/` or `~/.claude/skills/`) so they are available across all your projects. +The choice depends on whether you want NemoClaw skills scoped to one workspace or accessible everywhere. + +## Update the Skills + +The sparse checkout filter is saved, so `git pull` fetches only updated skills without downloading the full source tree. +Run `git pull` after each NemoClaw release to pick up new and updated skills. + +## Available Skills + +The following user skills ship with NemoClaw. + +| Skill | Summary | +|-------|---------| +| `nemoclaw-user-overview` | What NemoClaw is, ecosystem placement (OpenClaw + OpenShell + NemoClaw), how it works internally, and release notes. | +| `nemoclaw-user-get-started` | Install NemoClaw, launch a sandbox, and run the first agent prompt. | +| `nemoclaw-user-configure-inference` | Choose inference providers during onboarding, switch models without restarting, and set up local inference servers (Ollama, vLLM, TensorRT-LLM, NIM). | +| `nemoclaw-user-manage-policy` | Approve or deny blocked egress requests in the TUI and customize the sandbox network policy (add, remove, or modify allowed endpoints). | +| `nemoclaw-user-monitor-sandbox` | Check sandbox health, read logs, and trace agent behavior to diagnose problems. | +| `nemoclaw-user-deploy-remote` | Deploy NemoClaw to a remote GPU instance, set up the Telegram bridge, and review sandbox container hardening. | +| `nemoclaw-user-configure-security` | Review the risk framework for every configurable security control, understand credential storage, and assess posture trade-offs. | +| `nemoclaw-user-manage-sandboxes` | Manage day-two sandbox operations, including status, logs, diagnostics, rebuilds, upgrades, messaging channels, workspace files, backup, and restore. | +| `nemoclaw-user-reference` | CLI command reference, plugin and blueprint architecture, baseline network policies, and troubleshooting guide. | + +## Example Questions and Triggered Skills + +After opening the cloned repository in your coding assistant, ask a NemoClaw question in natural language. +The assistant matches your question to the relevant skill and follows the guidance it contains. + +Examples of questions your assistant can answer with these skills: + +| Question | Skill triggered | +|----------|-----------------| +| "How do I install NemoClaw?" | `nemoclaw-user-get-started` | +| "Switch my inference provider to Ollama." | `nemoclaw-user-configure-inference` | +| "A network request was blocked. How do I approve it?" | `nemoclaw-user-manage-policy` | +| "Show me the sandbox logs." | `nemoclaw-user-monitor-sandbox` | +| "How do I deploy NemoClaw to a remote GPU?" | `nemoclaw-user-deploy-remote` | +| "What security controls can I configure?" | `nemoclaw-user-configure-security` | +| "Back up my agent workspace files." | `nemoclaw-user-manage-sandboxes` | +| "What CLI commands are available?" | `nemoclaw-user-reference` | + +You can also reference a skill directly by name if you know which one you need. + +## AI Coding Assistants that You Can Use with NemoClaw Skills + +The NemoClaw agent skills follow the [Agent Skills best practices](https://agentskills.io/skill-creation/best-practices) and the [Claude Skills best practices](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices). +The following table shows how each AI coding assistant can use the NemoClaw skills. + +| Assistant | Skill discovery | +|-----------|----------------| +| Cursor | Reads `AGENTS.md` at the project root, which references `.agents/skills/`. | +| Claude Code | Follows the `.claude/skills/` symlink, which points to `.agents/skills/`. | +| Other assistants | Point the assistant to `.agents/skills/` if it supports project-level skill loading. | diff --git a/.agents/skills/nemoclaw-user-agent-skills/references/agent-skills.md b/.agents/skills/nemoclaw-user-agent-skills/references/agent-skills.md deleted file mode 100644 index 04543e62d1..0000000000 --- a/.agents/skills/nemoclaw-user-agent-skills/references/agent-skills.md +++ /dev/null @@ -1,85 +0,0 @@ - - -# NemoClaw Agent Skills for Your AI Coding Assistant - -NemoClaw ships agent skills that are generated directly from this documentation. -Each skill is a converted version of one or more doc pages, structured so AI coding assistants can consume it as context. -This means you can interact with the full NemoClaw documentation as skills inside your agent chat session, instead of reading the docs separately. - -Ask your assistant a question about NemoClaw and it responds with the same guidance found in these docs, adapted to your current situation. -Skills cover installation, inference configuration, network policy management, monitoring, deployment, security, workspace management, and the CLI reference. - -**Note:** - -If you are a contributor and have cloned the full NemoClaw repository, the full set of skills including contributor and maintainer skills are already available at the project root. -Open the `NemoClaw` directory in your coding assistant and the skills load automatically. -This page is for users who installed NemoClaw with the installer and do not have a local clone. - -## Get the Skills - -Fetch only the skills from the NemoClaw repository without downloading the full source tree. - -```console -$ git clone --filter=blob:none --no-checkout https://github.com/NVIDIA/NemoClaw.git -$ cd NemoClaw -$ git sparse-checkout set --no-cone '/.agents/skills/nemoclaw-user-*/**' '/.agents/skills/nemoclaw-skills-guide/**' '/.claude/**' '/AGENTS.md' '/CLAUDE.md' -$ git checkout -``` - -Open the `NemoClaw` directory in your AI coding assistant. -The assistant discovers the skills in `.agents/skills/` and uses them to answer NemoClaw questions with project-specific guidance. - -You can keep the skills inside the cloned directory or copy `.agents/skills/` to a global location (such as `~/.cursor/skills/` or `~/.claude/skills/`) so they are available across all your projects. -The choice depends on whether you want NemoClaw skills scoped to one workspace or accessible everywhere. - -## Update the Skills - -The sparse checkout filter is saved, so `git pull` fetches only updated skills without downloading the full source tree. -Run `git pull` after each NemoClaw release to pick up new and updated skills. - -## Available Skills - -The following user skills ship with NemoClaw. - -| Skill | Summary | -|-------|---------| -| `nemoclaw-user-overview` | What NemoClaw is, ecosystem placement (OpenClaw + OpenShell + NemoClaw), how it works internally, and release notes. | -| `nemoclaw-user-get-started` | Install NemoClaw, launch a sandbox, and run the first agent prompt. | -| `nemoclaw-user-configure-inference` | Choose inference providers during onboarding, switch models without restarting, and set up local inference servers (Ollama, vLLM, TensorRT-LLM, NIM). | -| `nemoclaw-user-manage-policy` | Approve or deny blocked egress requests in the TUI and customize the sandbox network policy (add, remove, or modify allowed endpoints). | -| `nemoclaw-user-monitor-sandbox` | Check sandbox health, read logs, and trace agent behavior to diagnose problems. | -| `nemoclaw-user-deploy-remote` | Deploy NemoClaw to a remote GPU instance, set up the Telegram bridge, and review sandbox container hardening. | -| `nemoclaw-user-configure-security` | Review the risk framework for every configurable security control, understand credential storage, and assess posture trade-offs. | -| `nemoclaw-user-manage-sandboxes` | Manage day-two sandbox operations, including status, logs, diagnostics, rebuilds, upgrades, messaging channels, workspace files, backup, and restore. | -| `nemoclaw-user-reference` | CLI command reference, plugin and blueprint architecture, baseline network policies, and troubleshooting guide. | - -## Example Questions and Triggered Skills - -After opening the cloned repository in your coding assistant, ask a NemoClaw question in natural language. -The assistant matches your question to the relevant skill and follows the guidance it contains. - -Examples of questions your assistant can answer with these skills: - -| Question | Skill triggered | -|----------|-----------------| -| "How do I install NemoClaw?" | `nemoclaw-user-get-started` | -| "Switch my inference provider to Ollama." | `nemoclaw-user-configure-inference` | -| "A network request was blocked. How do I approve it?" | `nemoclaw-user-manage-policy` | -| "Show me the sandbox logs." | `nemoclaw-user-monitor-sandbox` | -| "How do I deploy NemoClaw to a remote GPU?" | `nemoclaw-user-deploy-remote` | -| "What security controls can I configure?" | `nemoclaw-user-configure-security` | -| "Back up my agent workspace files." | `nemoclaw-user-manage-sandboxes` | -| "What CLI commands are available?" | `nemoclaw-user-reference` | - -You can also reference a skill directly by name if you know which one you need. - -## AI Coding Assistants that You Can Use with NemoClaw Skills - -The NemoClaw agent skills follow the [Agent Skills best practices](https://agentskills.io/skill-creation/best-practices) and the [Claude Skills best practices](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices). -The following table shows how each AI coding assistant can use the NemoClaw skills. - -| Assistant | Skill discovery | -|-----------|----------------| -| Cursor | Reads `AGENTS.md` at the project root, which references `.agents/skills/`. | -| Claude Code | Follows the `.claude/skills/` symlink, which points to `.agents/skills/`. | -| Other assistants | Point the assistant to `.agents/skills/` if it supports project-level skill loading. | diff --git a/.agents/skills/nemoclaw-user-configure-inference/SKILL.md b/.agents/skills/nemoclaw-user-configure-inference/SKILL.md index b3462e029a..07f7a20d98 100644 --- a/.agents/skills/nemoclaw-user-configure-inference/SKILL.md +++ b/.agents/skills/nemoclaw-user-configure-inference/SKILL.md @@ -1,6 +1,6 @@ --- name: "nemoclaw-user-configure-inference" -description: "Connects NemoClaw to a local inference server. Use when setting up Ollama, vLLM, TensorRT-LLM, NIM, or any OpenAI-compatible local model server with NemoClaw. Trigger keywords - nemoclaw local inference, ollama nemoclaw, vllm nemoclaw, local model server, openai compatible endpoint, switch nemoclaw inference model, change inference runtime, nemoclaw additional model, nemoclaw sub-agent model, openclaw sub-agent, agents.list, sessions_spawn, vlm-demo, nemoclaw tool calling, ollama tool calls, vllm tool-call-parser, raw json in tui, nemoclaw inference options, nemoclaw onboarding providers, nemoclaw inference routing." +description: "Lists all inference providers offered during NemoClaw onboarding. Use when explaining which providers are available, what the onboard wizard presents, or how inference routing works. Trigger keywords - nemoclaw inference options, nemoclaw onboarding providers, nemoclaw inference routing, nemoclaw additional model, nemoclaw sub-agent model, openclaw sub-agent, agents.list, sessions_spawn, vlm-demo, switch nemoclaw inference model, change inference runtime, nemoclaw tool calling, ollama tool calls, vllm tool-call-parser, raw json in tui, nemoclaw local inference, ollama nemoclaw, vllm nemoclaw, local model server, openai compatible endpoint." license: "Apache-2.0" --- @@ -109,6 +109,52 @@ JSON such as `{"name":"memory_search","arguments":{...}}` instead of running a tool, switch to vLLM with `--enable-auto-tool-choice` and the correct `--tool-call-parser`. See [Tool-Calling Reliability](references/tool-calling-reliability.md). +### Authenticated Reverse Proxy + +On non-WSL hosts, NemoClaw keeps Ollama bound to `127.0.0.1:11434` and starts a token-gated reverse proxy on `0.0.0.0:11435`. +The native install/start paths also reset NemoClaw-managed systemd launches to the loopback binding. +Containers and other hosts on the local network reach Ollama only through the +proxy, which validates a Bearer token before forwarding requests. +On that native path, NemoClaw never exposes Ollama without authentication. + +WSL Ollama paths do not use this proxy. +Windows-host Ollama uses the Windows daemon through `host.docker.internal`. + +For non-WSL Ollama setups, the onboard wizard manages the proxy automatically: + +- Generates a random 24-byte token on first run and stores it in + `~/.nemoclaw/ollama-proxy-token` with `0600` permissions. +- Starts the proxy after Ollama and verifies it before continuing. +- Cleans up stale proxy processes from previous runs. +- Probes the sandbox Docker network path to the proxy before committing the inference route. +- Stops matching proxy processes during uninstall before deleting NemoClaw state. +- Reuses the persisted token after a host reboot so you do not need to re-run + onboard. + +On native Linux hosts, a firewall can allow the host proxy health check while still blocking sandbox containers on the OpenShell Docker bridge. +When the sandbox-side proxy probe fails with a TCP error, onboarding exits before it saves the inference route and prints a command like: + +```console +$ sudo ufw allow from to any port 11435 proto tcp +$ nemoclaw onboard +``` + +If the probe cannot run, for example because Docker Desktop or WSL uses a different host routing model, onboarding continues and relies on the regular proxy health check. + +The sandbox provider is configured to use proxy port `11435` with the generated +token as its `OPENAI_API_KEY` credential. +OpenShell's L7 proxy injects the token at egress, so the agent inside the +sandbox never sees the token directly. + +All proxy endpoints require the Bearer token, including `GET /api/tags`. +Internal health and reachability checks run via the proxy treat any HTTP +response (including `401`) as proof the proxy is alive — they only fail +when nothing answers at all. + +If Ollama is already running on a non-loopback address when you start onboard, +the wizard restarts it on `127.0.0.1:11434` so the proxy is the only network +path to the model server. + ### GPU Memory Cleanup When you switch away from Ollama, stop host services, or destroy an Ollama-backed sandbox, NemoClaw asks Ollama to unload currently loaded models from GPU memory. @@ -137,8 +183,6 @@ Run onboard without `--non-interactive` to get the interactive `[y/N]` prompt th | `NEMOCLAW_MODEL` | Ollama model tag to use. Optional. | | `NEMOCLAW_YES` | Set to `1` to auto-accept the model-download confirmation prompt. Optional. | -Load [references/use-local-inference-details.md](references/use-local-inference-details.md) for detailed steps on Authenticated Reverse Proxy. - ## OpenAI-Compatible Server This option works with any server that implements `/v1/chat/completions`, including vLLM, TensorRT-LLM, llama.cpp, LocalAI, and others. @@ -171,11 +215,69 @@ If you set `NEMOCLAW_PREFERRED_API=openai-responses`, NemoClaw probes `/v1/respo If a reasoning model returns only reasoning content before producing a final answer, NemoClaw retries the smoke request with a larger response budget. Route, configuration, and authentication failures still fail immediately. -Load [references/use-local-inference-details.md](references/use-local-inference-details.md) for detailed steps on Non-Interactive Setup, Selecting the API Path. +### Non-Interactive Setup + +Set the following environment variables for scripted or CI/CD deployments. + +```console +$ NEMOCLAW_PROVIDER=custom \ + NEMOCLAW_ENDPOINT_URL=http://localhost:8000/v1 \ + NEMOCLAW_MODEL=meta-llama/Llama-3.1-8B-Instruct \ + COMPATIBLE_API_KEY=dummy \ + nemoclaw onboard --non-interactive +``` + +| Variable | Purpose | +|---|---| +| `NEMOCLAW_PROVIDER` | Set to `custom` for an OpenAI-compatible endpoint. | +| `NEMOCLAW_ENDPOINT_URL` | Base URL of the local server. | +| `NEMOCLAW_MODEL` | Model ID as reported by the server. | +| `COMPATIBLE_API_KEY` | API key for the endpoint. Use any non-empty value if authentication is not required. | + +### Selecting the API Path + +For the compatible-endpoint provider, `/v1/chat/completions` is the default. +NemoClaw tests streaming events during onboarding and uses chat completions +without probing the Responses API. + +To opt in to `/v1/responses`, set `NEMOCLAW_PREFERRED_API` before running onboard: + +```console +$ NEMOCLAW_PREFERRED_API=openai-responses nemoclaw onboard +``` + +The wizard then probes `/v1/responses` and only selects it when streaming +support is complete. +If the probe fails, the wizard falls back to `/v1/chat/completions` +automatically. +You can use this variable in both interactive and non-interactive mode. + +| Variable | Values | Default | +|---|---|---| +| `NEMOCLAW_PREFERRED_API` | `openai-completions`, `openai-responses` | `openai-completions` for compatible endpoints | + +If you already onboarded and the sandbox is failing at runtime, re-run +`nemoclaw onboard` to re-probe the endpoint and bake the correct API path +into the image. +Refer to [Switch Inference Models](references/switch-inference-providers.md) for details. ## Anthropic-Compatible Server -Load [references/use-local-inference-details.md](references/use-local-inference-details.md) for detailed steps. +If your local server implements the Anthropic Messages API (`/v1/messages`), choose **Other Anthropic-compatible endpoint** during onboarding instead. + +```console +$ nemoclaw onboard +``` + +For non-interactive setup, use `NEMOCLAW_PROVIDER=anthropicCompatible` and set `COMPATIBLE_ANTHROPIC_API_KEY`. + +```console +$ NEMOCLAW_PROVIDER=anthropicCompatible \ + NEMOCLAW_ENDPOINT_URL=http://localhost:8080 \ + NEMOCLAW_MODEL=my-model \ + COMPATIBLE_ANTHROPIC_API_KEY=dummy \ + nemoclaw onboard --non-interactive +``` ## vLLM @@ -208,7 +310,53 @@ Managed vLLM uses these profiles: NemoClaw forces the `chat/completions` API path for vLLM. The vLLM `/v1/responses` endpoint does not run the `--tool-call-parser`, so tool calls arrive as raw text. -Load [references/use-local-inference-details.md](references/use-local-inference-details.md) for detailed steps on Non-Interactive Setup, Override the Managed-vLLM Model. +### Non-Interactive Setup + +Use an already-running vLLM server: + +```console +$ NEMOCLAW_PROVIDER=vllm \ + nemoclaw onboard --non-interactive +``` + +Install or start managed vLLM when a supported profile is detected. +On DGX Spark and DGX Station, `NEMOCLAW_PROVIDER=install-vllm` is enough for non-interactive runs; add `NEMOCLAW_EXPERIMENTAL=1` on generic Linux NVIDIA GPU hosts. + +```console +$ NEMOCLAW_PROVIDER=install-vllm \ + nemoclaw onboard --non-interactive +``` + +NemoClaw records the model returned by vLLM's `/v1/models` endpoint. +Start vLLM with the model you want before onboarding if you manage the server yourself. + +### Override the Managed-vLLM Model + +Managed vLLM serves the profile default unless you select a different registry entry. +Export `NEMOCLAW_VLLM_MODEL=` before invoking the installer to choose a different model from the registry. +NemoClaw uses the matching `vllm serve` flags, including the reasoning parser, tool-call parser, and `--max-model-len`. +Recognised slugs: + +| Slug | Hugging Face model | Notes | +|---|---|---| +| `qwen3.6-27b` | `Qwen/Qwen3.6-27B-FP8` | Default on DGX Spark and DGX Station profiles | +| `nemotron-3-nano-4b` | `nvidia/NVIDIA-Nemotron-3-Nano-4B-FP8` | Default on the generic Linux + NVIDIA GPU profile | +| `deepseek-r1-distill-70b` | `deepseek-ai/DeepSeek-R1-Distill-Llama-70B` | Gated. Requires Hugging Face license acceptance | + +The slug is case-insensitive; the full Hugging Face id is also accepted. +An unrecognised value fails fast with a list of valid slugs. + +Gated models require a Hugging Face token; export it before onboarding so NemoClaw can forward it into the managed vLLM container: + +```console +$ export HF_TOKEN= +$ NEMOCLAW_PROVIDER=install-vllm \ + NEMOCLAW_VLLM_MODEL=deepseek-r1-distill-70b \ + nemoclaw onboard --non-interactive +``` + +`HUGGING_FACE_HUB_TOKEN` is accepted as an alternative. +The token check runs on the host before any docker pull, so a missing or empty token aborts onboarding before bandwidth is spent on a 401. ## NVIDIA NIM (Experimental) @@ -236,27 +384,77 @@ If the NIM container exits before the health endpoint becomes ready, onboarding NIM uses vLLM internally. The same `chat/completions` API path restriction applies. -Load [references/use-local-inference-details.md](references/use-local-inference-details.md) for detailed steps on Non-Interactive Setup. +### Non-Interactive Setup + +```console +$ NEMOCLAW_EXPERIMENTAL=1 \ + NEMOCLAW_PROVIDER=nim \ + nemoclaw onboard --non-interactive +``` + +To select a specific model, set `NEMOCLAW_MODEL`. ## Timeout Configuration -Load [references/use-local-inference-details.md](references/use-local-inference-details.md) for detailed steps. +Local inference requests use a default timeout of 180 seconds. +Large prompts on hardware such as DGX Spark can exceed shorter timeouts, so NemoClaw sets a higher default for Ollama, vLLM, NIM, and compatible-endpoint setup. + +To override the timeout, set the `NEMOCLAW_LOCAL_INFERENCE_TIMEOUT` environment variable before onboarding: + +```console +$ export NEMOCLAW_LOCAL_INFERENCE_TIMEOUT=300 +$ nemoclaw onboard +``` + +The value is in seconds. +This setting is baked into the sandbox at build time. +Changing it after onboarding requires re-running `nemoclaw onboard`. + +`NEMOCLAW_LOCAL_INFERENCE_TIMEOUT` only governs the inference-server validation probe. +During local Ollama setup, NemoClaw treats host-side curl process timeouts as retryable probe failures and retries with a larger timeout before it reports a validation failure. +NemoClaw also retries Docker runtime detection with a longer `docker info` timeout before it chooses the local inference route. +The post-create readiness wait (image build, gateway upload, in-sandbox boot) has its own budget, `NEMOCLAW_SANDBOX_READY_TIMEOUT`, also defaulting to 180 seconds. +On hosts where the sandbox image takes minutes to build or upload — large quantised models, DGX Station first runs, or remote VMs over a slow link — raise both together: + +```console +$ export NEMOCLAW_LOCAL_INFERENCE_TIMEOUT=300 +$ export NEMOCLAW_SANDBOX_READY_TIMEOUT=600 +$ nemoclaw onboard +``` + +If onboard ends with `Sandbox '' was created but did not become ready within 180s`, refer to Troubleshooting (use the `nemoclaw-user-reference` skill). ## Verify the Configuration -Load [references/use-local-inference-details.md](references/use-local-inference-details.md) for detailed steps. +After onboarding completes, confirm the active provider and model. + +```console +$ nemoclaw status +``` + +The output shows the provider label (for example, "Local vLLM" or "Other OpenAI-compatible endpoint") and the active model. +For Local Ollama, status also checks the authenticated proxy when a proxy token is available. +If `Inference` is healthy but `Inference (auth proxy)` is not, rerun onboarding to repair the proxy path that sandbox requests use. ## Switch Models at Runtime -Load [references/use-local-inference-details.md](references/use-local-inference-details.md) for detailed steps. +You can change the model without re-running onboard. +Refer to [Switch Inference Models](references/switch-inference-providers.md) for the full procedure. + +For compatible endpoints, the command is: + +```console +$ nemoclaw inference set --provider compatible-endpoint --model +``` + +If the provider itself needs to change (for example, switching from vLLM to a cloud API), pass the new provider to `nemoclaw inference set`. ## References - **Load [references/switch-inference-providers.md](references/switch-inference-providers.md)** when switching inference providers, changing the model runtime, or reconfiguring inference routing. Changes the active inference model without restarting the sandbox. - **Load [references/set-up-sub-agent.md](references/set-up-sub-agent.md)** when users ask how to add a second model, configure a sub-agent model, use Omni for vision tasks, configure agents.list, or use sessions_spawn in NemoClaw. Shows the NemoClaw-specific file paths and update flow for adding an auxiliary OpenClaw sub-agent model. -- **[references/tool-calling-reliability.md](references/tool-calling-reliability.md)** — Explains Ollama tool-call leak symptoms, when vLLM with a tool-call parser is recommended, and how to repoint NemoClaw to a parser-aware local endpoint. - **Load [references/inference-options.md](references/inference-options.md)** when explaining which providers are available, what the onboard wizard presents, or how inference routing works. Lists all inference providers offered during NemoClaw onboarding. -- **Load [references/use-local-inference-details.md](references/use-local-inference-details.md)** when you need detailed steps for Authenticated Reverse Proxy, Non-Interactive Setup, Selecting the API Path, and related details. +- **[references/tool-calling-reliability.md](references/tool-calling-reliability.md)** — Explains Ollama tool-call leak symptoms, when vLLM with a tool-call parser is recommended, and how to repoint NemoClaw to a parser-aware local endpoint. ## Related Skills diff --git a/.agents/skills/nemoclaw-user-configure-inference/references/use-local-inference-details.md b/.agents/skills/nemoclaw-user-configure-inference/references/use-local-inference-details.md deleted file mode 100644 index 04d962fde2..0000000000 --- a/.agents/skills/nemoclaw-user-configure-inference/references/use-local-inference-details.md +++ /dev/null @@ -1,216 +0,0 @@ - - -# Use a Local Inference Server: Details - -## Authenticated Reverse Proxy - -On non-WSL hosts, NemoClaw keeps Ollama bound to `127.0.0.1:11434` and starts a token-gated reverse proxy on `0.0.0.0:11435`. -The native install/start paths also reset NemoClaw-managed systemd launches to the loopback binding. -Containers and other hosts on the local network reach Ollama only through the -proxy, which validates a Bearer token before forwarding requests. -On that native path, NemoClaw never exposes Ollama without authentication. - -WSL Ollama paths do not use this proxy. -Windows-host Ollama uses the Windows daemon through `host.docker.internal`. - -For non-WSL Ollama setups, the onboard wizard manages the proxy automatically: - -- Generates a random 24-byte token on first run and stores it in - `~/.nemoclaw/ollama-proxy-token` with `0600` permissions. -- Starts the proxy after Ollama and verifies it before continuing. -- Cleans up stale proxy processes from previous runs. -- Probes the sandbox Docker network path to the proxy before committing the inference route. -- Stops matching proxy processes during uninstall before deleting NemoClaw state. -- Reuses the persisted token after a host reboot so you do not need to re-run - onboard. - -On native Linux hosts, a firewall can allow the host proxy health check while still blocking sandbox containers on the OpenShell Docker bridge. -When the sandbox-side proxy probe fails with a TCP error, onboarding exits before it saves the inference route and prints a command like: - -```console -$ sudo ufw allow from to any port 11435 proto tcp -$ nemoclaw onboard -``` - -If the probe cannot run, for example because Docker Desktop or WSL uses a different host routing model, onboarding continues and relies on the regular proxy health check. - -The sandbox provider is configured to use proxy port `11435` with the generated -token as its `OPENAI_API_KEY` credential. -OpenShell's L7 proxy injects the token at egress, so the agent inside the -sandbox never sees the token directly. - -All proxy endpoints require the Bearer token, including `GET /api/tags`. -Internal health and reachability checks run via the proxy treat any HTTP -response (including `401`) as proof the proxy is alive — they only fail -when nothing answers at all. - -If Ollama is already running on a non-loopback address when you start onboard, -the wizard restarts it on `127.0.0.1:11434` so the proxy is the only network -path to the model server. - -### Non-Interactive Setup - -Set the following environment variables for scripted or CI/CD deployments. - -```console -$ NEMOCLAW_PROVIDER=custom \ - NEMOCLAW_ENDPOINT_URL=http://localhost:8000/v1 \ - NEMOCLAW_MODEL=meta-llama/Llama-3.1-8B-Instruct \ - COMPATIBLE_API_KEY=dummy \ - nemoclaw onboard --non-interactive -``` - -| Variable | Purpose | -|---|---| -| `NEMOCLAW_PROVIDER` | Set to `custom` for an OpenAI-compatible endpoint. | -| `NEMOCLAW_ENDPOINT_URL` | Base URL of the local server. | -| `NEMOCLAW_MODEL` | Model ID as reported by the server. | -| `COMPATIBLE_API_KEY` | API key for the endpoint. Use any non-empty value if authentication is not required. | - -### Selecting the API Path - -For the compatible-endpoint provider, `/v1/chat/completions` is the default. -NemoClaw tests streaming events during onboarding and uses chat completions -without probing the Responses API. - -To opt in to `/v1/responses`, set `NEMOCLAW_PREFERRED_API` before running onboard: - -```console -$ NEMOCLAW_PREFERRED_API=openai-responses nemoclaw onboard -``` - -The wizard then probes `/v1/responses` and only selects it when streaming -support is complete. -If the probe fails, the wizard falls back to `/v1/chat/completions` -automatically. -You can use this variable in both interactive and non-interactive mode. - -| Variable | Values | Default | -|---|---|---| -| `NEMOCLAW_PREFERRED_API` | `openai-completions`, `openai-responses` | `openai-completions` for compatible endpoints | - -If you already onboarded and the sandbox is failing at runtime, re-run -`nemoclaw onboard` to re-probe the endpoint and bake the correct API path -into the image. -Refer to [Switch Inference Models](switch-inference-providers.md) for details. - -## Anthropic-Compatible Server - -If your local server implements the Anthropic Messages API (`/v1/messages`), choose **Other Anthropic-compatible endpoint** during onboarding instead. - -```console -$ nemoclaw onboard -``` - -For non-interactive setup, use `NEMOCLAW_PROVIDER=anthropicCompatible` and set `COMPATIBLE_ANTHROPIC_API_KEY`. - -```console -$ NEMOCLAW_PROVIDER=anthropicCompatible \ - NEMOCLAW_ENDPOINT_URL=http://localhost:8080 \ - NEMOCLAW_MODEL=my-model \ - COMPATIBLE_ANTHROPIC_API_KEY=dummy \ - nemoclaw onboard --non-interactive -``` - -### Non-Interactive Setup - -Use an already-running vLLM server: - -```console -$ NEMOCLAW_PROVIDER=vllm \ - nemoclaw onboard --non-interactive -``` - -Install or start managed vLLM when a supported profile is detected. -On DGX Spark and DGX Station, `NEMOCLAW_PROVIDER=install-vllm` is enough for non-interactive runs; add `NEMOCLAW_EXPERIMENTAL=1` on generic Linux NVIDIA GPU hosts. - -```console -$ NEMOCLAW_PROVIDER=install-vllm \ - nemoclaw onboard --non-interactive -``` - -NemoClaw records the model returned by vLLM's `/v1/models` endpoint. -Start vLLM with the model you want before onboarding if you manage the server yourself. - -### Override the Managed-vLLM Model - -Managed vLLM serves the profile default unless you select a different registry entry. -Export `NEMOCLAW_VLLM_MODEL=` before invoking the installer to choose a different model from the registry. -NemoClaw uses the matching `vllm serve` flags, including the reasoning parser, tool-call parser, and `--max-model-len`. -Recognised slugs: - -| Slug | Hugging Face model | Notes | -|---|---|---| -| `qwen3.6-27b` | `Qwen/Qwen3.6-27B-FP8` | Default on DGX Spark and DGX Station profiles | -| `nemotron-3-nano-4b` | `nvidia/NVIDIA-Nemotron-3-Nano-4B-FP8` | Default on the generic Linux + NVIDIA GPU profile | -| `deepseek-r1-distill-70b` | `deepseek-ai/DeepSeek-R1-Distill-Llama-70B` | Gated. Requires Hugging Face license acceptance | - -The slug is case-insensitive; the full Hugging Face id is also accepted. -An unrecognised value fails fast with a list of valid slugs. - -Gated models require a Hugging Face token; export it before onboarding so NemoClaw can forward it into the managed vLLM container: - -```console -$ export HF_TOKEN= -$ NEMOCLAW_PROVIDER=install-vllm \ - NEMOCLAW_VLLM_MODEL=deepseek-r1-distill-70b \ - nemoclaw onboard --non-interactive -``` - -`HUGGING_FACE_HUB_TOKEN` is accepted as an alternative. -The token check runs on the host before any docker pull, so a missing or empty token aborts onboarding before bandwidth is spent on a 401. - -## Timeout Configuration - -Local inference requests use a default timeout of 180 seconds. -Large prompts on hardware such as DGX Spark can exceed shorter timeouts, so NemoClaw sets a higher default for Ollama, vLLM, NIM, and compatible-endpoint setup. - -To override the timeout, set the `NEMOCLAW_LOCAL_INFERENCE_TIMEOUT` environment variable before onboarding: - -```console -$ export NEMOCLAW_LOCAL_INFERENCE_TIMEOUT=300 -$ nemoclaw onboard -``` - -The value is in seconds. -This setting is baked into the sandbox at build time. -Changing it after onboarding requires re-running `nemoclaw onboard`. - -`NEMOCLAW_LOCAL_INFERENCE_TIMEOUT` only governs the inference-server validation probe. -During local Ollama setup, NemoClaw treats host-side curl process timeouts as retryable probe failures and retries with a larger timeout before it reports a validation failure. -NemoClaw also retries Docker runtime detection with a longer `docker info` timeout before it chooses the local inference route. -The post-create readiness wait (image build, gateway upload, in-sandbox boot) has its own budget, `NEMOCLAW_SANDBOX_READY_TIMEOUT`, also defaulting to 180 seconds. -On hosts where the sandbox image takes minutes to build or upload — large quantised models, DGX Station first runs, or remote VMs over a slow link — raise both together: - -```console -$ export NEMOCLAW_LOCAL_INFERENCE_TIMEOUT=300 -$ export NEMOCLAW_SANDBOX_READY_TIMEOUT=600 -$ nemoclaw onboard -``` - -If onboard ends with `Sandbox '' was created but did not become ready within 180s`, refer to Troubleshooting (use the `nemoclaw-user-reference` skill). - -## Verify the Configuration - -After onboarding completes, confirm the active provider and model. - -```console -$ nemoclaw status -``` - -The output shows the provider label (for example, "Local vLLM" or "Other OpenAI-compatible endpoint") and the active model. -For Local Ollama, status also checks the authenticated proxy when a proxy token is available. -If `Inference` is healthy but `Inference (auth proxy)` is not, rerun onboarding to repair the proxy path that sandbox requests use. - -## Switch Models at Runtime - -You can change the model without re-running onboard. -Refer to [Switch Inference Models](switch-inference-providers.md) for the full procedure. - -For compatible endpoints, the command is: - -```console -$ nemoclaw inference set --provider compatible-endpoint --model -``` - -If the provider itself needs to change (for example, switching from vLLM to a cloud API), pass the new provider to `nemoclaw inference set`. diff --git a/.agents/skills/nemoclaw-user-configure-security/SKILL.md b/.agents/skills/nemoclaw-user-configure-security/SKILL.md index 36df08415f..2d00a28a84 100644 --- a/.agents/skills/nemoclaw-user-configure-security/SKILL.md +++ b/.agents/skills/nemoclaw-user-configure-security/SKILL.md @@ -9,8 +9,514 @@ license: "Apache-2.0" # NemoClaw Security Best Practices: Controls, Risks, and Posture Profiles +NemoClaw ships with deny-by-default security controls across four layers: network, filesystem, process, and inference. +You can tune every control, but each change shifts the risk profile. +This page documents every configurable knob, its default, what it protects, the concrete risk of relaxing it, and a recommendation for common use cases. + +For background on how the layers fit together, refer to How It Works (use the `nemoclaw-user-overview` skill). + +## Protection Layers at a Glance + +NemoClaw enforces security at four layers. +NemoClaw locks some when it creates the sandbox and requires a restart to change them. +You can hot-reload others while the sandbox runs. + +The following diagram shows the default posture immediately after `nemoclaw onboard`, before you approve any endpoints or apply any presets. + +```mermaid +flowchart TB + subgraph HOST["Your Machine: default posture after nemoclaw onboard"] + direction TB + + YOU["👤 Operator"] + + subgraph NC["NemoClaw + OpenShell"] + direction TB + + subgraph SB["Sandbox: the agent's isolated world"] + direction LR + PROC["⚙️ Process Layer
Controls what the agent can execute"] + FS["📁 Filesystem Layer
Controls what the agent can read and write"] + AGENT["🤖 Agent"] + end + + subgraph GW["Gateway: the gatekeeper"] + direction LR + NET["🌐 Network Layer
Controls where the agent can connect"] + INF["🧠 Inference Layer
Controls which AI models the agent can use"] + end + end + end + + OUTSIDE["🌍 Outside World
Internet · AI Providers · APIs"] + + AGENT -- "all requests" --> GW + GW -- "approved only" --> OUTSIDE + YOU -. "approve / deny" .-> GW + + classDef agent fill:#76b900,stroke:#5a8f00,color:#fff,stroke-width:2px,font-weight:bold + classDef locked fill:#1a1a1a,stroke:#76b900,color:#fff,stroke-width:2px + classDef hot fill:#333,stroke:#76b900,color:#e6f2cc,stroke-width:2px + classDef external fill:#f5f5f5,stroke:#ccc,color:#1a1a1a,stroke-width:1px + classDef operator fill:#fff,stroke:#76b900,color:#1a1a1a,stroke-width:2px,font-weight:bold + + class AGENT agent + class PROC,FS locked + class NET,INF hot + class OUTSIDE external + class YOU operator + + style HOST fill:none,stroke:#76b900,stroke-width:2px,color:#1a1a1a + style NC fill:none,stroke:#76b900,stroke-width:1px,stroke-dasharray:5 5,color:#1a1a1a + style SB fill:#f5faed,stroke:#76b900,stroke-width:2px,color:#1a1a1a + style GW fill:#2a2a2a,stroke:#76b900,stroke-width:2px,color:#fff +``` + +| Layer | What it protects | Enforcement point | Changeable at runtime | +| --- | --- | --- | --- | +| Network | Unauthorized outbound connections and data exfiltration. | OpenShell gateway | Yes. Use `openshell policy set` or operator approval. | +| Filesystem | System binary tampering, credential theft, config manipulation. | Landlock LSM + container mounts | Landlock layout: no. Requires sandbox re-creation. Use host-side NemoClaw commands for durable config changes. | +| Process | Privilege escalation, fork bombs, syscall abuse. | Container runtime (Docker/K8s `securityContext`) | No. Requires sandbox re-creation. | +| Inference | Credential exposure, unauthorized model access, cost overruns. | OpenShell gateway | Yes. Use `nemoclaw inference set`. | + +## Network Controls + +NemoClaw controls which hosts, ports, and HTTP methods the sandbox can reach, and lets operators approve or deny requests in real time. + +### Deny-by-Default Egress + +The sandbox blocks all outbound connections unless you explicitly list the endpoint in the policy file `nemoclaw-blueprint/policies/openclaw-sandbox.yaml`. + +| Aspect | Detail | +|---|---| +| Default | All egress denied. Only endpoints in the baseline policy can receive traffic. | +| What you can change | Add endpoints to the policy file (static) or with `openshell policy set` (dynamic). | +| Risk if relaxed | Each allowed endpoint is a potential data exfiltration path. The agent can send workspace content, credentials, or conversation history to any reachable host. | +| Recommendation | Add only endpoints the agent needs for its task. Prefer operator approval for one-off requests over permanently widening the baseline. | + +### Binary-Scoped Endpoint Rules + +Each network policy entry restricts which executables can reach the endpoint using the `binaries` field. + +OpenShell identifies the calling binary by reading `/proc//exe` (the kernel-trusted executable path, not `argv[0]`), walking the process tree for ancestor binaries, and computing a SHA256 hash of each binary on first use. +If someone replaces a binary while the sandbox runs, the hash mismatch triggers an immediate deny. + +| Aspect | Detail | +|---|---| +| Default | Each endpoint restricts access to specific binaries. For example, the `github` preset restricts access so only `/usr/bin/git` can reach `github.com`. Binary paths support glob patterns (`*` matches one path component, `**` matches recursively). | +| What you can change | Add binaries to an endpoint entry, or omit the `binaries` field to allow any executable. | +| Risk if relaxed | Removing binary restrictions lets any process in the sandbox reach the endpoint. An agent could use `curl`, `wget`, or a Python script to exfiltrate data to an allowed host, bypassing the intended usage pattern. | +| Recommendation | Always scope endpoints to the binaries that need them. If the agent needs a host from a new binary, add that binary explicitly rather than removing the restriction. | + +### Path-Scoped HTTP Rules + +Endpoint rules restrict allowed HTTP methods and URL paths. + +| Aspect | Detail | +|---|---| +| Default | Some endpoints allow GET and POST on `/**` (for example, `clawhub.ai`). Others restrict methods and paths to specific API routes (for example, `integrate.api.nvidia.com` allows POST only to inference and embedding paths and GET to model listings). Read-only endpoints such as `docs.openclaw.ai`, the `npm_registry` baseline entry, and the `pypi` preset allow GET only (PyPI also allows HEAD). The `npm` preset is an intentional exception: npm/Yarn registry traffic uses L4 pass-through for Node 22 undici CONNECT compatibility. | +| What you can change | Add methods (PUT, DELETE, PATCH) or restrict paths to specific prefixes. | +| Risk if relaxed | Allowing all methods on an API endpoint gives the agent write and delete access. For example, allowing DELETE on `api.github.com` lets the agent delete repositories. | +| Recommendation | Use GET-only rules for endpoints that the agent only reads. Add write methods only for endpoints where the agent must create or modify resources. Restrict paths to specific API routes when possible. | + +### L4-Only vs L7 Inspection (`protocol` Field) + +All sandbox egress goes through OpenShell's CONNECT proxy. +The `protocol` field on an endpoint controls whether the proxy also inspects individual HTTP requests inside the tunnel. + +| Aspect | Detail | +|---|---| +| Default | Endpoints without a `protocol` field use L4-only enforcement: the proxy checks host, port, and binary identity, then relays the TCP stream without inspecting payloads. Setting `protocol: rest` enables L7 inspection: the proxy auto-detects and terminates TLS, then evaluates each HTTP request's method and path against the endpoint's `rules` or `access` preset. | +| What you can change | Add `protocol: rest` to an endpoint to enable per-request HTTP inspection. Use the `access` preset (`full`, `read-only`, `read-write`) or explicit `rules` to control allowed methods and paths. | +| Risk if relaxed | L4-only endpoints (no `protocol` field) allow the agent to send any data through the tunnel after the initial connection is permitted. The proxy cannot see or filter the HTTP method, path, or body. The `access: full` preset with `protocol: rest` enables inspection but allows all methods and paths, so it does not restrict what the agent can do at the HTTP level. | +| Recommendation | Use `protocol: rest` with specific `rules` for REST APIs where you want method and path control. Use `protocol: rest` with `access: read-only` for read-only endpoints. Omit `protocol` only for non-HTTP protocols (WebSocket, gRPC streaming), endpoints that do not need HTTP inspection, or documented compatibility exceptions that require a client-managed CONNECT tunnel. | + +### Operator Approval Flow + +When the agent reaches an unlisted endpoint, OpenShell blocks the request and prompts the operator in the TUI. + +| Aspect | Detail | +|---|---| +| Default | Enabled. The gateway blocks all unlisted endpoints and requires approval. | +| What you can change | The system merges approved endpoints into the sandbox's policy as a new durable revision. They persist across sandbox restarts within the same sandbox instance. However, when you destroy and recreate the sandbox (for example, by running `nemoclaw onboard`), the policy resets to the baseline defined in the blueprint. | +| Risk if relaxed | Approving an endpoint permanently widens the running sandbox's policy. If you approve a broad domain (such as a CDN that hosts arbitrary content), the agent can fetch anything from that domain until you destroy and recreate the sandbox. | +| Recommendation | Review each blocked request before approving. If you find yourself approving the same endpoint repeatedly, add it to the baseline policy with appropriate binary and path restrictions. To reset approved endpoints, destroy and recreate the sandbox. | + +### Policy Presets + +NemoClaw ships preset policy files in `nemoclaw-blueprint/policies/presets/` for common integrations. + +| Preset | What it enables | Key risk | +|---|---|---| +| `brave` | Brave Search API. | Agent can issue search queries. | +| `brew` | Homebrew (Linuxbrew) package manager. The sandbox base image includes the `brew` binary; this preset opens network egress to GitHub and the Homebrew formulae index so `brew install` can fetch bottles. | Allows installing arbitrary Homebrew packages, which may contain malicious code. | +| `discord` | Discord REST API, WebSocket gateway, CDN. | CDN endpoint (`cdn.discordapp.com`) allows GET to any path. WebSocket uses `access: full` (no inspection). | +| `github` | GitHub and GitHub REST API. | Gives agent read/write access to repositories and issues via `git`. | +| `huggingface` | Hugging Face Hub (download-only) and inference router. | Allows downloading arbitrary models and datasets. POST is restricted to the inference router only. | +| `jira` | Atlassian Jira API. | Gives agent read/write access to project issues and comments. | +| `local-inference` | Local Ollama and vLLM through the host gateway. | Allows sandbox access to host-side local inference ports covered by the preset. | +| `npm` | npm and Yarn registries via L4 pass-through. | Allows installing arbitrary npm packages, which may contain malicious code. OpenShell still gates by host, port, and binary, but does not inspect HTTP method, path, or body for this preset. | +| `outlook` | Microsoft 365, Outlook. | Gives agent access to email. | +| `pypi` | Python Package Index (GET and HEAD only). | Allows installing arbitrary Python packages, which may contain malicious code. Publishing is blocked. | +| `slack` | Slack API, Socket Mode, webhooks. | WebSocket uses `access: full`. Agent can post to any channel the bot token has access to. | +| `telegram` | Telegram Bot API. | Agent can send messages to any chat the bot token has access to. | + +**Recommendation:** Apply presets only when the agent's task requires the integration. Review the preset's YAML file before applying to understand the endpoints, methods, and binary restrictions it adds. + +## Filesystem Controls + +NemoClaw restricts which paths the agent can read and write, protecting system binaries, configuration files, and gateway credentials. + +### Read-Only System Paths + +The container mounts system directories read-only to prevent the agent from modifying binaries, libraries, or configuration files. + +| Aspect | Detail | +|---|---| +| Default | `/usr`, `/lib`, `/proc`, `/dev/urandom`, `/app`, `/etc`, `/var/log` are read-only. | +| What you can change | Add or remove paths in the `filesystem_policy.read_only` section of the policy file. | +| Risk if relaxed | Making `/usr` or `/lib` writable lets the agent replace system binaries (such as `curl` or `node`) with trojanized versions. Making `/etc` writable lets the agent modify DNS resolution, TLS trust stores, or user accounts. | +| Recommendation | Never make system paths writable. If the agent needs a writable location for generated files, use a subdirectory of `/sandbox`. | + +### Agent Config Directory + +The `/sandbox/.openclaw` directory contains the OpenClaw gateway configuration (model routing, CORS settings, channel config). +The current entrypoint reads the gateway auth token from OpenClaw config when present, exports it as `OPENCLAW_GATEWAY_TOKEN`, and writes it to `/tmp/nemoclaw-proxy-env.sh` so interactive sandbox sessions can reach the gateway through system-wide shell hooks. +In root mode, the gateway process still runs as the separate `gateway` user, but the token is intentionally available to sandbox shells for local gateway access. + +Writable agent state such as plugins, skills, hooks, and workspace metadata lives directly under `/sandbox/.openclaw`. + +By default, this directory starts writable so the agent can manage its own config, install skills, and write to standard home-directory paths natively. +For sensitive workloads, use a reviewed host-side immutability workflow after initial setup so config and writable state entry points cannot be changed by the sandbox user. + +- **DAC permissions (default).** The sandbox user owns `/sandbox/.openclaw` with mode `2770` (setgid `sandbox:sandbox`) and `openclaw.json` with mode `660`, so the agent and its group can read and write config directly. A reviewed host-side immutability workflow should compare the intended ownership and mode with the live sandbox filesystem before treating the config tree as locked. +- **Config integrity hash.** The image includes a SHA256 hash of `openclaw.json`. In the default mutable state, `.config-hash` is sandbox-owned and is not a tamper-proof trust anchor, so startup does not fail closed on that hash. When the hash is root-owned and read-only, startup enforces it and refuses to start if the hash does not match. +- **Gateway token environment.** The gateway exports `OPENCLAW_GATEWAY_TOKEN` and writes it to `/tmp/nemoclaw-proxy-env.sh` for interactive sandbox sessions. Keep this in mind when deciding whether a workload should run with mutable config or an immutable config posture. + +| Aspect | Detail | +|---|---| +| Default | The sandbox keeps `/sandbox/.openclaw` writable (`2770 sandbox:sandbox`), sets `openclaw.json` to `660 sandbox:sandbox`, lets the agent manage state directly, and has the gateway place `OPENCLAW_GATEWAY_TOKEN` in `/tmp/nemoclaw-proxy-env.sh` for interactive shells. | +| What you can change | Apply a reviewed host-side immutability workflow to lock config and state directories with DAC permissions and the immutable flag where available. | +| Risk of default | A writable `.openclaw` directory lets the agent modify its own gateway config: disabling CORS or redirecting inference to an attacker-controlled endpoint. | +| Recommendation | For always-on assistants handling sensitive workloads, lock config after initial setup. For development workflows, the writable default is appropriate. | + +### Writable Paths + +The agent has read-write access to `/sandbox`, `/tmp`, and `/dev/null`. + +| Aspect | Detail | +|---|---| +| Default | `/sandbox` (agent workspace), `/tmp` (temporary files), `/dev/null`. | +| What you can change | Add additional writable paths in `filesystem_policy.read_write`. | +| Risk if relaxed | Each additional writable path expands the agent's ability to persist data and potentially modify system behavior. Adding `/var` lets the agent write to log directories. Adding `/home` gives access to other user directories. | +| Recommendation | Keep writable paths to `/sandbox` and `/tmp`. If the agent needs a persistent working directory, create a subdirectory under `/sandbox`. | + +### Landlock LSM Enforcement + +Landlock is a Linux Security Module that enforces filesystem access rules at the kernel level. + +| Aspect | Detail | +|---|---| +| Default | `compatibility: best_effort`. The entrypoint applies Landlock rules when the kernel supports them and silently skips them on older kernels. | +| What you can change | This is a NemoClaw default, not a user-facing knob. | +| Risk if relaxed | On kernels without Landlock support (pre-5.13), filesystem restrictions rely solely on container mount configuration, which is less granular. | +| Recommendation | Run on a kernel that supports Landlock (5.13+). Ubuntu 22.04 LTS and later include Landlock support. | + +## Process Controls + +NemoClaw limits the capabilities, user privileges, and resource quotas available to processes inside the sandbox. + +### Capability Drops + +The entrypoint drops dangerous Linux capabilities from the bounding set at startup using `capsh`. +This limits what capabilities any child process (gateway, sandbox, agent) can ever acquire. +When the entrypoint switches from root to the `sandbox` and `gateway` users, it uses `setpriv` when available to remove the remaining privilege-separation capabilities from the child process at the same time as the user change. + +The initial entrypoint drop removes `cap_sys_admin`, `cap_sys_ptrace`, `cap_net_raw`, `cap_dac_override`, `cap_sys_chroot`, `cap_fsetid`, `cap_setfcap`, `cap_mknod`, `cap_audit_write`, and `cap_net_bind_service`. +During `setpriv` step-down, the child process also loses `cap_setuid`, `cap_setgid`, `cap_fowner`, `cap_chown`, and `cap_kill`. + +This is best-effort: if `capsh` is not available or `CAP_SETPCAP` is not in the bounding set, the entrypoint logs a warning and continues with the default capability set. +If `setpriv` is unavailable, the entrypoint falls back to `gosu` and logs a warning that the remaining bounding-set capabilities were retained for the child process. +For additional protection, pass `--cap-drop=ALL` with `docker run` or Compose (see Sandbox Hardening (use the `nemoclaw-user-deploy-remote` skill)). + +| Aspect | Detail | +|---|---| +| Default | The entrypoint drops dangerous capabilities at startup using `capsh`, then uses `setpriv` during user step-down when possible. Best-effort. | +| What you can change | When launching with `docker run` directly, pass `--cap-drop=ALL --cap-add=NET_BIND_SERVICE` for stricter enforcement. In the standard NemoClaw flow (with `nemoclaw onboard`), the entrypoint handles capability dropping automatically. | +| Risk if relaxed | `CAP_SYS_ADMIN` and `CAP_SYS_PTRACE` expand kernel and process attack surface. `CAP_NET_RAW` allows raw socket access for network sniffing. `CAP_DAC_OVERRIDE` bypasses filesystem permission checks. If `capsh` or `setpriv` cannot run, the container retains more of the runtime-provided capability set. | +| Recommendation | Run on an image that includes `capsh` and `setpriv` (the NemoClaw image includes them). For defense-in-depth, also pass `--cap-drop=ALL` at the container runtime level. | + +### Gateway Process Isolation + +The OpenClaw gateway runs as a separate `gateway` user, not as the `sandbox` user that runs the agent. + +| Aspect | Detail | +|---|---| +| Default | The entrypoint starts the gateway process using `gosu gateway`, isolating it from the agent's `sandbox` user. | +| What you can change | This is not a user-facing knob. The entrypoint enforces it when running as root. In non-root mode (when OpenShell sets `no-new-privileges`), gateway process isolation does not work because `gosu` cannot change users. | +| Risk if relaxed | If the gateway and agent run as the same user, the agent can kill the gateway process and restart it with a tampered configuration (the "fake-HOME" attack). | +| Recommendation | No action needed. The entrypoint handles this automatically. Be aware that non-root mode disables this isolation. | + +### No New Privileges + +The `no-new-privileges` flag prevents processes from gaining additional privileges through setuid binaries or capability inheritance. + +| Aspect | Detail | +|---|---| +| Default | OpenShell sets `PR_SET_NO_NEW_PRIVS` using `prctl()` inside the sandbox process as part of the seccomp filter setup. The NemoClaw Compose example also shows the equivalent `security_opt: no-new-privileges:true` setting. | +| What you can change | OpenShell's seccomp path enforces this inside the sandbox. It is not a user-facing knob. | +| Risk if relaxed | Without this flag, a compromised process could execute a setuid binary to escalate to root inside the container, then attempt container escape techniques. | +| Recommendation | No action needed. OpenShell enforces this automatically when the sandbox network policy is active. This flag prevents `gosu` from switching users, so non-root mode disables gateway process isolation in the NemoClaw entrypoint. | + +### Process Limit + +A process limit caps the number of processes the sandbox user can spawn. +The entrypoint sets both soft and hard limits using `ulimit -u 512`. +This is best-effort: if the container runtime restricts `ulimit` modification, the entrypoint logs a security warning and continues without the limit. + +| Aspect | Detail | +|---|---| +| Default | 512 processes (`ulimit -u 512`), best-effort. | +| What you can change | Increase or decrease the limit with `--ulimit nproc=N:N` in `docker run` or the `ulimits` section in Compose. The runtime-level ulimit takes precedence over the entrypoint's setting. | +| Risk if relaxed | Removing or raising the limit makes the sandbox vulnerable to fork-bomb attacks, where a runaway process spawns children until the host runs out of resources. If the entrypoint cannot set the limit (logs `[SECURITY] Could not set soft/hard nproc limit`), the container runs without process limits. | +| Recommendation | Keep the default at 512. If the agent runs workloads that spawn many child processes (such as parallel test runners), increase to 1024 and monitor host resource usage. If the entrypoint logs a warning about ulimit restrictions, set the limit through the container runtime instead. | + +### Non-Root User + +The sandbox runs agent processes as a dedicated `sandbox` user and group. +The entrypoint starts as root for privilege separation, then drops to the `sandbox` user for all agent commands. + +| Aspect | Detail | +|---|---| +| Default | `run_as_user: sandbox`, `run_as_group: sandbox`. A separate `gateway` user runs the gateway process. | +| What you can change | Change the `process` section in the policy file to run as a different user. | +| Risk if relaxed | Running as `root` inside the container gives the agent access to modify any file in the container filesystem and increases the impact of container escape vulnerabilities. | +| Recommendation | Never run as root. Keep the `sandbox` user. | + +### PATH Hardening + +The entrypoint locks the `PATH` environment variable to system directories, preventing the agent from injecting malicious binaries into command resolution. + +| Aspect | Detail | +|---|---| +| Default | The entrypoint sets `PATH` to `/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin` at startup. | +| What you can change | This is not a user-facing knob. The entrypoint enforces it. | +| Risk if relaxed | Without PATH hardening, the agent could create an executable named `curl` or `git` in a writable directory earlier in the PATH, intercepting commands run by the entrypoint or other processes. | +| Recommendation | No action needed. The entrypoint handles this automatically. | + +### Build Toolchain Removal + +The Dockerfile removes compilers and network probes from the runtime image. + +| Aspect | Detail | +|---|---| +| Default | The Dockerfile purges `gcc`, `gcc-12`, `g++`, `g++-12`, `cpp`, `cpp-12`, `make`, `netcat-openbsd`, `netcat-traditional`, and `ncat` from the sandbox image. | +| What you can change | Modify the Dockerfile to keep these tools, or install them at runtime if package manager access is allowed. | +| Risk if relaxed | A compiler lets the agent build arbitrary native code, including kernel exploits or custom network tools. `netcat` enables arbitrary TCP connections that bypass HTTP-level policy enforcement. | +| Recommendation | Keep build tools removed. If the agent needs to compile code, run the build in a separate, purpose-built container and copy artifacts into the sandbox. | + +### Image Digest Pinning + +The blueprint references the sandbox image by an immutable `@sha256:` digest instead of a mutable tag such as `:latest`. +A registry compromise or accidental force-push cannot silently swap the sandbox image. + +| Aspect | Detail | +|---|---| +| Default | `nemoclaw-blueprint/blueprint.yaml` pins the sandbox image by digest. A CI regression test blocks any mutable-tag reference from merging. | +| What you can change | Contributors bumping the sandbox image must update the digest in `blueprint.yaml`. Release tooling should rewrite the digest automatically. | +| Risk if relaxed | Reverting to a mutable tag (`:latest`) allows a registry-side change to replace the sandbox image without any blueprint update, which is a supply-chain risk. | +| Recommendation | Always reference the sandbox image by digest. If you build a custom image with `nemoclaw onboard --from`, the digest constraint does not apply to your local build. | + +### Auth Profile Permissions + +The entrypoint and migration flows enforce `chmod 600` on all `auth-profiles.json` files under `~/.openclaw`. +This prevents other users on the host from reading stored credentials. + +| Aspect | Detail | +|---|---| +| Default | `600` permissions applied recursively at startup and after migration restores. | +| What you can change | This is not a user-facing knob. The entrypoint enforces it. | +| Risk if relaxed | Looser permissions let other users or processes on the host read provider API keys and tokens stored in auth profiles. | +| Recommendation | No action needed. If you see a `permission denied` error when reading auth profiles, verify that you are running as the same user who created them. | + +## Gateway Authentication Controls + +The OpenClaw gateway authenticates devices that connect to the Control UI dashboard. +NemoClaw hardens these defaults at image build time. + +### Device Authentication + +Device authentication requires each connecting device to go through a pairing flow before it can interact with the gateway. + +| Aspect | Detail | +|---|---| +| Default | Enabled. The gateway requires device pairing for all connections. | +| What you can change | Set `NEMOCLAW_DISABLE_DEVICE_AUTH=1` as a Docker build argument to disable device authentication. This is a build-time setting baked into `openclaw.json` and verified by hash at startup. | +| Risk if relaxed | Disabling device auth allows any device on the network to connect to the gateway without proving identity. This is dangerous when combined with LAN-bind changes or cloudflared tunnels in remote deployments, resulting in an unauthenticated, publicly reachable dashboard. | +| Recommendation | Keep device auth enabled (the default). Only disable it for headless or development environments where no untrusted devices can reach the gateway. | + +### Gateway Bind Address + +NemoClaw binds the OpenShell gateway to loopback by default. + +| Aspect | Detail | +|---|---| +| Default | `NEMOCLAW_GATEWAY_BIND_ADDRESS=127.0.0.1`. | +| What you can change | Set `NEMOCLAW_GATEWAY_BIND_ADDRESS=0.0.0.0` before onboarding to listen on all IPv4 interfaces. | +| Risk if relaxed | Other hosts on the network may be able to reach the OpenShell gateway. | +| Recommendation | Keep the loopback default unless the gateway must be reachable from another host. | + +### Insecure Auth Derivation + +The `allowInsecureAuth` setting controls whether the gateway permits non-HTTPS authentication. + +| Aspect | Detail | +|---|---| +| Default | Derived from the `CHAT_UI_URL` scheme at build time. When the URL uses `http://` (local development), insecure auth is allowed. When it uses `https://` (remote or production), insecure auth is blocked. | +| What you can change | This is derived automatically from `CHAT_UI_URL`. Set `CHAT_UI_URL` to an `https://` URL to enforce secure auth. | +| Risk if relaxed | Allowing insecure auth over HTTPS defeats the purpose of TLS, because authentication tokens transit in cleartext. | +| Recommendation | Use `https://` for any deployment accessible beyond `localhost`. The default local URL (`http://127.0.0.1:18789`) correctly allows insecure auth for local development. | + +### Auto-Pair Client Allowlist + +The auto-pair watcher automatically approves device pairing requests from recognized clients, so you do not need to manually approve the Control UI. + +| Aspect | Detail | +|---|---| +| Default | The watcher approves devices with `clientId` set to `openclaw-control-ui` or `clientMode` set to `webchat`. All other clients are rejected and logged. | +| What you can change | This is not a user-facing knob. The allowlist is defined in the entrypoint script. | +| Risk if relaxed | Approving all device types without validation lets rogue or unexpected clients pair with the gateway unchallenged. | +| Recommendation | No action needed. The entrypoint handles this automatically. If you see `[auto-pair] rejected unknown client=...` in the logs, investigate the source of the unexpected connection. | + +### CLI Secret Redaction + +The CLI automatically redacts secret patterns (API keys, bearer tokens, provider credentials) from command output and error messages before logging them. + +| Aspect | Detail | +|---|---| +| Default | Enabled. The runner redacts secrets from stdout, stderr, and thrown error messages. | +| What you can change | This is not a user-facing knob. The CLI enforces it on all command output paths. | +| Risk if relaxed | Without redaction, secrets could appear in terminal scrollback, log files, or debug output shared in bug reports. | +| Recommendation | No action needed. If you share `nemoclaw debug` output, verify that no secrets appear in the collected diagnostics. | + +### Memory Secret Scanner + +The NemoClaw plugin blocks the agent from writing likely secrets (API keys, tokens, private keys) into persistent memory files. +The scanner intercepts Write, Edit, and similar tool calls targeting memory and workspace paths before they reach disk. + +| Aspect | Detail | +|---|---| +| Default | Enabled. The plugin registers a `before_tool_call` hook that scans for 14 high-confidence secret patterns. | +| What it covers | Three classifiers, all enforced through `isMemoryPath()`: (1) absolute `MEMORY_PATH_SEGMENTS` such as `/.openclaw/memory/`, `/.openclaw/workspace/`, `/.openclaw/agents/`, `/.openclaw/skills/`, `/.openclaw/hooks/`, `/.openclaw/credentials/`, `/.openclaw/openclaw.json`, `/.nemoclaw/`; (2) canonical workspace basenames in `MEMORY_BASENAMES` (`IDENTITY.md`, `MEMORY.md`, `SOUL.md`, `USER.md`, `AGENTS.md`) matched regardless of the surrounding path; and (3) lexically-normalized workspace-relative writes matching `MEMORY_RELATIVE_PREFIXES` (`.openclaw/`, `.nemoclaw/`, `memory/`) or named workspace daily memory paths, for embedded-fallback mode where the host's path resolver is unavailable. | +| What you can change | This is not a user-facing knob. The plugin enforces it automatically. | +| Risk if relaxed | Without scanning, the agent could persist API keys or tokens in memory files that survive across sessions and backups. | +| Recommendation | No action needed. If a write is blocked, the agent receives an actionable error listing the detected patterns. | + +## Inference Controls + +OpenShell routes all inference traffic through the gateway to isolate provider credentials from the sandbox. + +### Routed Inference through `inference.local` + +The OpenShell gateway intercepts all inference requests from the agent and routes them to the configured provider. +The agent never receives the provider API key. + +| Aspect | Detail | +|---|---| +| Default | The agent talks to `inference.local`. The host owns the credential and upstream endpoint. | +| What you can change | You cannot configure this architecture. The system always enforces it. | +| Risk if bypassed | If the agent could reach an inference endpoint directly (by adding it to the network policy), it would need an API key. Since the sandbox does not contain credentials, this acts as defense-in-depth. However, adding an inference provider's host to the network policy without going through OpenShell routing could let the agent use a stolen or hardcoded key. | +| Recommendation | Do not add inference provider hosts (such as `api.openai.com` or `api.anthropic.com`) to the network policy. Use OpenShell inference routing instead. | + +### Provider Trust Tiers + +Different inference providers have different trust and cost profiles. + +| Provider | Trust level | Cost risk | Data handling | +|---|---|---|---| +| NVIDIA Endpoints | High. Hosted on `build.nvidia.com`. | Pay-per-token with an API key. Unattended agents can accumulate cost. | NVIDIA infrastructure processes requests. | +| OpenAI | High. Commercial API. | Pay-per-token. Same cost risk as NVIDIA Endpoints. | Subject to OpenAI data policies. | +| Anthropic | High. Commercial API. | Pay-per-token. Same cost risk as NVIDIA Endpoints. | Subject to Anthropic data policies. | +| Google Gemini | High. Commercial API. | Pay-per-token. Same cost risk as NVIDIA Endpoints. | Subject to Google data policies. | +| Local Ollama | Self-hosted. No data leaves the machine. | No per-token cost. GPU/CPU resource cost. | Data stays local. | +| Custom compatible endpoint | Varies. Depends on the proxy or gateway. | Varies. | Depends on the endpoint operator. | + +**Recommendation:** For sensitive workloads, use local Ollama to keep data on-premise. For general use, NVIDIA Endpoints provide a good balance of capability and trust. Review the data policies of any cloud provider you use. + +### Experimental Providers + +The `NEMOCLAW_EXPERIMENTAL=1` environment variable gates local NVIDIA NIM and generic Linux managed vLLM install/start. DGX Spark and DGX Station managed vLLM entries are offered by default, and an already-running vLLM server on `localhost:8000` is offered in the menu without a flag, because selecting either is an explicit user action. + +| Aspect | Detail | +|---|---| +| Default | Local NVIDIA NIM and generic Linux managed vLLM install/start are hidden. DGX Spark and DGX Station managed vLLM entries, plus already-running vLLM on `localhost:8000`, are offered when detected. | +| What you can change | Set `NEMOCLAW_EXPERIMENTAL=1` before running `nemoclaw onboard` to surface Local NIM and generic Linux managed vLLM. To request only the managed vLLM path non-interactively, set `NEMOCLAW_PROVIDER=install-vllm`. | +| Risk if selected | NemoClaw has not fully validated these providers. NIM requires a NIM-capable GPU. The managed vLLM path pulls a container image and starts it on a supported NVIDIA GPU host. Misconfiguration can cause failed inference or unexpected behavior. | +| Recommendation | Use experimental providers only for evaluation. Do not rely on them for always-on assistants. | + +## Posture Profiles + +The following profiles describe how to configure NemoClaw for different use cases. +These are not separate policy files. +They provide guidance on which controls to keep tight or relax. + +### Locked-Down (Default) + +Use for always-on assistants with minimal external access. + +- Keep all defaults. Do not add presets. +- Use operator approval for any endpoint the agent requests. +- Use NVIDIA Endpoints or local Ollama for inference. +- Monitor the TUI for unexpected network requests. + +### Development + +Use when the agent needs package registries, Docker Hub, or broader GitHub access during development tasks. + +- Apply the `pypi` and `npm` presets for package installation. +- Keep binary restrictions on all presets. +- Review the agent's network activity periodically with `openshell term`. +- Use operator approval for any endpoint not covered by a preset. + +### Integration Testing + +Use when the agent talks to internal APIs or third-party services during testing. + +- Add custom endpoint entries with tight path and method restrictions. +- Use `protocol: rest` for all HTTP APIs to maintain inspection. +- Use operator approval for unknown endpoints during test runs. +- Review and clean up the baseline policy after testing. Remove endpoints that are no longer needed. + +## Common Mistakes + +The following patterns weaken security without providing meaningful benefit. + +| Mistake | Why it matters | What to do instead | +|---------|---------------|-------------------| +| Omitting `protocol: rest` on REST API endpoints without a compatibility reason | Endpoints without a `protocol` field use L4-only enforcement. The proxy allows the TCP stream through after checking host, port, and binary, but cannot see or filter individual HTTP requests. | Add `protocol: rest` with explicit `rules` to enable per-request method and path control on REST APIs. Use L4 pass-through only for documented cases such as npm/Yarn on Node 22, where the client requires a CONNECT tunnel that L7 inspection would break. | +| Adding endpoints to the baseline policy for one-off requests | Adding an endpoint to the baseline policy makes it permanently reachable across all sandbox instances. | Use operator approval. Approved endpoints persist within the sandbox instance but reset when you destroy and recreate the sandbox. | +| Relying solely on the entrypoint for capability drops | The entrypoint drops dangerous capabilities using `capsh`, but this is best-effort. If `capsh` is unavailable or `CAP_SETPCAP` is not in the bounding set, the container runs with the default capability set. | Pass `--cap-drop=ALL` at the container runtime level as defense-in-depth. | +| Leaving `/sandbox/.openclaw` writable on sensitive workloads | This directory contains the OpenClaw gateway configuration. A writable `.openclaw` lets the agent disable CORS, redirect inference routing, or weaken gateway protections. | Lock config for always-on assistants handling sensitive data. | +| Adding inference provider hosts to the network policy | Direct network access to an inference host bypasses credential isolation and usage tracking. | Use OpenShell inference routing instead of adding hosts like `api.openai.com` or `api.anthropic.com` to the network policy. | +| Disabling device auth for remote deployments | Without device auth, any device on the network can connect to the gateway without pairing. Combined with a cloudflared tunnel, this makes the dashboard publicly accessible and unauthenticated. | Keep `NEMOCLAW_DISABLE_DEVICE_AUTH` at its default (`0`). Only set it to `1` for local headless or development environments. | + +## Known Limitations + +| Limitation | Impact | Mitigation | +|-----------|--------|------------| +| `openclaw agent --local` bypasses gateway | Secret scanning, network policy, and inference auth are not enforced when the agent runs in local mode. | A runtime warning is emitted when `--local` is detected. Avoid `--local` for production workflows. A future OpenClaw-level hook will close this gap. | +| Direct filesystem writes bypass secret scanner | The scanner intercepts OpenClaw tool calls, not raw filesystem writes (e.g., `echo secret > file`). | Landlock restricts writable paths. The scanner is application-layer defense-in-depth, not a filesystem-level control. | +| Base64/hex-encoded secrets are not detected | Content-based regex scanning cannot detect encoded or obfuscated secrets. | Use environment variables or credential stores instead of writing secrets to files. | + ## References -- **Load [references/best-practices.md](references/best-practices.md)** when evaluating security posture, reviewing sandbox security defaults, or assessing control trade-offs. Presents a risk framework for every configurable security control in NemoClaw. -- **Load [references/openclaw-controls.md](references/openclaw-controls.md)** when reviewing the security boundary between NemoClaw and OpenClaw or assessing what NemoClaw does not cover. Lists OpenClaw security controls that operate independently of NemoClaw, including prompt injection detection, tool access control, rate limiting, environment variable policy, audit framework, supply chain scanning, messaging access policy, context visibility, and safe regex. - **Load [references/credential-storage.md](references/credential-storage.md)** when reviewing how credentials are handled, locating a stored credential, or assessing the storage threat model. Covers where NemoClaw stores provider credentials, why nothing is persisted to host disk, and how the OpenShell gateway acts as the single system of record. +- **Load [references/openclaw-controls.md](references/openclaw-controls.md)** when reviewing the security boundary between NemoClaw and OpenClaw or assessing what NemoClaw does not cover. Lists OpenClaw security controls that operate independently of NemoClaw, including prompt injection detection, tool access control, rate limiting, environment variable policy, audit framework, supply chain scanning, messaging access policy, context visibility, and safe regex. + +## Related Skills + +- `nemoclaw-user-reference` — Network Policies (use the `nemoclaw-user-reference` skill) for the full baseline policy reference +- `nemoclaw-user-manage-policy` — Customize the Network Policy (use the `nemoclaw-user-manage-policy` skill) for static and dynamic policy changes +- `nemoclaw-user-deploy-remote` — Sandbox Hardening (use the `nemoclaw-user-deploy-remote` skill) for container-level security measures +- `nemoclaw-user-configure-inference` — Inference Options (use the `nemoclaw-user-configure-inference` skill) for provider configuration details +- `nemoclaw-user-overview` — How It Works (use the `nemoclaw-user-overview` skill) for the protection layer architecture diff --git a/.agents/skills/nemoclaw-user-configure-security/references/best-practices.md b/.agents/skills/nemoclaw-user-configure-security/references/best-practices.md deleted file mode 100644 index 440571e4d2..0000000000 --- a/.agents/skills/nemoclaw-user-configure-security/references/best-practices.md +++ /dev/null @@ -1,511 +0,0 @@ - - -# NemoClaw Security Best Practices: Controls, Risks, and Posture Profiles - -NemoClaw ships with deny-by-default security controls across four layers: network, filesystem, process, and inference. -You can tune every control, but each change shifts the risk profile. -This page documents every configurable knob, its default, what it protects, the concrete risk of relaxing it, and a recommendation for common use cases. - -For background on how the layers fit together, refer to How It Works (use the `nemoclaw-user-overview` skill). - -## Protection Layers at a Glance - -NemoClaw enforces security at four layers. -NemoClaw locks some when it creates the sandbox and requires a restart to change them. -You can hot-reload others while the sandbox runs. - -The following diagram shows the default posture immediately after `nemoclaw onboard`, before you approve any endpoints or apply any presets. - -```mermaid -flowchart TB - subgraph HOST["Your Machine: default posture after nemoclaw onboard"] - direction TB - - YOU["👤 Operator"] - - subgraph NC["NemoClaw + OpenShell"] - direction TB - - subgraph SB["Sandbox: the agent's isolated world"] - direction LR - PROC["⚙️ Process Layer
Controls what the agent can execute"] - FS["📁 Filesystem Layer
Controls what the agent can read and write"] - AGENT["🤖 Agent"] - end - - subgraph GW["Gateway: the gatekeeper"] - direction LR - NET["🌐 Network Layer
Controls where the agent can connect"] - INF["🧠 Inference Layer
Controls which AI models the agent can use"] - end - end - end - - OUTSIDE["🌍 Outside World
Internet · AI Providers · APIs"] - - AGENT -- "all requests" --> GW - GW -- "approved only" --> OUTSIDE - YOU -. "approve / deny" .-> GW - - classDef agent fill:#76b900,stroke:#5a8f00,color:#fff,stroke-width:2px,font-weight:bold - classDef locked fill:#1a1a1a,stroke:#76b900,color:#fff,stroke-width:2px - classDef hot fill:#333,stroke:#76b900,color:#e6f2cc,stroke-width:2px - classDef external fill:#f5f5f5,stroke:#ccc,color:#1a1a1a,stroke-width:1px - classDef operator fill:#fff,stroke:#76b900,color:#1a1a1a,stroke-width:2px,font-weight:bold - - class AGENT agent - class PROC,FS locked - class NET,INF hot - class OUTSIDE external - class YOU operator - - style HOST fill:none,stroke:#76b900,stroke-width:2px,color:#1a1a1a - style NC fill:none,stroke:#76b900,stroke-width:1px,stroke-dasharray:5 5,color:#1a1a1a - style SB fill:#f5faed,stroke:#76b900,stroke-width:2px,color:#1a1a1a - style GW fill:#2a2a2a,stroke:#76b900,stroke-width:2px,color:#fff -``` - -| Layer | What it protects | Enforcement point | Changeable at runtime | -| --- | --- | --- | --- | -| Network | Unauthorized outbound connections and data exfiltration. | OpenShell gateway | Yes. Use `openshell policy set` or operator approval. | -| Filesystem | System binary tampering, credential theft, config manipulation. | Landlock LSM + container mounts | Landlock layout: no. Requires sandbox re-creation. Use host-side NemoClaw commands for durable config changes. | -| Process | Privilege escalation, fork bombs, syscall abuse. | Container runtime (Docker/K8s `securityContext`) | No. Requires sandbox re-creation. | -| Inference | Credential exposure, unauthorized model access, cost overruns. | OpenShell gateway | Yes. Use `nemoclaw inference set`. | - -## Network Controls - -NemoClaw controls which hosts, ports, and HTTP methods the sandbox can reach, and lets operators approve or deny requests in real time. - -### Deny-by-Default Egress - -The sandbox blocks all outbound connections unless you explicitly list the endpoint in the policy file `nemoclaw-blueprint/policies/openclaw-sandbox.yaml`. - -| Aspect | Detail | -|---|---| -| Default | All egress denied. Only endpoints in the baseline policy can receive traffic. | -| What you can change | Add endpoints to the policy file (static) or with `openshell policy set` (dynamic). | -| Risk if relaxed | Each allowed endpoint is a potential data exfiltration path. The agent can send workspace content, credentials, or conversation history to any reachable host. | -| Recommendation | Add only endpoints the agent needs for its task. Prefer operator approval for one-off requests over permanently widening the baseline. | - -### Binary-Scoped Endpoint Rules - -Each network policy entry restricts which executables can reach the endpoint using the `binaries` field. - -OpenShell identifies the calling binary by reading `/proc//exe` (the kernel-trusted executable path, not `argv[0]`), walking the process tree for ancestor binaries, and computing a SHA256 hash of each binary on first use. -If someone replaces a binary while the sandbox runs, the hash mismatch triggers an immediate deny. - -| Aspect | Detail | -|---|---| -| Default | Each endpoint restricts access to specific binaries. For example, the `github` preset restricts access so only `/usr/bin/git` can reach `github.com`. Binary paths support glob patterns (`*` matches one path component, `**` matches recursively). | -| What you can change | Add binaries to an endpoint entry, or omit the `binaries` field to allow any executable. | -| Risk if relaxed | Removing binary restrictions lets any process in the sandbox reach the endpoint. An agent could use `curl`, `wget`, or a Python script to exfiltrate data to an allowed host, bypassing the intended usage pattern. | -| Recommendation | Always scope endpoints to the binaries that need them. If the agent needs a host from a new binary, add that binary explicitly rather than removing the restriction. | - -### Path-Scoped HTTP Rules - -Endpoint rules restrict allowed HTTP methods and URL paths. - -| Aspect | Detail | -|---|---| -| Default | Some endpoints allow GET and POST on `/**` (for example, `clawhub.ai`). Others restrict methods and paths to specific API routes (for example, `integrate.api.nvidia.com` allows POST only to inference and embedding paths and GET to model listings). Read-only endpoints such as `docs.openclaw.ai`, the `npm_registry` baseline entry, and the `pypi` preset allow GET only (PyPI also allows HEAD). The `npm` preset is an intentional exception: npm/Yarn registry traffic uses L4 pass-through for Node 22 undici CONNECT compatibility. | -| What you can change | Add methods (PUT, DELETE, PATCH) or restrict paths to specific prefixes. | -| Risk if relaxed | Allowing all methods on an API endpoint gives the agent write and delete access. For example, allowing DELETE on `api.github.com` lets the agent delete repositories. | -| Recommendation | Use GET-only rules for endpoints that the agent only reads. Add write methods only for endpoints where the agent must create or modify resources. Restrict paths to specific API routes when possible. | - -### L4-Only vs L7 Inspection (`protocol` Field) - -All sandbox egress goes through OpenShell's CONNECT proxy. -The `protocol` field on an endpoint controls whether the proxy also inspects individual HTTP requests inside the tunnel. - -| Aspect | Detail | -|---|---| -| Default | Endpoints without a `protocol` field use L4-only enforcement: the proxy checks host, port, and binary identity, then relays the TCP stream without inspecting payloads. Setting `protocol: rest` enables L7 inspection: the proxy auto-detects and terminates TLS, then evaluates each HTTP request's method and path against the endpoint's `rules` or `access` preset. | -| What you can change | Add `protocol: rest` to an endpoint to enable per-request HTTP inspection. Use the `access` preset (`full`, `read-only`, `read-write`) or explicit `rules` to control allowed methods and paths. | -| Risk if relaxed | L4-only endpoints (no `protocol` field) allow the agent to send any data through the tunnel after the initial connection is permitted. The proxy cannot see or filter the HTTP method, path, or body. The `access: full` preset with `protocol: rest` enables inspection but allows all methods and paths, so it does not restrict what the agent can do at the HTTP level. | -| Recommendation | Use `protocol: rest` with specific `rules` for REST APIs where you want method and path control. Use `protocol: rest` with `access: read-only` for read-only endpoints. Omit `protocol` only for non-HTTP protocols (WebSocket, gRPC streaming), endpoints that do not need HTTP inspection, or documented compatibility exceptions that require a client-managed CONNECT tunnel. | - -### Operator Approval Flow - -When the agent reaches an unlisted endpoint, OpenShell blocks the request and prompts the operator in the TUI. - -| Aspect | Detail | -|---|---| -| Default | Enabled. The gateway blocks all unlisted endpoints and requires approval. | -| What you can change | The system merges approved endpoints into the sandbox's policy as a new durable revision. They persist across sandbox restarts within the same sandbox instance. However, when you destroy and recreate the sandbox (for example, by running `nemoclaw onboard`), the policy resets to the baseline defined in the blueprint. | -| Risk if relaxed | Approving an endpoint permanently widens the running sandbox's policy. If you approve a broad domain (such as a CDN that hosts arbitrary content), the agent can fetch anything from that domain until you destroy and recreate the sandbox. | -| Recommendation | Review each blocked request before approving. If you find yourself approving the same endpoint repeatedly, add it to the baseline policy with appropriate binary and path restrictions. To reset approved endpoints, destroy and recreate the sandbox. | - -### Policy Presets - -NemoClaw ships preset policy files in `nemoclaw-blueprint/policies/presets/` for common integrations. - -| Preset | What it enables | Key risk | -|---|---|---| -| `brave` | Brave Search API. | Agent can issue search queries. | -| `brew` | Homebrew (Linuxbrew) package manager. The sandbox base image includes the `brew` binary; this preset opens network egress to GitHub and the Homebrew formulae index so `brew install` can fetch bottles. | Allows installing arbitrary Homebrew packages, which may contain malicious code. | -| `discord` | Discord REST API, WebSocket gateway, CDN. | CDN endpoint (`cdn.discordapp.com`) allows GET to any path. WebSocket uses `access: full` (no inspection). | -| `github` | GitHub and GitHub REST API. | Gives agent read/write access to repositories and issues via `git`. | -| `huggingface` | Hugging Face Hub (download-only) and inference router. | Allows downloading arbitrary models and datasets. POST is restricted to the inference router only. | -| `jira` | Atlassian Jira API. | Gives agent read/write access to project issues and comments. | -| `local-inference` | Local Ollama and vLLM through the host gateway. | Allows sandbox access to host-side local inference ports covered by the preset. | -| `npm` | npm and Yarn registries via L4 pass-through. | Allows installing arbitrary npm packages, which may contain malicious code. OpenShell still gates by host, port, and binary, but does not inspect HTTP method, path, or body for this preset. | -| `outlook` | Microsoft 365, Outlook. | Gives agent access to email. | -| `pypi` | Python Package Index (GET and HEAD only). | Allows installing arbitrary Python packages, which may contain malicious code. Publishing is blocked. | -| `slack` | Slack API, Socket Mode, webhooks. | WebSocket uses `access: full`. Agent can post to any channel the bot token has access to. | -| `telegram` | Telegram Bot API. | Agent can send messages to any chat the bot token has access to. | - -**Recommendation:** Apply presets only when the agent's task requires the integration. Review the preset's YAML file before applying to understand the endpoints, methods, and binary restrictions it adds. - -## Filesystem Controls - -NemoClaw restricts which paths the agent can read and write, protecting system binaries, configuration files, and gateway credentials. - -### Read-Only System Paths - -The container mounts system directories read-only to prevent the agent from modifying binaries, libraries, or configuration files. - -| Aspect | Detail | -|---|---| -| Default | `/usr`, `/lib`, `/proc`, `/dev/urandom`, `/app`, `/etc`, `/var/log` are read-only. | -| What you can change | Add or remove paths in the `filesystem_policy.read_only` section of the policy file. | -| Risk if relaxed | Making `/usr` or `/lib` writable lets the agent replace system binaries (such as `curl` or `node`) with trojanized versions. Making `/etc` writable lets the agent modify DNS resolution, TLS trust stores, or user accounts. | -| Recommendation | Never make system paths writable. If the agent needs a writable location for generated files, use a subdirectory of `/sandbox`. | - -### Agent Config Directory - -The `/sandbox/.openclaw` directory contains the OpenClaw gateway configuration (model routing, CORS settings, channel config). -The current entrypoint reads the gateway auth token from OpenClaw config when present, exports it as `OPENCLAW_GATEWAY_TOKEN`, and writes it to `/tmp/nemoclaw-proxy-env.sh` so interactive sandbox sessions can reach the gateway through system-wide shell hooks. -In root mode, the gateway process still runs as the separate `gateway` user, but the token is intentionally available to sandbox shells for local gateway access. - -Writable agent state such as plugins, skills, hooks, and workspace metadata lives directly under `/sandbox/.openclaw`. - -By default, this directory starts writable so the agent can manage its own config, install skills, and write to standard home-directory paths natively. -For sensitive workloads, use a reviewed host-side immutability workflow after initial setup so config and writable state entry points cannot be changed by the sandbox user. - -- **DAC permissions (default).** The sandbox user owns `/sandbox/.openclaw` with mode `2770` (setgid `sandbox:sandbox`) and `openclaw.json` with mode `660`, so the agent and its group can read and write config directly. A reviewed host-side immutability workflow should compare the intended ownership and mode with the live sandbox filesystem before treating the config tree as locked. -- **Config integrity hash.** The image includes a SHA256 hash of `openclaw.json`. In the default mutable state, `.config-hash` is sandbox-owned and is not a tamper-proof trust anchor, so startup does not fail closed on that hash. When the hash is root-owned and read-only, startup enforces it and refuses to start if the hash does not match. -- **Gateway token environment.** The gateway exports `OPENCLAW_GATEWAY_TOKEN` and writes it to `/tmp/nemoclaw-proxy-env.sh` for interactive sandbox sessions. Keep this in mind when deciding whether a workload should run with mutable config or an immutable config posture. - -| Aspect | Detail | -|---|---| -| Default | The sandbox keeps `/sandbox/.openclaw` writable (`2770 sandbox:sandbox`), sets `openclaw.json` to `660 sandbox:sandbox`, lets the agent manage state directly, and has the gateway place `OPENCLAW_GATEWAY_TOKEN` in `/tmp/nemoclaw-proxy-env.sh` for interactive shells. | -| What you can change | Apply a reviewed host-side immutability workflow to lock config and state directories with DAC permissions and the immutable flag where available. | -| Risk of default | A writable `.openclaw` directory lets the agent modify its own gateway config: disabling CORS or redirecting inference to an attacker-controlled endpoint. | -| Recommendation | For always-on assistants handling sensitive workloads, lock config after initial setup. For development workflows, the writable default is appropriate. | - -### Writable Paths - -The agent has read-write access to `/sandbox`, `/tmp`, and `/dev/null`. - -| Aspect | Detail | -|---|---| -| Default | `/sandbox` (agent workspace), `/tmp` (temporary files), `/dev/null`. | -| What you can change | Add additional writable paths in `filesystem_policy.read_write`. | -| Risk if relaxed | Each additional writable path expands the agent's ability to persist data and potentially modify system behavior. Adding `/var` lets the agent write to log directories. Adding `/home` gives access to other user directories. | -| Recommendation | Keep writable paths to `/sandbox` and `/tmp`. If the agent needs a persistent working directory, create a subdirectory under `/sandbox`. | - -### Landlock LSM Enforcement - -Landlock is a Linux Security Module that enforces filesystem access rules at the kernel level. - -| Aspect | Detail | -|---|---| -| Default | `compatibility: best_effort`. The entrypoint applies Landlock rules when the kernel supports them and silently skips them on older kernels. | -| What you can change | This is a NemoClaw default, not a user-facing knob. | -| Risk if relaxed | On kernels without Landlock support (pre-5.13), filesystem restrictions rely solely on container mount configuration, which is less granular. | -| Recommendation | Run on a kernel that supports Landlock (5.13+). Ubuntu 22.04 LTS and later include Landlock support. | - -## Process Controls - -NemoClaw limits the capabilities, user privileges, and resource quotas available to processes inside the sandbox. - -### Capability Drops - -The entrypoint drops dangerous Linux capabilities from the bounding set at startup using `capsh`. -This limits what capabilities any child process (gateway, sandbox, agent) can ever acquire. -When the entrypoint switches from root to the `sandbox` and `gateway` users, it uses `setpriv` when available to remove the remaining privilege-separation capabilities from the child process at the same time as the user change. - -The initial entrypoint drop removes `cap_sys_admin`, `cap_sys_ptrace`, `cap_net_raw`, `cap_dac_override`, `cap_sys_chroot`, `cap_fsetid`, `cap_setfcap`, `cap_mknod`, `cap_audit_write`, and `cap_net_bind_service`. -During `setpriv` step-down, the child process also loses `cap_setuid`, `cap_setgid`, `cap_fowner`, `cap_chown`, and `cap_kill`. - -This is best-effort: if `capsh` is not available or `CAP_SETPCAP` is not in the bounding set, the entrypoint logs a warning and continues with the default capability set. -If `setpriv` is unavailable, the entrypoint falls back to `gosu` and logs a warning that the remaining bounding-set capabilities were retained for the child process. -For additional protection, pass `--cap-drop=ALL` with `docker run` or Compose (see Sandbox Hardening (use the `nemoclaw-user-deploy-remote` skill)). - -| Aspect | Detail | -|---|---| -| Default | The entrypoint drops dangerous capabilities at startup using `capsh`, then uses `setpriv` during user step-down when possible. Best-effort. | -| What you can change | When launching with `docker run` directly, pass `--cap-drop=ALL --cap-add=NET_BIND_SERVICE` for stricter enforcement. In the standard NemoClaw flow (with `nemoclaw onboard`), the entrypoint handles capability dropping automatically. | -| Risk if relaxed | `CAP_SYS_ADMIN` and `CAP_SYS_PTRACE` expand kernel and process attack surface. `CAP_NET_RAW` allows raw socket access for network sniffing. `CAP_DAC_OVERRIDE` bypasses filesystem permission checks. If `capsh` or `setpriv` cannot run, the container retains more of the runtime-provided capability set. | -| Recommendation | Run on an image that includes `capsh` and `setpriv` (the NemoClaw image includes them). For defense-in-depth, also pass `--cap-drop=ALL` at the container runtime level. | - -### Gateway Process Isolation - -The OpenClaw gateway runs as a separate `gateway` user, not as the `sandbox` user that runs the agent. - -| Aspect | Detail | -|---|---| -| Default | The entrypoint starts the gateway process using `gosu gateway`, isolating it from the agent's `sandbox` user. | -| What you can change | This is not a user-facing knob. The entrypoint enforces it when running as root. In non-root mode (when OpenShell sets `no-new-privileges`), gateway process isolation does not work because `gosu` cannot change users. | -| Risk if relaxed | If the gateway and agent run as the same user, the agent can kill the gateway process and restart it with a tampered configuration (the "fake-HOME" attack). | -| Recommendation | No action needed. The entrypoint handles this automatically. Be aware that non-root mode disables this isolation. | - -### No New Privileges - -The `no-new-privileges` flag prevents processes from gaining additional privileges through setuid binaries or capability inheritance. - -| Aspect | Detail | -|---|---| -| Default | OpenShell sets `PR_SET_NO_NEW_PRIVS` using `prctl()` inside the sandbox process as part of the seccomp filter setup. The NemoClaw Compose example also shows the equivalent `security_opt: no-new-privileges:true` setting. | -| What you can change | OpenShell's seccomp path enforces this inside the sandbox. It is not a user-facing knob. | -| Risk if relaxed | Without this flag, a compromised process could execute a setuid binary to escalate to root inside the container, then attempt container escape techniques. | -| Recommendation | No action needed. OpenShell enforces this automatically when the sandbox network policy is active. This flag prevents `gosu` from switching users, so non-root mode disables gateway process isolation in the NemoClaw entrypoint. | - -### Process Limit - -A process limit caps the number of processes the sandbox user can spawn. -The entrypoint sets both soft and hard limits using `ulimit -u 512`. -This is best-effort: if the container runtime restricts `ulimit` modification, the entrypoint logs a security warning and continues without the limit. - -| Aspect | Detail | -|---|---| -| Default | 512 processes (`ulimit -u 512`), best-effort. | -| What you can change | Increase or decrease the limit with `--ulimit nproc=N:N` in `docker run` or the `ulimits` section in Compose. The runtime-level ulimit takes precedence over the entrypoint's setting. | -| Risk if relaxed | Removing or raising the limit makes the sandbox vulnerable to fork-bomb attacks, where a runaway process spawns children until the host runs out of resources. If the entrypoint cannot set the limit (logs `[SECURITY] Could not set soft/hard nproc limit`), the container runs without process limits. | -| Recommendation | Keep the default at 512. If the agent runs workloads that spawn many child processes (such as parallel test runners), increase to 1024 and monitor host resource usage. If the entrypoint logs a warning about ulimit restrictions, set the limit through the container runtime instead. | - -### Non-Root User - -The sandbox runs agent processes as a dedicated `sandbox` user and group. -The entrypoint starts as root for privilege separation, then drops to the `sandbox` user for all agent commands. - -| Aspect | Detail | -|---|---| -| Default | `run_as_user: sandbox`, `run_as_group: sandbox`. A separate `gateway` user runs the gateway process. | -| What you can change | Change the `process` section in the policy file to run as a different user. | -| Risk if relaxed | Running as `root` inside the container gives the agent access to modify any file in the container filesystem and increases the impact of container escape vulnerabilities. | -| Recommendation | Never run as root. Keep the `sandbox` user. | - -### PATH Hardening - -The entrypoint locks the `PATH` environment variable to system directories, preventing the agent from injecting malicious binaries into command resolution. - -| Aspect | Detail | -|---|---| -| Default | The entrypoint sets `PATH` to `/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin` at startup. | -| What you can change | This is not a user-facing knob. The entrypoint enforces it. | -| Risk if relaxed | Without PATH hardening, the agent could create an executable named `curl` or `git` in a writable directory earlier in the PATH, intercepting commands run by the entrypoint or other processes. | -| Recommendation | No action needed. The entrypoint handles this automatically. | - -### Build Toolchain Removal - -The Dockerfile removes compilers and network probes from the runtime image. - -| Aspect | Detail | -|---|---| -| Default | The Dockerfile purges `gcc`, `gcc-12`, `g++`, `g++-12`, `cpp`, `cpp-12`, `make`, `netcat-openbsd`, `netcat-traditional`, and `ncat` from the sandbox image. | -| What you can change | Modify the Dockerfile to keep these tools, or install them at runtime if package manager access is allowed. | -| Risk if relaxed | A compiler lets the agent build arbitrary native code, including kernel exploits or custom network tools. `netcat` enables arbitrary TCP connections that bypass HTTP-level policy enforcement. | -| Recommendation | Keep build tools removed. If the agent needs to compile code, run the build in a separate, purpose-built container and copy artifacts into the sandbox. | - -### Image Digest Pinning - -The blueprint references the sandbox image by an immutable `@sha256:` digest instead of a mutable tag such as `:latest`. -A registry compromise or accidental force-push cannot silently swap the sandbox image. - -| Aspect | Detail | -|---|---| -| Default | `nemoclaw-blueprint/blueprint.yaml` pins the sandbox image by digest. A CI regression test blocks any mutable-tag reference from merging. | -| What you can change | Contributors bumping the sandbox image must update the digest in `blueprint.yaml`. Release tooling should rewrite the digest automatically. | -| Risk if relaxed | Reverting to a mutable tag (`:latest`) allows a registry-side change to replace the sandbox image without any blueprint update, which is a supply-chain risk. | -| Recommendation | Always reference the sandbox image by digest. If you build a custom image with `nemoclaw onboard --from`, the digest constraint does not apply to your local build. | - -### Auth Profile Permissions - -The entrypoint and migration flows enforce `chmod 600` on all `auth-profiles.json` files under `~/.openclaw`. -This prevents other users on the host from reading stored credentials. - -| Aspect | Detail | -|---|---| -| Default | `600` permissions applied recursively at startup and after migration restores. | -| What you can change | This is not a user-facing knob. The entrypoint enforces it. | -| Risk if relaxed | Looser permissions let other users or processes on the host read provider API keys and tokens stored in auth profiles. | -| Recommendation | No action needed. If you see a `permission denied` error when reading auth profiles, verify that you are running as the same user who created them. | - -## Gateway Authentication Controls - -The OpenClaw gateway authenticates devices that connect to the Control UI dashboard. -NemoClaw hardens these defaults at image build time. - -### Device Authentication - -Device authentication requires each connecting device to go through a pairing flow before it can interact with the gateway. - -| Aspect | Detail | -|---|---| -| Default | Enabled. The gateway requires device pairing for all connections. | -| What you can change | Set `NEMOCLAW_DISABLE_DEVICE_AUTH=1` as a Docker build argument to disable device authentication. This is a build-time setting baked into `openclaw.json` and verified by hash at startup. | -| Risk if relaxed | Disabling device auth allows any device on the network to connect to the gateway without proving identity. This is dangerous when combined with LAN-bind changes or cloudflared tunnels in remote deployments, resulting in an unauthenticated, publicly reachable dashboard. | -| Recommendation | Keep device auth enabled (the default). Only disable it for headless or development environments where no untrusted devices can reach the gateway. | - -### Gateway Bind Address - -NemoClaw binds the OpenShell gateway to loopback by default. - -| Aspect | Detail | -|---|---| -| Default | `NEMOCLAW_GATEWAY_BIND_ADDRESS=127.0.0.1`. | -| What you can change | Set `NEMOCLAW_GATEWAY_BIND_ADDRESS=0.0.0.0` before onboarding to listen on all IPv4 interfaces. | -| Risk if relaxed | Other hosts on the network may be able to reach the OpenShell gateway. | -| Recommendation | Keep the loopback default unless the gateway must be reachable from another host. | - -### Insecure Auth Derivation - -The `allowInsecureAuth` setting controls whether the gateway permits non-HTTPS authentication. - -| Aspect | Detail | -|---|---| -| Default | Derived from the `CHAT_UI_URL` scheme at build time. When the URL uses `http://` (local development), insecure auth is allowed. When it uses `https://` (remote or production), insecure auth is blocked. | -| What you can change | This is derived automatically from `CHAT_UI_URL`. Set `CHAT_UI_URL` to an `https://` URL to enforce secure auth. | -| Risk if relaxed | Allowing insecure auth over HTTPS defeats the purpose of TLS, because authentication tokens transit in cleartext. | -| Recommendation | Use `https://` for any deployment accessible beyond `localhost`. The default local URL (`http://127.0.0.1:18789`) correctly allows insecure auth for local development. | - -### Auto-Pair Client Allowlist - -The auto-pair watcher automatically approves device pairing requests from recognized clients, so you do not need to manually approve the Control UI. - -| Aspect | Detail | -|---|---| -| Default | The watcher approves devices with `clientId` set to `openclaw-control-ui` or `clientMode` set to `webchat`. All other clients are rejected and logged. | -| What you can change | This is not a user-facing knob. The allowlist is defined in the entrypoint script. | -| Risk if relaxed | Approving all device types without validation lets rogue or unexpected clients pair with the gateway unchallenged. | -| Recommendation | No action needed. The entrypoint handles this automatically. If you see `[auto-pair] rejected unknown client=...` in the logs, investigate the source of the unexpected connection. | - -### CLI Secret Redaction - -The CLI automatically redacts secret patterns (API keys, bearer tokens, provider credentials) from command output and error messages before logging them. - -| Aspect | Detail | -|---|---| -| Default | Enabled. The runner redacts secrets from stdout, stderr, and thrown error messages. | -| What you can change | This is not a user-facing knob. The CLI enforces it on all command output paths. | -| Risk if relaxed | Without redaction, secrets could appear in terminal scrollback, log files, or debug output shared in bug reports. | -| Recommendation | No action needed. If you share `nemoclaw debug` output, verify that no secrets appear in the collected diagnostics. | - -### Memory Secret Scanner - -The NemoClaw plugin blocks the agent from writing likely secrets (API keys, tokens, private keys) into persistent memory files. -The scanner intercepts Write, Edit, and similar tool calls targeting memory and workspace paths before they reach disk. - -| Aspect | Detail | -|---|---| -| Default | Enabled. The plugin registers a `before_tool_call` hook that scans for 14 high-confidence secret patterns. | -| What it covers | Three classifiers, all enforced through `isMemoryPath()`: (1) absolute `MEMORY_PATH_SEGMENTS` such as `/.openclaw/memory/`, `/.openclaw/workspace/`, `/.openclaw/agents/`, `/.openclaw/skills/`, `/.openclaw/hooks/`, `/.openclaw/credentials/`, `/.openclaw/openclaw.json`, `/.nemoclaw/`; (2) canonical workspace basenames in `MEMORY_BASENAMES` (`IDENTITY.md`, `MEMORY.md`, `SOUL.md`, `USER.md`, `AGENTS.md`) matched regardless of the surrounding path; and (3) lexically-normalized workspace-relative writes matching `MEMORY_RELATIVE_PREFIXES` (`.openclaw/`, `.nemoclaw/`, `memory/`) or named workspace daily memory paths, for embedded-fallback mode where the host's path resolver is unavailable. | -| What you can change | This is not a user-facing knob. The plugin enforces it automatically. | -| Risk if relaxed | Without scanning, the agent could persist API keys or tokens in memory files that survive across sessions and backups. | -| Recommendation | No action needed. If a write is blocked, the agent receives an actionable error listing the detected patterns. | - -## Inference Controls - -OpenShell routes all inference traffic through the gateway to isolate provider credentials from the sandbox. - -### Routed Inference through `inference.local` - -The OpenShell gateway intercepts all inference requests from the agent and routes them to the configured provider. -The agent never receives the provider API key. - -| Aspect | Detail | -|---|---| -| Default | The agent talks to `inference.local`. The host owns the credential and upstream endpoint. | -| What you can change | You cannot configure this architecture. The system always enforces it. | -| Risk if bypassed | If the agent could reach an inference endpoint directly (by adding it to the network policy), it would need an API key. Since the sandbox does not contain credentials, this acts as defense-in-depth. However, adding an inference provider's host to the network policy without going through OpenShell routing could let the agent use a stolen or hardcoded key. | -| Recommendation | Do not add inference provider hosts (such as `api.openai.com` or `api.anthropic.com`) to the network policy. Use OpenShell inference routing instead. | - -### Provider Trust Tiers - -Different inference providers have different trust and cost profiles. - -| Provider | Trust level | Cost risk | Data handling | -|---|---|---|---| -| NVIDIA Endpoints | High. Hosted on `build.nvidia.com`. | Pay-per-token with an API key. Unattended agents can accumulate cost. | NVIDIA infrastructure processes requests. | -| OpenAI | High. Commercial API. | Pay-per-token. Same cost risk as NVIDIA Endpoints. | Subject to OpenAI data policies. | -| Anthropic | High. Commercial API. | Pay-per-token. Same cost risk as NVIDIA Endpoints. | Subject to Anthropic data policies. | -| Google Gemini | High. Commercial API. | Pay-per-token. Same cost risk as NVIDIA Endpoints. | Subject to Google data policies. | -| Local Ollama | Self-hosted. No data leaves the machine. | No per-token cost. GPU/CPU resource cost. | Data stays local. | -| Custom compatible endpoint | Varies. Depends on the proxy or gateway. | Varies. | Depends on the endpoint operator. | - -**Recommendation:** For sensitive workloads, use local Ollama to keep data on-premise. For general use, NVIDIA Endpoints provide a good balance of capability and trust. Review the data policies of any cloud provider you use. - -### Experimental Providers - -The `NEMOCLAW_EXPERIMENTAL=1` environment variable gates local NVIDIA NIM and generic Linux managed vLLM install/start. DGX Spark and DGX Station managed vLLM entries are offered by default, and an already-running vLLM server on `localhost:8000` is offered in the menu without a flag, because selecting either is an explicit user action. - -| Aspect | Detail | -|---|---| -| Default | Local NVIDIA NIM and generic Linux managed vLLM install/start are hidden. DGX Spark and DGX Station managed vLLM entries, plus already-running vLLM on `localhost:8000`, are offered when detected. | -| What you can change | Set `NEMOCLAW_EXPERIMENTAL=1` before running `nemoclaw onboard` to surface Local NIM and generic Linux managed vLLM. To request only the managed vLLM path non-interactively, set `NEMOCLAW_PROVIDER=install-vllm`. | -| Risk if selected | NemoClaw has not fully validated these providers. NIM requires a NIM-capable GPU. The managed vLLM path pulls a container image and starts it on a supported NVIDIA GPU host. Misconfiguration can cause failed inference or unexpected behavior. | -| Recommendation | Use experimental providers only for evaluation. Do not rely on them for always-on assistants. | - -## Posture Profiles - -The following profiles describe how to configure NemoClaw for different use cases. -These are not separate policy files. -They provide guidance on which controls to keep tight or relax. - -### Locked-Down (Default) - -Use for always-on assistants with minimal external access. - -- Keep all defaults. Do not add presets. -- Use operator approval for any endpoint the agent requests. -- Use NVIDIA Endpoints or local Ollama for inference. -- Monitor the TUI for unexpected network requests. - -### Development - -Use when the agent needs package registries, Docker Hub, or broader GitHub access during development tasks. - -- Apply the `pypi` and `npm` presets for package installation. -- Keep binary restrictions on all presets. -- Review the agent's network activity periodically with `openshell term`. -- Use operator approval for any endpoint not covered by a preset. - -### Integration Testing - -Use when the agent talks to internal APIs or third-party services during testing. - -- Add custom endpoint entries with tight path and method restrictions. -- Use `protocol: rest` for all HTTP APIs to maintain inspection. -- Use operator approval for unknown endpoints during test runs. -- Review and clean up the baseline policy after testing. Remove endpoints that are no longer needed. - -## Common Mistakes - -The following patterns weaken security without providing meaningful benefit. - -| Mistake | Why it matters | What to do instead | -|---------|---------------|-------------------| -| Omitting `protocol: rest` on REST API endpoints without a compatibility reason | Endpoints without a `protocol` field use L4-only enforcement. The proxy allows the TCP stream through after checking host, port, and binary, but cannot see or filter individual HTTP requests. | Add `protocol: rest` with explicit `rules` to enable per-request method and path control on REST APIs. Use L4 pass-through only for documented cases such as npm/Yarn on Node 22, where the client requires a CONNECT tunnel that L7 inspection would break. | -| Adding endpoints to the baseline policy for one-off requests | Adding an endpoint to the baseline policy makes it permanently reachable across all sandbox instances. | Use operator approval. Approved endpoints persist within the sandbox instance but reset when you destroy and recreate the sandbox. | -| Relying solely on the entrypoint for capability drops | The entrypoint drops dangerous capabilities using `capsh`, but this is best-effort. If `capsh` is unavailable or `CAP_SETPCAP` is not in the bounding set, the container runs with the default capability set. | Pass `--cap-drop=ALL` at the container runtime level as defense-in-depth. | -| Leaving `/sandbox/.openclaw` writable on sensitive workloads | This directory contains the OpenClaw gateway configuration. A writable `.openclaw` lets the agent disable CORS, redirect inference routing, or weaken gateway protections. | Lock config for always-on assistants handling sensitive data. | -| Adding inference provider hosts to the network policy | Direct network access to an inference host bypasses credential isolation and usage tracking. | Use OpenShell inference routing instead of adding hosts like `api.openai.com` or `api.anthropic.com` to the network policy. | -| Disabling device auth for remote deployments | Without device auth, any device on the network can connect to the gateway without pairing. Combined with a cloudflared tunnel, this makes the dashboard publicly accessible and unauthenticated. | Keep `NEMOCLAW_DISABLE_DEVICE_AUTH` at its default (`0`). Only set it to `1` for local headless or development environments. | - -## Known Limitations - -| Limitation | Impact | Mitigation | -|-----------|--------|------------| -| `openclaw agent --local` bypasses gateway | Secret scanning, network policy, and inference auth are not enforced when the agent runs in local mode. | A runtime warning is emitted when `--local` is detected. Avoid `--local` for production workflows. A future OpenClaw-level hook will close this gap. | -| Direct filesystem writes bypass secret scanner | The scanner intercepts OpenClaw tool calls, not raw filesystem writes (e.g., `echo secret > file`). | Landlock restricts writable paths. The scanner is application-layer defense-in-depth, not a filesystem-level control. | -| Base64/hex-encoded secrets are not detected | Content-based regex scanning cannot detect encoded or obfuscated secrets. | Use environment variables or credential stores instead of writing secrets to files. | - -## Related Topics - -- Network Policies (use the `nemoclaw-user-reference` skill) for the full baseline policy reference. -- Customize the Network Policy (use the `nemoclaw-user-manage-policy` skill) for static and dynamic policy changes. -- Approve or Deny Network Requests (use the `nemoclaw-user-manage-policy` skill) for the operator approval flow. -- Sandbox Hardening (use the `nemoclaw-user-deploy-remote` skill) for container-level security measures. -- Inference Options (use the `nemoclaw-user-configure-inference` skill) for provider configuration details. -- How It Works (use the `nemoclaw-user-overview` skill) for the protection layer architecture. diff --git a/.agents/skills/nemoclaw-user-configure-security/references/credential-storage.md b/.agents/skills/nemoclaw-user-configure-security/references/credential-storage.md index b40b676a20..45ef77e3e0 100644 --- a/.agents/skills/nemoclaw-user-configure-security/references/credential-storage.md +++ b/.agents/skills/nemoclaw-user-configure-security/references/credential-storage.md @@ -107,4 +107,4 @@ On the next run NemoClaw prompts again unless the credential is supplied through ## Related Files -For the broader sandbox security model and operational trade-offs, see [Security Best Practices](best-practices.md) and Architecture (use the `nemoclaw-user-reference` skill). +For the broader sandbox security model and operational trade-offs, see [Security Best Practices](../SKILL.md) and Architecture (use the `nemoclaw-user-reference` skill). diff --git a/.agents/skills/nemoclaw-user-configure-security/references/openclaw-controls.md b/.agents/skills/nemoclaw-user-configure-security/references/openclaw-controls.md index 2ced0c76de..f65f32869e 100644 --- a/.agents/skills/nemoclaw-user-configure-security/references/openclaw-controls.md +++ b/.agents/skills/nemoclaw-user-configure-security/references/openclaw-controls.md @@ -117,5 +117,5 @@ The implementation detects unsafe nested quantifiers, bounds input length, and c ## Next Steps -- [Security Best Practices](best-practices.md) for NemoClaw's own security controls and risk framework. +- [Security Best Practices](../SKILL.md) for NemoClaw's own security controls and risk framework. - [Credential Storage](credential-storage.md) for how NemoClaw stores and protects provider credentials. diff --git a/.agents/skills/nemoclaw-user-deploy-remote/SKILL.md b/.agents/skills/nemoclaw-user-deploy-remote/SKILL.md index d2ac40e834..810915f4fd 100644 --- a/.agents/skills/nemoclaw-user-deploy-remote/SKILL.md +++ b/.agents/skills/nemoclaw-user-deploy-remote/SKILL.md @@ -1,6 +1,6 @@ --- name: "nemoclaw-user-deploy-remote" -description: "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. Trigger keywords - deploy nemoclaw remote gpu, nemoclaw brev cloud deployment, nemoclaw plugins, openclaw plugins, install openclaw plugin, nemoclaw onboard from dockerfile, nemoclaw brev web ui, nemoclaw getting started, brev quickstart, nvidia nemotron agent, nemoclaw sandbox hardening, container security, docker capabilities, process limits." +description: "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. Trigger keywords - nemoclaw brev web ui, nemoclaw getting started, brev quickstart, nvidia nemotron agent, deploy nemoclaw remote gpu, nemoclaw brev cloud deployment, nemoclaw plugins, openclaw plugins, install openclaw plugin, nemoclaw onboard from dockerfile, nemoclaw sandbox hardening, container security, docker capabilities, process limits." license: "Apache-2.0" --- diff --git a/.agents/skills/nemoclaw-user-get-started/SKILL.md b/.agents/skills/nemoclaw-user-get-started/SKILL.md index bc63352312..7d47188ab9 100644 --- a/.agents/skills/nemoclaw-user-get-started/SKILL.md +++ b/.agents/skills/nemoclaw-user-get-started/SKILL.md @@ -1,6 +1,6 @@ --- name: "nemoclaw-user-get-started" -description: "Installs NemoClaw, launches a sandbox, and runs the first agent prompt. Use when onboarding, installing, or launching a NemoClaw sandbox for the first time. Trigger keywords - nemoclaw quickstart, install nemoclaw openclaw sandbox, nemohermes quickstart, hermes agent nemoclaw, run hermes openshell sandbox, nemoclaw prerequisites, nemoclaw supported platforms, nemoclaw hardware software, nemoclaw windows wsl2 setup, nemoclaw install windows docker desktop." +description: "Lists the hardware, software, and container runtime requirements for running NemoClaw. Use when verifying prerequisites before installation. Trigger keywords - nemoclaw prerequisites, nemoclaw supported platforms, nemoclaw hardware software, nemohermes quickstart, hermes agent nemoclaw, run hermes openshell sandbox, nemoclaw quickstart, install nemoclaw openclaw sandbox, nemoclaw windows wsl2 setup, nemoclaw install windows docker desktop." license: "Apache-2.0" --- @@ -87,6 +87,172 @@ The onboard flow builds the sandbox image with `NEMOCLAW_DISABLE_DEVICE_AUTH=1` This is a build-time setting baked into the sandbox image, not a runtime knob. If you export `NEMOCLAW_DISABLE_DEVICE_AUTH` after onboarding finishes, it has no effect on an existing sandbox. +### Respond to the Onboard Wizard + +After the installer launches `nemoclaw onboard`, the wizard runs preflight checks, starts or reuses the OpenShell gateway, asks for an inference provider and model, collects any required credential, then asks for the sandbox name. +It prints a review summary before it registers the provider with OpenShell. +After you confirm, NemoClaw registers inference, prompts for optional web search and messaging channels, builds and starts the sandbox, sets up OpenClaw, then applies the selected network policy tier and presets. +At any prompt, press Enter to accept the default shown in `[brackets]`, type `back` to return to the previous prompt, or type `exit` to quit. +If existing sandbox sessions are running, the installer warns before onboarding because the setup can rebuild or upgrade sandboxes after the new sandbox launches. + +The inference provider prompt presents a numbered list. + +```text + 1) NVIDIA Endpoints + 2) OpenAI + 3) Other OpenAI-compatible endpoint + 4) Anthropic + 5) Other Anthropic-compatible endpoint + 6) Google Gemini + 7) Local Ollama (localhost:11434) + 8) Model Router (experimental) + Choose [1]: +``` + +Pick the option that matches where you want inference traffic to go, then expand the matching helper below for the follow-up prompts and the API key environment variable to set. +For the full list of providers and validation behavior, refer to Inference Options (use the `nemoclaw-user-configure-inference` skill). +Local Ollama appears when NemoClaw detects a usable local Ollama path or can offer an install or start action for your platform. +The Model Router option appears when the blueprint router profile is enabled. + +**Tip:** + +Export the API key before launching the installer so the wizard does not have to ask for it. +For example, run `export NVIDIA_API_KEY=` before `curl ... | bash`. +If you entered a key incorrectly, refer to Reset a Stored Credential (use the `nemoclaw-user-manage-sandboxes` skill) to clear and re-enter it. + +**Option 1: NVIDIA Endpoints:** + +Routes inference to models hosted on [build.nvidia.com](https://build.nvidia.com). + +Use `NVIDIA_API_KEY` for the API key. Get one from the [NVIDIA build API keys page](https://build.nvidia.com/settings/api-keys). + +Respond to the wizard as follows. + +1. At the `Choose [1]:` prompt, press Enter (or type `1`) to select **NVIDIA Endpoints**. +2. At the `NVIDIA_API_KEY:` prompt, paste your key if it is not already exported. +3. At the `Choose model [1]:` prompt, pick a curated model from the list (for example, `Nemotron 3 Super 120B`, `GLM-5`, `MiniMax M2.7`, `GPT-OSS 120B`, or `DeepSeek V4 Pro`), or pick `Other...` to enter any model ID from the [NVIDIA Endpoints catalog](https://build.nvidia.com). + +NemoClaw validates the model against the catalog API before creating the sandbox. + +**Tip:** + +Use this option for Nemotron and other models hosted on `build.nvidia.com`. If you run NVIDIA Nemotron from a self-hosted NIM, an enterprise gateway, or any other endpoint, choose **Option 3** instead, since all Nemotron models expose OpenAI-compatible APIs. + +**Option 2: OpenAI:** + +Routes inference to the OpenAI API at `https://api.openai.com/v1`. + +Use `OPENAI_API_KEY` for the API key. Get one from the [OpenAI API keys page](https://platform.openai.com/api-keys). + +Respond to the wizard as follows. + +1. At the `Choose [1]:` prompt, type `2` to select **OpenAI**. +2. At the `OPENAI_API_KEY:` prompt, paste your key if it is not already exported. +3. At the `Choose model [1]:` prompt, pick a curated model (for example, `gpt-5.4`, `gpt-5.4-mini`, `gpt-5.4-nano`, or `gpt-5.4-pro-2026-03-05`), or pick **Other...** to enter any OpenAI model ID. + +**Option 3: Other OpenAI-Compatible Endpoint:** + +Routes inference to any server that implements `/v1/chat/completions`, including OpenRouter, LocalAI, llama.cpp, vLLM behind a proxy, and any compatible gateway. + +Use `COMPATIBLE_API_KEY` for the API key. Set it to whatever credential your endpoint expects. If your endpoint does not require auth, use any non-empty placeholder. + +Respond to the wizard as follows. + +1. At the `Choose [1]:` prompt, type `3` to select **Other OpenAI-compatible endpoint**. +2. At the `OpenAI-compatible base URL` prompt, enter the provider's base URL. Find the exact value in your provider's API documentation. NemoClaw appends `/v1` automatically, so leave that suffix off. +3. At the `COMPATIBLE_API_KEY:` prompt, paste your key if it is not already exported. +4. At the `Other OpenAI-compatible endpoint model []:` prompt, enter the model ID exactly as it appears in your provider's model catalog. + +For example, when you use NVIDIA's OpenAI-compatible inference endpoint, enter `https://inference-api.nvidia.com` as the base URL and the model ID your endpoint exposes, such as `openai/openai/gpt-5.5`. + +NemoClaw sends a real inference request to validate the endpoint and model. +If the endpoint does not return the streaming events OpenClaw needs from the Responses API, NemoClaw falls back to the chat completions API and configures OpenClaw to use `openai-completions`. + +**Tip:** + +NVIDIA Nemotron models expose OpenAI-compatible APIs, so this option is the right choice for any Nemotron deployment that does not live on `build.nvidia.com`. Common examples include a self-hosted NIM container, an enterprise NVIDIA AI Enterprise gateway, or a vLLM/SGLang server running Nemotron weights. Point the base URL at your endpoint and enter the Nemotron model ID exactly as your server reports it. + +**Option 4: Anthropic:** + +Routes inference to the Anthropic Messages API at `https://api.anthropic.com`. + +Use `ANTHROPIC_API_KEY` for the API key. Get one from the [Anthropic console keys page](https://console.anthropic.com/settings/keys). + +Respond to the wizard as follows. + +1. At the `Choose [1]:` prompt, type `4` to select **Anthropic**. +2. At the `ANTHROPIC_API_KEY:` prompt, paste your key if it is not already exported. +3. At the `Choose model [1]:` prompt, pick a curated model (for example, `claude-sonnet-4-6`, `claude-haiku-4-5`, or `claude-opus-4-6`), or pick **Other...** to enter any Claude model ID. + +**Option 5: Other Anthropic-Compatible Endpoint:** + +Routes inference to any server that implements the Anthropic Messages API at `/v1/messages`, including Claude proxies, Bedrock-compatible gateways, and self-hosted Anthropic-compatible servers. + +Use `COMPATIBLE_ANTHROPIC_API_KEY` for the API key. Set it to whatever credential your endpoint expects. + +Respond to the wizard as follows. + +1. At the `Choose [1]:` prompt, type `5` to select **Other Anthropic-compatible endpoint**. +2. At the `Anthropic-compatible base URL` prompt, enter the proxy or gateway's base URL from its documentation. +3. At the `COMPATIBLE_ANTHROPIC_API_KEY:` prompt, paste your key if it is not already exported. +4. At the `Other Anthropic-compatible endpoint model []:` prompt, enter the model ID exactly as it appears in your gateway's model catalog. + +**Option 6: Google Gemini:** + +Routes inference to Google's OpenAI-compatible Gemini endpoint at `https://generativelanguage.googleapis.com/v1beta/openai/`. + +Use `GEMINI_API_KEY` for the API key. Get one from [Google AI Studio API keys](https://aistudio.google.com/app/apikey). + +Respond to the wizard as follows. + +1. At the `Choose [1]:` prompt, type `6` to select **Google Gemini**. +2. At the `GEMINI_API_KEY:` prompt, paste your key if it is not already exported. +3. At the `Choose model [5]:` prompt, pick a curated model (for example, `gemini-3.1-pro-preview`, `gemini-3.1-flash-lite-preview`, `gemini-3-flash-preview`, `gemini-2.5-pro`, `gemini-2.5-flash`, or `gemini-2.5-flash-lite`), or pick **Other...** to enter any Gemini model ID. + +**Option 7: Local Ollama:** + +Routes inference to a local Ollama instance. Depending on your platform, the wizard can use an existing daemon, start an installed daemon, or offer an install action. + +No API key is required. On non-WSL hosts, NemoClaw generates a token and starts an authenticated proxy so containers can reach Ollama without exposing the daemon directly to your network. +On WSL, NemoClaw can also use Ollama on the Windows host through `host.docker.internal`. + +Respond to the wizard as follows. + +1. At the `Choose [1]:` prompt, type `7` to select **Local Ollama**. +2. At the `Choose model [1]:` prompt, pick from **Ollama models** if any are already installed. If none are installed, pick a **starter model** to pull and load now, or pick **Other...** to enter any Ollama model ID. + +For setup details, including GPU recommendations and starter model choices, refer to Use a Local Inference Server (use the `nemoclaw-user-configure-inference` skill). + +**Option 8: Model Router:** + +Starts a host-side model router and routes sandbox inference through OpenShell to that router. +The router chooses from the model pool in `nemoclaw-blueprint/router/pool-config.yaml` for each request. + +Use `NVIDIA_API_KEY` for the model pool credentials. + +Respond to the wizard as follows. + +1. At the `Choose [1]:` prompt, type `8` to select **Model Router (experimental)**. +2. At the `NVIDIA_API_KEY:` prompt, paste your key if it is not already exported. +3. Review the configuration summary and continue with the sandbox build. + +For scripted setup, set: + +```console +$ NEMOCLAW_PROVIDER=routed NVIDIA_API_KEY= nemoclaw onboard --non-interactive +``` + +The router listens on the host at port `4000`. +The sandbox still calls `https://inference.local/v1`, so do not point in-sandbox tools at the host router port directly. + +**Local NIM and Local vLLM:** + +- **Local NVIDIA NIM** appears when `NEMOCLAW_EXPERIMENTAL=1` is set and the host has a NIM-capable GPU. NemoClaw pulls and manages a NIM container. +- **Local vLLM (already running)** appears whenever NemoClaw detects a vLLM server on `localhost:8000`. No flag is required for the menu entry. NemoClaw auto-detects the loaded model. +- **Local vLLM (managed install/start)** appears by default on DGX Spark and DGX Station. Generic Linux NVIDIA GPU hosts require `NEMOCLAW_EXPERIMENTAL=1` or `NEMOCLAW_PROVIDER=install-vllm`. NemoClaw pulls and starts a vLLM container on supported hosts. + +For setup, refer to Use a Local Inference Server (use the `nemoclaw-user-configure-inference` skill). + ### Review the Configuration Before the Sandbox Build After you enter the sandbox name, the wizard prints a review summary and asks for final confirmation before registering the provider, prompting for optional integrations, and building the sandbox image. @@ -176,8 +342,6 @@ Manage later If you picked a different option, the `Model` line shows that provider's model and label instead. For example, you might see `gpt-5.4 (OpenAI)`, `claude-sonnet-4-6 (Anthropic)`, `gemini-2.5-flash (Google Gemini)`, `llama3.1:8b (Local Ollama)`, `nvidia-routed (Model Router)`, or ` (Other OpenAI-compatible endpoint)`. -Load [references/quickstart-details.md](references/quickstart-details.md) for detailed steps on Respond to the Onboard Wizard. - ## Run Your First Agent Prompt You can chat with the agent from the terminal or the browser. @@ -214,7 +378,6 @@ openclaw tui - **Load [references/quickstart-hermes.md](references/quickstart-hermes.md)** when users ask for Hermes setup, NemoHermes onboarding, or running Hermes inside OpenShell. Installs NemoClaw, selects the Hermes agent, and launches a sandboxed Hermes API endpoint. - **Load [references/prerequisites.md](references/prerequisites.md)** when verifying prerequisites before installation. Lists the hardware, software, and container runtime requirements for running NemoClaw. - **Load [references/windows-preparation.md](references/windows-preparation.md)** when preparing a Windows machine for NemoClaw, enabling WSL 2, configuring Docker Desktop for Windows, or troubleshooting a Windows-specific install error. Covers Windows-only preparation steps required before the Quickstart. -- **Load [references/quickstart-details.md](references/quickstart-details.md)** when you need detailed steps for Respond to the Onboard Wizard. ## Related Skills diff --git a/.agents/skills/nemoclaw-user-get-started/references/quickstart-details.md b/.agents/skills/nemoclaw-user-get-started/references/quickstart-details.md deleted file mode 100644 index a688104748..0000000000 --- a/.agents/skills/nemoclaw-user-get-started/references/quickstart-details.md +++ /dev/null @@ -1,169 +0,0 @@ - - -# NemoClaw Quickstart with OpenClaw: Details - -## Respond to the Onboard Wizard - -After the installer launches `nemoclaw onboard`, the wizard runs preflight checks, starts or reuses the OpenShell gateway, asks for an inference provider and model, collects any required credential, then asks for the sandbox name. -It prints a review summary before it registers the provider with OpenShell. -After you confirm, NemoClaw registers inference, prompts for optional web search and messaging channels, builds and starts the sandbox, sets up OpenClaw, then applies the selected network policy tier and presets. -At any prompt, press Enter to accept the default shown in `[brackets]`, type `back` to return to the previous prompt, or type `exit` to quit. -If existing sandbox sessions are running, the installer warns before onboarding because the setup can rebuild or upgrade sandboxes after the new sandbox launches. - -The inference provider prompt presents a numbered list. - -```text - 1) NVIDIA Endpoints - 2) OpenAI - 3) Other OpenAI-compatible endpoint - 4) Anthropic - 5) Other Anthropic-compatible endpoint - 6) Google Gemini - 7) Local Ollama (localhost:11434) - 8) Model Router (experimental) - Choose [1]: -``` - -Pick the option that matches where you want inference traffic to go, then expand the matching helper below for the follow-up prompts and the API key environment variable to set. -For the full list of providers and validation behavior, refer to Inference Options (use the `nemoclaw-user-configure-inference` skill). -Local Ollama appears when NemoClaw detects a usable local Ollama path or can offer an install or start action for your platform. -The Model Router option appears when the blueprint router profile is enabled. - -**Tip:** - -Export the API key before launching the installer so the wizard does not have to ask for it. -For example, run `export NVIDIA_API_KEY=` before `curl ... | bash`. -If you entered a key incorrectly, refer to Reset a Stored Credential (use the `nemoclaw-user-manage-sandboxes` skill) to clear and re-enter it. - -**Option 1: NVIDIA Endpoints:** - -Routes inference to models hosted on [build.nvidia.com](https://build.nvidia.com). - -Use `NVIDIA_API_KEY` for the API key. Get one from the [NVIDIA build API keys page](https://build.nvidia.com/settings/api-keys). - -Respond to the wizard as follows. - -1. At the `Choose [1]:` prompt, press Enter (or type `1`) to select **NVIDIA Endpoints**. -2. At the `NVIDIA_API_KEY:` prompt, paste your key if it is not already exported. -3. At the `Choose model [1]:` prompt, pick a curated model from the list (for example, `Nemotron 3 Super 120B`, `GLM-5`, `MiniMax M2.7`, `GPT-OSS 120B`, or `DeepSeek V4 Pro`), or pick `Other...` to enter any model ID from the [NVIDIA Endpoints catalog](https://build.nvidia.com). - -NemoClaw validates the model against the catalog API before creating the sandbox. - -**Tip:** - -Use this option for Nemotron and other models hosted on `build.nvidia.com`. If you run NVIDIA Nemotron from a self-hosted NIM, an enterprise gateway, or any other endpoint, choose **Option 3** instead, since all Nemotron models expose OpenAI-compatible APIs. - -**Option 2: OpenAI:** - -Routes inference to the OpenAI API at `https://api.openai.com/v1`. - -Use `OPENAI_API_KEY` for the API key. Get one from the [OpenAI API keys page](https://platform.openai.com/api-keys). - -Respond to the wizard as follows. - -1. At the `Choose [1]:` prompt, type `2` to select **OpenAI**. -2. At the `OPENAI_API_KEY:` prompt, paste your key if it is not already exported. -3. At the `Choose model [1]:` prompt, pick a curated model (for example, `gpt-5.4`, `gpt-5.4-mini`, `gpt-5.4-nano`, or `gpt-5.4-pro-2026-03-05`), or pick **Other...** to enter any OpenAI model ID. - -**Option 3: Other OpenAI-Compatible Endpoint:** - -Routes inference to any server that implements `/v1/chat/completions`, including OpenRouter, LocalAI, llama.cpp, vLLM behind a proxy, and any compatible gateway. - -Use `COMPATIBLE_API_KEY` for the API key. Set it to whatever credential your endpoint expects. If your endpoint does not require auth, use any non-empty placeholder. - -Respond to the wizard as follows. - -1. At the `Choose [1]:` prompt, type `3` to select **Other OpenAI-compatible endpoint**. -2. At the `OpenAI-compatible base URL` prompt, enter the provider's base URL. Find the exact value in your provider's API documentation. NemoClaw appends `/v1` automatically, so leave that suffix off. -3. At the `COMPATIBLE_API_KEY:` prompt, paste your key if it is not already exported. -4. At the `Other OpenAI-compatible endpoint model []:` prompt, enter the model ID exactly as it appears in your provider's model catalog. - -For example, when you use NVIDIA's OpenAI-compatible inference endpoint, enter `https://inference-api.nvidia.com` as the base URL and the model ID your endpoint exposes, such as `openai/openai/gpt-5.5`. - -NemoClaw sends a real inference request to validate the endpoint and model. -If the endpoint does not return the streaming events OpenClaw needs from the Responses API, NemoClaw falls back to the chat completions API and configures OpenClaw to use `openai-completions`. - -**Tip:** - -NVIDIA Nemotron models expose OpenAI-compatible APIs, so this option is the right choice for any Nemotron deployment that does not live on `build.nvidia.com`. Common examples include a self-hosted NIM container, an enterprise NVIDIA AI Enterprise gateway, or a vLLM/SGLang server running Nemotron weights. Point the base URL at your endpoint and enter the Nemotron model ID exactly as your server reports it. - -**Option 4: Anthropic:** - -Routes inference to the Anthropic Messages API at `https://api.anthropic.com`. - -Use `ANTHROPIC_API_KEY` for the API key. Get one from the [Anthropic console keys page](https://console.anthropic.com/settings/keys). - -Respond to the wizard as follows. - -1. At the `Choose [1]:` prompt, type `4` to select **Anthropic**. -2. At the `ANTHROPIC_API_KEY:` prompt, paste your key if it is not already exported. -3. At the `Choose model [1]:` prompt, pick a curated model (for example, `claude-sonnet-4-6`, `claude-haiku-4-5`, or `claude-opus-4-6`), or pick **Other...** to enter any Claude model ID. - -**Option 5: Other Anthropic-Compatible Endpoint:** - -Routes inference to any server that implements the Anthropic Messages API at `/v1/messages`, including Claude proxies, Bedrock-compatible gateways, and self-hosted Anthropic-compatible servers. - -Use `COMPATIBLE_ANTHROPIC_API_KEY` for the API key. Set it to whatever credential your endpoint expects. - -Respond to the wizard as follows. - -1. At the `Choose [1]:` prompt, type `5` to select **Other Anthropic-compatible endpoint**. -2. At the `Anthropic-compatible base URL` prompt, enter the proxy or gateway's base URL from its documentation. -3. At the `COMPATIBLE_ANTHROPIC_API_KEY:` prompt, paste your key if it is not already exported. -4. At the `Other Anthropic-compatible endpoint model []:` prompt, enter the model ID exactly as it appears in your gateway's model catalog. - -**Option 6: Google Gemini:** - -Routes inference to Google's OpenAI-compatible Gemini endpoint at `https://generativelanguage.googleapis.com/v1beta/openai/`. - -Use `GEMINI_API_KEY` for the API key. Get one from [Google AI Studio API keys](https://aistudio.google.com/app/apikey). - -Respond to the wizard as follows. - -1. At the `Choose [1]:` prompt, type `6` to select **Google Gemini**. -2. At the `GEMINI_API_KEY:` prompt, paste your key if it is not already exported. -3. At the `Choose model [5]:` prompt, pick a curated model (for example, `gemini-3.1-pro-preview`, `gemini-3.1-flash-lite-preview`, `gemini-3-flash-preview`, `gemini-2.5-pro`, `gemini-2.5-flash`, or `gemini-2.5-flash-lite`), or pick **Other...** to enter any Gemini model ID. - -**Option 7: Local Ollama:** - -Routes inference to a local Ollama instance. Depending on your platform, the wizard can use an existing daemon, start an installed daemon, or offer an install action. - -No API key is required. On non-WSL hosts, NemoClaw generates a token and starts an authenticated proxy so containers can reach Ollama without exposing the daemon directly to your network. -On WSL, NemoClaw can also use Ollama on the Windows host through `host.docker.internal`. - -Respond to the wizard as follows. - -1. At the `Choose [1]:` prompt, type `7` to select **Local Ollama**. -2. At the `Choose model [1]:` prompt, pick from **Ollama models** if any are already installed. If none are installed, pick a **starter model** to pull and load now, or pick **Other...** to enter any Ollama model ID. - -For setup details, including GPU recommendations and starter model choices, refer to Use a Local Inference Server (use the `nemoclaw-user-configure-inference` skill). - -**Option 8: Model Router:** - -Starts a host-side model router and routes sandbox inference through OpenShell to that router. -The router chooses from the model pool in `nemoclaw-blueprint/router/pool-config.yaml` for each request. - -Use `NVIDIA_API_KEY` for the model pool credentials. - -Respond to the wizard as follows. - -1. At the `Choose [1]:` prompt, type `8` to select **Model Router (experimental)**. -2. At the `NVIDIA_API_KEY:` prompt, paste your key if it is not already exported. -3. Review the configuration summary and continue with the sandbox build. - -For scripted setup, set: - -```console -$ NEMOCLAW_PROVIDER=routed NVIDIA_API_KEY= nemoclaw onboard --non-interactive -``` - -The router listens on the host at port `4000`. -The sandbox still calls `https://inference.local/v1`, so do not point in-sandbox tools at the host router port directly. - -**Local NIM and Local vLLM:** - -- **Local NVIDIA NIM** appears when `NEMOCLAW_EXPERIMENTAL=1` is set and the host has a NIM-capable GPU. NemoClaw pulls and manages a NIM container. -- **Local vLLM (already running)** appears whenever NemoClaw detects a vLLM server on `localhost:8000`. No flag is required for the menu entry. NemoClaw auto-detects the loaded model. -- **Local vLLM (managed install/start)** appears by default on DGX Spark and DGX Station. Generic Linux NVIDIA GPU hosts require `NEMOCLAW_EXPERIMENTAL=1` or `NEMOCLAW_PROVIDER=install-vllm`. NemoClaw pulls and starts a vLLM container on supported hosts. - -For setup, refer to Use a Local Inference Server (use the `nemoclaw-user-configure-inference` skill). diff --git a/.agents/skills/nemoclaw-user-manage-policy/SKILL.md b/.agents/skills/nemoclaw-user-manage-policy/SKILL.md index 298c672588..9498bc8fea 100644 --- a/.agents/skills/nemoclaw-user-manage-policy/SKILL.md +++ b/.agents/skills/nemoclaw-user-manage-policy/SKILL.md @@ -1,6 +1,6 @@ --- name: "nemoclaw-user-manage-policy" -description: "Adds, removes, or modifies allowed endpoints in the sandbox policy. Use when customizing network policy, changing egress rules, or configuring sandbox endpoint access. Trigger keywords - customize nemoclaw network policy, sandbox egress policy configuration, nemoclaw integration policy examples, post-install policy setup, openshell approval workflow, policy preset, nemoclaw approve network requests, sandbox egress approval tui." +description: "Reviews and approves blocked agent network requests in the TUI. Use when approving or denying sandbox egress requests, managing blocked network calls, or using the approval TUI. Trigger keywords - nemoclaw approve network requests, sandbox egress approval tui, customize nemoclaw network policy, sandbox egress policy configuration, nemoclaw integration policy examples, post-install policy setup, openshell approval workflow, policy preset." license: "Apache-2.0" --- @@ -279,13 +279,20 @@ Fix the failing file and re-run the command to continue. Custom preset hosts bypass NemoClaw's review process and can widen sandbox egress to arbitrary destinations. Review every host in a custom preset before applying it, especially when the file originates outside your team. -Load [references/customize-network-policy-details.md](references/customize-network-policy-details.md) for detailed steps on Remove a Custom Preset. +### Remove a Custom Preset + +Custom presets applied with `--from-file` or `--from-dir` are recorded in the NemoClaw sandbox registry alongside their full YAML content, so they can be removed by name — the original file does not need to be kept on disk: + +```console +$ nemoclaw my-assistant policy-remove my-internal-api --yes +``` + +`policy-remove` accepts both built-in and custom preset names. Run `nemoclaw policy-list` to see every preset currently applied to the sandbox. ## References - **[references/integration-policy-examples.md](references/integration-policy-examples.md)** — Guides users through common post-install integration policy setup for maintained NemoClaw policy presets, including Outlook, messaging channels, GitHub, Jira, Brave Search, package managers, Hugging Face, local inference, and OpenShell approval workflows. - **Load [references/approve-network-requests.md](references/approve-network-requests.md)** when approving or denying sandbox egress requests, managing blocked network calls, or using the approval TUI. Reviews and approves blocked agent network requests in the TUI. -- **Load [references/customize-network-policy-details.md](references/customize-network-policy-details.md)** when you need detailed steps for Remove a Custom Preset. ## Related Skills diff --git a/.agents/skills/nemoclaw-user-manage-policy/references/customize-network-policy-details.md b/.agents/skills/nemoclaw-user-manage-policy/references/customize-network-policy-details.md deleted file mode 100644 index 224829b964..0000000000 --- a/.agents/skills/nemoclaw-user-manage-policy/references/customize-network-policy-details.md +++ /dev/null @@ -1,13 +0,0 @@ - - -# Customize the Sandbox Network Policy: Details - -## Remove a Custom Preset - -Custom presets applied with `--from-file` or `--from-dir` are recorded in the NemoClaw sandbox registry alongside their full YAML content, so they can be removed by name — the original file does not need to be kept on disk: - -```console -$ nemoclaw my-assistant policy-remove my-internal-api --yes -``` - -`policy-remove` accepts both built-in and custom preset names. Run `nemoclaw policy-list` to see every preset currently applied to the sandbox. diff --git a/.agents/skills/nemoclaw-user-manage-sandboxes/SKILL.md b/.agents/skills/nemoclaw-user-manage-sandboxes/SKILL.md index b5618052c5..1dc4bae284 100644 --- a/.agents/skills/nemoclaw-user-manage-sandboxes/SKILL.md +++ b/.agents/skills/nemoclaw-user-manage-sandboxes/SKILL.md @@ -1,6 +1,6 @@ --- name: "nemoclaw-user-manage-sandboxes" -description: "Explains operational tasks after the quickstart: listing sandboxes, status and health checks, logs, diagnostics, port forwards, multiple sandboxes, credential reset, rebuilds, network presets, upgrades, and uninstall. Trigger keywords - manage nemoclaw sandboxes, nemoclaw status, nemoclaw list, nemoclaw dashboard port, nemoclaw rebuild, nemoclaw upgrade sandboxes, nemoclaw uninstall, sandbox mutability, sandbox runtime configuration, sandbox rebuild, nemoclaw backup, nemoclaw restore, workspace backup, openshell sandbox download upload, nemoclaw messaging channels, nemoclaw telegram, nemoclaw discord, nemoclaw slack, nemoclaw wechat, nemoclaw whatsapp, openshell channel messaging, nemoclaw workspace files, soul.md, user.md, identity.md, agents.md, sandbox persistence." +description: "Backs up and restores OpenClaw workspace files before destructive operations such as sandbox rebuilds. Use when downloading workspace files from a sandbox, uploading restored files into a new sandbox, or preserving sandbox state across rebuilds. Trigger keywords - nemoclaw backup, nemoclaw restore, workspace backup, openshell sandbox download upload, manage nemoclaw sandboxes, nemoclaw status, nemoclaw list, nemoclaw dashboard port, nemoclaw rebuild, nemoclaw upgrade sandboxes, nemoclaw uninstall, nemoclaw messaging channels, nemoclaw telegram, nemoclaw discord, nemoclaw slack, nemoclaw wechat, nemoclaw whatsapp, openshell channel messaging, sandbox mutability, sandbox runtime configuration, sandbox rebuild, nemoclaw workspace files, soul.md, user.md, identity.md, agents.md, sandbox persistence." license: "Apache-2.0" --- @@ -218,7 +218,28 @@ This restores saved state directories only; it does not downgrade the sandbox im $ nemoclaw snapshot restore pre-upgrade ``` -Load [references/lifecycle-details.md](references/lifecycle-details.md) for detailed steps on What Changes During a Rebuild. +### What Changes During a Rebuild + +Each rebuild destroys the existing container and creates a new one. +NemoClaw protects your data through the same backup-and-restore flow as `nemoclaw rebuild` (use the `nemoclaw-user-reference` skill): + +- NemoClaw preserves manifest-defined workspace state. Before deleting the old container, NemoClaw snapshots the state directories and durable state files defined in the agent manifest, typically `/sandbox/.openclaw/workspace/`; for Hermes this also includes `SOUL.md` and the SQLite database behind `.hermes/state.db`. Stored credentials (`~/.nemoclaw/credentials.json`) and registered policy presets live on the host and are re-applied to the new sandbox automatically. +- NemoClaw does not preserve runtime changes outside the workspace state directories. This includes packages installed inside the running container with `apt` or `pip`, files in non-workspace paths, and in-memory or process state. If you have customized the running container at runtime, capture that as `Dockerfile` changes for `nemoclaw onboard --from` or a manual `openshell sandbox download` before the rebuild starts. + +Aborts before the destroy step are non-destructive. +The flow refuses to proceed past preflight if a credential is missing or past backup if required manifest-defined state cannot be copied, so a failed run leaves the original sandbox intact and ready to retry. +When a backup command reports partial archive output, NemoClaw keeps the usable entries and reports only the manifest-defined paths that could not be archived. + +See [Backup and Restore](references/backup-restore.md) for the full list of state-preservation guarantees, snapshot retention, and instructions for manual backups when the auto-flow is not enough. + +**If the rebuild aborts with `Missing credential: `:** + +The rebuild preflight reads the provider credential recorded by your last `nemoclaw onboard` session. +If you have switched providers since onboarding, for example from a remote API to a local Ollama setup, the preflight may still reference the old key and fail before any destroy step runs. + +To recover, re-run `nemoclaw onboard` and select your current provider. +This refreshes the session metadata. +Your existing container keeps serving traffic until the new image is ready. ## Uninstall @@ -256,7 +277,6 @@ For a full comparison of the two forms, including what they fetch, what they tru - **Load [references/backup-restore.md](references/backup-restore.md)** when downloading workspace files from a sandbox, uploading restored files into a new sandbox, or preserving sandbox state across rebuilds. Backs up and restores OpenClaw workspace files before destructive operations such as sandbox rebuilds. - **Load [references/messaging-channels.md](references/messaging-channels.md)** when setting up messaging channels, chat interfaces, or integrations without relying on nemoclaw tunnel start for bridges. Explains how Telegram, Discord, Slack, WeChat, and WhatsApp reach sandboxed OpenClaw and Hermes agents through OpenShell-managed processes and NemoClaw channel commands. - **Load [references/workspace-files.md](references/workspace-files.md)** when users ask about `SOUL.md`, `USER.md`, `IDENTITY.md`, `AGENTS.md`, or other workspace files, or when preparing to back up or restore workspace state. Explains what workspace personality and configuration files are, where they live, and how they persist across sandbox restarts. -- **Load [references/lifecycle-details.md](references/lifecycle-details.md)** when you need detailed steps for What Changes During a Rebuild. ## Related Skills diff --git a/.agents/skills/nemoclaw-user-manage-sandboxes/references/lifecycle-details.md b/.agents/skills/nemoclaw-user-manage-sandboxes/references/lifecycle-details.md deleted file mode 100644 index 38d055b82f..0000000000 --- a/.agents/skills/nemoclaw-user-manage-sandboxes/references/lifecycle-details.md +++ /dev/null @@ -1,26 +0,0 @@ - - -# Manage Sandbox Lifecycle: Details - -## What Changes During a Rebuild - -Each rebuild destroys the existing container and creates a new one. -NemoClaw protects your data through the same backup-and-restore flow as `nemoclaw rebuild` (use the `nemoclaw-user-reference` skill): - -- NemoClaw preserves manifest-defined workspace state. Before deleting the old container, NemoClaw snapshots the state directories and durable state files defined in the agent manifest, typically `/sandbox/.openclaw/workspace/`; for Hermes this also includes `SOUL.md` and the SQLite database behind `.hermes/state.db`. Stored credentials (`~/.nemoclaw/credentials.json`) and registered policy presets live on the host and are re-applied to the new sandbox automatically. -- NemoClaw does not preserve runtime changes outside the workspace state directories. This includes packages installed inside the running container with `apt` or `pip`, files in non-workspace paths, and in-memory or process state. If you have customized the running container at runtime, capture that as `Dockerfile` changes for `nemoclaw onboard --from` or a manual `openshell sandbox download` before the rebuild starts. - -Aborts before the destroy step are non-destructive. -The flow refuses to proceed past preflight if a credential is missing or past backup if required manifest-defined state cannot be copied, so a failed run leaves the original sandbox intact and ready to retry. -When a backup command reports partial archive output, NemoClaw keeps the usable entries and reports only the manifest-defined paths that could not be archived. - -See [Backup and Restore](backup-restore.md) for the full list of state-preservation guarantees, snapshot retention, and instructions for manual backups when the auto-flow is not enough. - -**If the rebuild aborts with `Missing credential: `:** - -The rebuild preflight reads the provider credential recorded by your last `nemoclaw onboard` session. -If you have switched providers since onboarding, for example from a remote API to a local Ollama setup, the preflight may still reference the old key and fail before any destroy step runs. - -To recover, re-run `nemoclaw onboard` and select your current provider. -This refreshes the session metadata. -Your existing container keeps serving traffic until the new image is ready. diff --git a/.agents/skills/nemoclaw-user-overview/SKILL.md b/.agents/skills/nemoclaw-user-overview/SKILL.md index 41250f65ec..11e7ba010a 100644 --- a/.agents/skills/nemoclaw-user-overview/SKILL.md +++ b/.agents/skills/nemoclaw-user-overview/SKILL.md @@ -9,9 +9,99 @@ license: "Apache-2.0" # Ecosystem +NemoClaw provides onboarding, lifecycle management, and OpenClaw operations within OpenShell containers. + +This page describes how the ecosystem is formed across projects, where NemoClaw sits relative to [OpenShell](https://github.com/NVIDIA/OpenShell) and [OpenClaw](https://openclaw.ai), and how to choose between NemoClaw and OpenShell. + +## How the Stack Fits Together + +There are three pieces that are put together in a NemoClaw deployment: OpenClaw, OpenShell, and NemoClaw, each with a distinct scope. +The following diagram shows how they fit together. + +```mermaid +flowchart TB + NC["🦞 NVIDIA NemoClaw
CLI, plugin, blueprint"] + OS["🐚 NVIDIA OpenShell
Gateway, policy, inference routing"] + OC["🦞 OpenClaw
Assistant in sandbox"] + + NC -->|orchestrates| OS + OS -->|isolates and runs| OC + + classDef nv fill:#76b900,stroke:#333,color:#fff + classDef nvLight fill:#e6f2cc,stroke:#76b900,color:#1a1a1a + classDef nvDark fill:#333,stroke:#76b900,color:#fff + + class NC nv + class OS nv + class OC nvDark + + linkStyle 0 stroke:#76b900,stroke-width:2px + linkStyle 1 stroke:#76b900,stroke-width:2px +``` + +NemoClaw sits above OpenShell in the operator workflow. +It drives OpenShell APIs and CLI to create and configure the sandbox that runs OpenClaw. +Models and endpoints sit behind OpenShell's inference routing. +NemoClaw onboarding wires provider choice into that routing. + +The following table shows the scope of each component in the stack. + +| Project | Scope | +|---------|--------| +| [OpenClaw](https://openclaw.ai) | The assistant: runtime, tools, memory, and behavior inside the container. It does not define the sandbox or the host gateway. | +| [OpenShell](https://github.com/NVIDIA/OpenShell) | The execution environment: sandbox lifecycle, network, filesystem, and process policy, inference routing, and the operator-facing `openshell` CLI for those primitives. | +| NemoClaw | The NVIDIA reference stack that implements the definition above on the host: `nemoclaw` CLI and plugin, versioned blueprint, channel messaging configured for OpenShell-managed delivery, and state migration helpers so OpenClaw runs inside OpenShell in a documented, repeatable way. | + +## NemoClaw Path versus OpenShell Path + +Both paths assume OpenShell can sandbox a workload. +The difference is who owns the integration work. + +| Path | What it means | +|------|---------------| +| **NemoClaw path** | You adopt the reference stack. NemoClaw's blueprint encodes a hardened image, default policies, and orchestration so `nemoclaw onboard` can stand up a known-good OpenClaw-on-OpenShell setup with less custom glue. | +| **OpenShell path** | You use OpenShell as the platform and supply your own container, install steps for OpenClaw, policy YAML, provider setup, and any host bridges. OpenShell stays the sandbox and policy engine; nothing requires NemoClaw's blueprint or CLI. | + +## What NemoClaw Adds Beyond the OpenShell Community Sandbox + +OpenShell ships a community sandbox for OpenClaw. +Running `openshell sandbox create --from openclaw` pulls that package, builds the image, applies the bundled policy, and starts a working sandbox. +This is a valid path, and it produces a running OpenClaw environment with OpenShell isolation. + +NemoClaw builds on that foundation with additional security hardening, automation, and lifecycle tooling. +The following table compares the two paths. + +| Capability | `openshell sandbox create --from openclaw` | `nemoclaw onboard` | +|---|---|---| +| Sandbox isolation | Yes. OpenShell applies seccomp filters, Landlock filesystem restrictions, privilege dropping, network namespace isolation, and no-new-privileges enforcement. The community sandbox bundles its own policy tailored for OpenClaw. | Yes. NemoClaw applies these through the blueprint and layers a more restrictive policy on top (see rows below). | +| Credential handling | OpenShell's provider system replaces real credentials with placeholder tokens in the sandbox environment. The L7 proxy resolves placeholders to real values at egress. You create providers manually with `openshell provider create`. | NemoClaw creates OpenShell providers automatically during onboarding. It also filters sensitive host environment variables (provider API keys, `DISCORD_BOT_TOKEN`, `SLACK_BOT_TOKEN`, `TELEGRAM_BOT_TOKEN`) from the sandbox creation command to prevent accidental leakage through build args. | +| Image hardening | The community image includes standard system tools for general-purpose use. | NemoClaw strips build toolchains (`gcc`, `g++`, `make`) and network probes (`netcat`) from the runtime image to reduce attack surface. | +| Filesystem policy | The community sandbox bundles a policy for OpenClaw. | NemoClaw defines a targeted read-only and read-write layout. System paths (`/usr`, `/lib`, `/etc`) are read-only. The agent's home directory (`/sandbox`) and config directory (`/sandbox/.openclaw`) are writable by default so the agent can manage config, install skills, and write to standard paths natively. | +| Inference setup | The community sandbox includes an `openclaw-start` script that runs OpenClaw's onboarding wizard inside the sandbox. You can also create providers and configure OpenShell inference routing manually from the host. | NemoClaw's onboarding wizard validates your credential from the host, lets you select a provider (NVIDIA Endpoints, OpenAI, Anthropic, Google Gemini, Ollama, and compatible endpoints), and configures OpenShell's inference routing automatically. Credentials stay on the host and are delivered through OpenShell's provider system. | +| Channel messaging | OpenShell provides the credential provider system and L7 proxy that delivers channel tokens securely (including path-based resolution for Telegram's `/bot/` URL pattern). You create providers and configure OpenClaw's channel settings manually. | NemoClaw automates channel setup during onboarding: it collects bot tokens, registers them as OpenShell providers, and bakes OpenClaw channel config with placeholder tokens that OpenShell's proxy resolves at egress. No separate bridge process runs on the host. | +| Blueprint versioning | No blueprint. The community sandbox uses whatever image version is currently published. | NemoClaw downloads the blueprint artifact, checks version compatibility, and verifies its digest before applying. Running `nemoclaw onboard` on different machines produces the same sandbox. | +| State migration | Not included. | NemoClaw migrates agent state across machines with credential stripping and integrity verification. | +| Process count limits | OpenShell applies seccomp and privilege dropping. You set process count limits manually with `--ulimit` or orchestrator config. | NemoClaw applies `ulimit -u 512` in the container entrypoint to cap the process count and mitigate fork-bomb attacks, on top of OpenShell's seccomp and privilege dropping. | + +## When to Use Which + +Use the following table to decide when to use NemoClaw versus OpenShell. + +| Situation | Prefer | +|-----------|--------| +| You want OpenClaw with minimal assembly, NVIDIA defaults, and the documented install and onboard flow. | NemoClaw | +| You need maximum flexibility for custom images, a layout that does not match the NemoClaw blueprint, or a workload outside this reference stack. | OpenShell with your own integration | +| You are standardizing on the NVIDIA reference for always-on assistants with policy and inference routing. | NemoClaw | +| You are building internal platform abstractions where the NemoClaw CLI or blueprint is not the right fit. | OpenShell (and your orchestration) | + ## References -- **Load [references/ecosystem.md](references/ecosystem.md)** when users ask about the relationship between OpenClaw, OpenShell, and NemoClaw, or when to use NemoClaw versus OpenShell. Explains how OpenClaw, OpenShell, and NemoClaw form the ecosystem, NemoClaw's position in the stack, what NemoClaw adds beyond the community sandbox, and when to prefer NemoClaw versus integrating OpenShell and OpenClaw directly. - **Load [references/how-it-works.md](references/how-it-works.md)** for sandbox lifecycle and architecture mechanics; not for product definition (Overview) or multi-project placement (Ecosystem). Describes how NemoClaw works internally: CLI, plugin, blueprint runner, OpenShell orchestration, inference routing, and protection layers. - **Load [references/overview.md](references/overview.md)** when users ask what NemoClaw is or what the project provides. For ecosystem placement or OpenShell-only paths, use the Ecosystem page; for internal mechanics, use How It Works. Explains what NemoClaw covers: onboarding, lifecycle management, and OpenClaw operations within OpenShell containers, plus capabilities and why it exists. - **Load [references/release-notes.md](references/release-notes.md)** when users ask about recent changes, the release cadence, or where to track versioned assets on GitHub. Includes the NemoClaw release notes. + +## Related Skills + +- [Overview](references/overview.md) contains what NemoClaw is, capabilities, benefits, and use cases. +- [How It Works](references/how-it-works.md) describes how NemoClaw runs, plugin, blueprint, sandbox creation, routing, protection layers. +- `nemoclaw-user-reference` — Architecture (use the `nemoclaw-user-reference` skill) shows the repository structure and technical diagrams diff --git a/.agents/skills/nemoclaw-user-overview/references/ecosystem.md b/.agents/skills/nemoclaw-user-overview/references/ecosystem.md deleted file mode 100644 index b1d97c4a22..0000000000 --- a/.agents/skills/nemoclaw-user-overview/references/ecosystem.md +++ /dev/null @@ -1,94 +0,0 @@ - - -# Ecosystem - -NemoClaw provides onboarding, lifecycle management, and OpenClaw operations within OpenShell containers. - -This page describes how the ecosystem is formed across projects, where NemoClaw sits relative to [OpenShell](https://github.com/NVIDIA/OpenShell) and [OpenClaw](https://openclaw.ai), and how to choose between NemoClaw and OpenShell. - -## How the Stack Fits Together - -There are three pieces that are put together in a NemoClaw deployment: OpenClaw, OpenShell, and NemoClaw, each with a distinct scope. -The following diagram shows how they fit together. - -```mermaid -flowchart TB - NC["🦞 NVIDIA NemoClaw
CLI, plugin, blueprint"] - OS["🐚 NVIDIA OpenShell
Gateway, policy, inference routing"] - OC["🦞 OpenClaw
Assistant in sandbox"] - - NC -->|orchestrates| OS - OS -->|isolates and runs| OC - - classDef nv fill:#76b900,stroke:#333,color:#fff - classDef nvLight fill:#e6f2cc,stroke:#76b900,color:#1a1a1a - classDef nvDark fill:#333,stroke:#76b900,color:#fff - - class NC nv - class OS nv - class OC nvDark - - linkStyle 0 stroke:#76b900,stroke-width:2px - linkStyle 1 stroke:#76b900,stroke-width:2px -``` - -NemoClaw sits above OpenShell in the operator workflow. -It drives OpenShell APIs and CLI to create and configure the sandbox that runs OpenClaw. -Models and endpoints sit behind OpenShell's inference routing. -NemoClaw onboarding wires provider choice into that routing. - -The following table shows the scope of each component in the stack. - -| Project | Scope | -|---------|--------| -| [OpenClaw](https://openclaw.ai) | The assistant: runtime, tools, memory, and behavior inside the container. It does not define the sandbox or the host gateway. | -| [OpenShell](https://github.com/NVIDIA/OpenShell) | The execution environment: sandbox lifecycle, network, filesystem, and process policy, inference routing, and the operator-facing `openshell` CLI for those primitives. | -| NemoClaw | The NVIDIA reference stack that implements the definition above on the host: `nemoclaw` CLI and plugin, versioned blueprint, channel messaging configured for OpenShell-managed delivery, and state migration helpers so OpenClaw runs inside OpenShell in a documented, repeatable way. | - -## NemoClaw Path versus OpenShell Path - -Both paths assume OpenShell can sandbox a workload. -The difference is who owns the integration work. - -| Path | What it means | -|------|---------------| -| **NemoClaw path** | You adopt the reference stack. NemoClaw's blueprint encodes a hardened image, default policies, and orchestration so `nemoclaw onboard` can stand up a known-good OpenClaw-on-OpenShell setup with less custom glue. | -| **OpenShell path** | You use OpenShell as the platform and supply your own container, install steps for OpenClaw, policy YAML, provider setup, and any host bridges. OpenShell stays the sandbox and policy engine; nothing requires NemoClaw's blueprint or CLI. | - -## What NemoClaw Adds Beyond the OpenShell Community Sandbox - -OpenShell ships a community sandbox for OpenClaw. -Running `openshell sandbox create --from openclaw` pulls that package, builds the image, applies the bundled policy, and starts a working sandbox. -This is a valid path, and it produces a running OpenClaw environment with OpenShell isolation. - -NemoClaw builds on that foundation with additional security hardening, automation, and lifecycle tooling. -The following table compares the two paths. - -| Capability | `openshell sandbox create --from openclaw` | `nemoclaw onboard` | -|---|---|---| -| Sandbox isolation | Yes. OpenShell applies seccomp filters, Landlock filesystem restrictions, privilege dropping, network namespace isolation, and no-new-privileges enforcement. The community sandbox bundles its own policy tailored for OpenClaw. | Yes. NemoClaw applies these through the blueprint and layers a more restrictive policy on top (see rows below). | -| Credential handling | OpenShell's provider system replaces real credentials with placeholder tokens in the sandbox environment. The L7 proxy resolves placeholders to real values at egress. You create providers manually with `openshell provider create`. | NemoClaw creates OpenShell providers automatically during onboarding. It also filters sensitive host environment variables (provider API keys, `DISCORD_BOT_TOKEN`, `SLACK_BOT_TOKEN`, `TELEGRAM_BOT_TOKEN`) from the sandbox creation command to prevent accidental leakage through build args. | -| Image hardening | The community image includes standard system tools for general-purpose use. | NemoClaw strips build toolchains (`gcc`, `g++`, `make`) and network probes (`netcat`) from the runtime image to reduce attack surface. | -| Filesystem policy | The community sandbox bundles a policy for OpenClaw. | NemoClaw defines a targeted read-only and read-write layout. System paths (`/usr`, `/lib`, `/etc`) are read-only. The agent's home directory (`/sandbox`) and config directory (`/sandbox/.openclaw`) are writable by default so the agent can manage config, install skills, and write to standard paths natively. | -| Inference setup | The community sandbox includes an `openclaw-start` script that runs OpenClaw's onboarding wizard inside the sandbox. You can also create providers and configure OpenShell inference routing manually from the host. | NemoClaw's onboarding wizard validates your credential from the host, lets you select a provider (NVIDIA Endpoints, OpenAI, Anthropic, Google Gemini, Ollama, and compatible endpoints), and configures OpenShell's inference routing automatically. Credentials stay on the host and are delivered through OpenShell's provider system. | -| Channel messaging | OpenShell provides the credential provider system and L7 proxy that delivers channel tokens securely (including path-based resolution for Telegram's `/bot/` URL pattern). You create providers and configure OpenClaw's channel settings manually. | NemoClaw automates channel setup during onboarding: it collects bot tokens, registers them as OpenShell providers, and bakes OpenClaw channel config with placeholder tokens that OpenShell's proxy resolves at egress. No separate bridge process runs on the host. | -| Blueprint versioning | No blueprint. The community sandbox uses whatever image version is currently published. | NemoClaw downloads the blueprint artifact, checks version compatibility, and verifies its digest before applying. Running `nemoclaw onboard` on different machines produces the same sandbox. | -| State migration | Not included. | NemoClaw migrates agent state across machines with credential stripping and integrity verification. | -| Process count limits | OpenShell applies seccomp and privilege dropping. You set process count limits manually with `--ulimit` or orchestrator config. | NemoClaw applies `ulimit -u 512` in the container entrypoint to cap the process count and mitigate fork-bomb attacks, on top of OpenShell's seccomp and privilege dropping. | - -## When to Use Which - -Use the following table to decide when to use NemoClaw versus OpenShell. - -| Situation | Prefer | -|-----------|--------| -| You want OpenClaw with minimal assembly, NVIDIA defaults, and the documented install and onboard flow. | NemoClaw | -| You need maximum flexibility for custom images, a layout that does not match the NemoClaw blueprint, or a workload outside this reference stack. | OpenShell with your own integration | -| You are standardizing on the NVIDIA reference for always-on assistants with policy and inference routing. | NemoClaw | -| You are building internal platform abstractions where the NemoClaw CLI or blueprint is not the right fit. | OpenShell (and your orchestration) | - -## Related topics - -- [Overview](overview.md) contains what NemoClaw is, capabilities, benefits, and use cases. -- [How It Works](how-it-works.md) describes how NemoClaw runs, plugin, blueprint, sandbox creation, routing, protection layers. -- Architecture (use the `nemoclaw-user-reference` skill) shows the repository structure and technical diagrams. diff --git a/.agents/skills/nemoclaw-user-overview/references/how-it-works.md b/.agents/skills/nemoclaw-user-overview/references/how-it-works.md index b0f9f4a240..8ffb5f4ea3 100644 --- a/.agents/skills/nemoclaw-user-overview/references/how-it-works.md +++ b/.agents/skills/nemoclaw-user-overview/references/how-it-works.md @@ -98,7 +98,7 @@ For details on the baseline rules, refer to Network Policies (use the `nemoclaw- ## Next Steps -- Read [Ecosystem](ecosystem.md) for stack-level relationships and NemoClaw versus OpenShell-only paths. +- Read [Ecosystem](../SKILL.md) for stack-level relationships and NemoClaw versus OpenShell-only paths. - Follow the Quickstart (use the `nemoclaw-user-get-started` skill) to launch your first sandbox. - Refer to the Architecture (use the `nemoclaw-user-reference` skill) for the full technical structure, including file layouts and the blueprint lifecycle. - Refer to Inference Options (use the `nemoclaw-user-configure-inference` skill) for detailed provider configuration. diff --git a/.agents/skills/nemoclaw-user-overview/references/overview.md b/.agents/skills/nemoclaw-user-overview/references/overview.md index 39c387a537..e2ef08440a 100644 --- a/.agents/skills/nemoclaw-user-overview/references/overview.md +++ b/.agents/skills/nemoclaw-user-overview/references/overview.md @@ -59,7 +59,7 @@ You can use NemoClaw for various use cases including the following. Navigate to the following topics to learn more about NemoClaw and how to install and use it. - [Architecture Overview](how-it-works.md) to understand how NemoClaw works. -- [Ecosystem](ecosystem.md) to understand how OpenClaw, OpenShell, and NemoClaw relate in the wider stack, and when to use NemoClaw versus OpenShell. +- [Ecosystem](../SKILL.md) to understand how OpenClaw, OpenShell, and NemoClaw relate in the wider stack, and when to use NemoClaw versus OpenShell. - Quickstart (use the `nemoclaw-user-get-started` skill) to install NemoClaw and run your first sandboxed agent. - Agent Skills (use the `nemoclaw-user-agent-skills` skill) to load NemoClaw guidance into an AI coding assistant. - Inference Options (use the `nemoclaw-user-configure-inference` skill) to check the inference providers that NemoClaw supports and how inference routing works. diff --git a/.agents/skills/nemoclaw-user-reference/SKILL.md b/.agents/skills/nemoclaw-user-reference/SKILL.md index 020f52f5df..c5b980a93e 100644 --- a/.agents/skills/nemoclaw-user-reference/SKILL.md +++ b/.agents/skills/nemoclaw-user-reference/SKILL.md @@ -9,9 +9,265 @@ license: "Apache-2.0" # Architecture Details +NemoClaw combines a host CLI, a TypeScript plugin that runs with OpenClaw inside the sandbox, and a versioned YAML blueprint that defines the sandbox image, policies, and inference profiles applied through OpenShell. + +## System Overview + +NVIDIA OpenShell is a general-purpose agent runtime. It provides sandbox containers, a credential-storing gateway, inference proxying, and policy enforcement, but has no opinions about what runs inside. NemoClaw is an opinionated reference stack built on OpenShell that handles what goes in the sandbox and makes the setup accessible. + +```mermaid +graph LR + classDef nemoclaw fill:#76b900,stroke:#5a8f00,color:#fff,stroke-width:2px,font-weight:bold + classDef openshell fill:#1a1a1a,stroke:#1a1a1a,color:#fff,stroke-width:2px,font-weight:bold + classDef sandbox fill:#444,stroke:#76b900,color:#fff,stroke-width:2px,font-weight:bold + classDef agent fill:#f5f5f5,stroke:#e0e0e0,color:#1a1a1a,stroke-width:1px + classDef external fill:#f5f5f5,stroke:#e0e0e0,color:#1a1a1a,stroke-width:1px + classDef user fill:#fff,stroke:#76b900,color:#1a1a1a,stroke-width:2px,font-weight:bold + + USER(["👤 User"]):::user + + subgraph EXTERNAL["External Services"] + INFERENCE["Inference Provider
NVIDIA Endpoints · OpenAI
Anthropic · Ollama · vLLM · Model Router
"]:::external + MSGAPI["Messaging Platforms
Telegram · Discord · Slack"]:::external + INTERNET["Internet
PyPI · npm · GitHub · APIs"]:::external + end + + subgraph HOST["Host Machine"] + + subgraph NEMOCLAW["NemoClaw"] + direction TB + NCLI["CLI + Onboarding
Guided setup · provider selection
credential validation · deploy
"]:::nemoclaw + BP["Blueprint
Hardened Dockerfile
Network policies · Presets
Security configuration
"]:::nemoclaw + MIGRATE["State Management
Migration snapshots
Credential stripping
Integrity verification
"]:::nemoclaw + end + + subgraph OPENSHELL["OpenShell"] + direction TB + GW["Gateway
Credential store
Inference proxy
Policy engine
Device auth
"]:::openshell + OSCLI["openshell CLI
provider · sandbox
gateway · policy
"]:::openshell + CHMSG["Channel messaging
OpenShell-managed
Telegram · Discord · Slack
"]:::openshell + + subgraph SANDBOX["Sandbox Container 🔒"] + direction TB + AGENT["Agent
OpenClaw or any
compatible agent
"]:::agent + PLUG["NemoClaw Plugin
Extends agent with
managed configuration
"]:::sandbox + end + end + end + + USER -->|"nemoclaw onboard
nemoclaw connect"| NCLI + USER -->|"Chat messages"| MSGAPI + + NCLI -->|"Orchestrates"| OSCLI + BP -->|"Defines sandbox
shape + policies"| SANDBOX + MIGRATE -->|"Safe state
transfer"| SANDBOX + + AGENT -->|"Inference requests
no credentials"| GW + GW -->|"Proxied with
credential injected"| INFERENCE + + MSGAPI -->|"Platform APIs"| CHMSG + CHMSG -->|"Deliver to agent"| AGENT + + AGENT -.->|"Policy-gated"| INTERNET + GW -.->|"Enforced by
gateway"| INTERNET +``` + +## Deployment Topology + +The logical diagram above shows how components relate. +This section shows what actually runs where on the host. +NemoClaw's default Docker-driver topology does not place the sandbox in an embedded k3s cluster. +On Linux and Apple Silicon macOS, NemoClaw starts the OpenShell Docker-driver gateway and creates the sandbox as a Docker container. +The gateway normally runs as a host process; Linux hosts that need the gateway compatibility patch may run the same gateway binary inside a small container. +In both Docker-driver modes, the sandbox is a Docker container, not a Kubernetes pod. +Legacy non-Docker-driver installs still use the k3s-based gateway path; the diagram below shows the standard Docker-driver topology. + +```mermaid +graph TB + classDef host fill:#fff,stroke:#76b900,stroke-width:2px,color:#1a1a1a,font-weight:bold + classDef cli fill:#76b900,stroke:#5a8f00,color:#fff,stroke-width:2px,font-weight:bold + classDef docker fill:#2496ed,stroke:#1577c2,color:#fff,stroke-width:2px,font-weight:bold + classDef gateway fill:#1a1a1a,stroke:#1a1a1a,color:#fff,stroke-width:2px,font-weight:bold + classDef sandbox fill:#444,stroke:#76b900,color:#fff,stroke-width:2px + classDef external fill:#f5f5f5,stroke:#e0e0e0,color:#1a1a1a,stroke-width:1px + + subgraph HOST["Host machine · Linux / Apple Silicon macOS / DGX Spark / DGX Station"] + direction TB + CLI["nemoclaw CLI
bin/nemoclaw.js → dist/
onboard · connect · status · logs
"]:::cli + GW["OpenShell gateway
host process by default
credential store · lifecycle · L7 proxy
"]:::gateway + + subgraph DOCKER["Docker daemon"] + direction TB + SANDBOX["Sandbox container 🔒
Landlock + seccomp + netns
OpenClaw agent + NemoClaw plugin
"]:::sandbox + end + end + + INFER["Inference provider
NVIDIA Endpoints · OpenAI
Anthropic · Ollama · vLLM · Model Router
"]:::external + + CLI -->|"openshell CLI
(orchestrates)"| GW + GW -->|"creates/recreates
Docker-driver sandbox"| SANDBOX + SANDBOX -->|"inference requests
placeholder credentials"| GW + GW -->|"egress with real credentials
injected at the L7 proxy"| INFER + + class HOST host + class DOCKER docker + class GW gateway + class SANDBOX sandbox +``` + +Layering from top to bottom: + +| Layer | Runs as | Role | +|---|---|---| +| Host CLI | Host process (`nemoclaw` on Node.js) | Orchestrates OpenShell via `openshell` CLI calls. | +| OpenShell gateway | Host process by default; optional Linux compatibility container when the gateway binary needs a newer host ABI | Hosts the credential store, owns sandbox lifecycle coordination, and provides the L7 proxy. | +| Docker daemon | Host service | Runs the Docker-driver sandbox container and, on affected Linux hosts, the optional gateway compatibility container. | +| Sandbox container | Docker container | Runs the OpenClaw agent and the NemoClaw plugin under Landlock + seccomp + netns. | +| OpenShell L7 proxy | Gateway process | Intercepts agent egress and rewrites `Authorization` headers (Bearer/Bot) and URL-path segments to inject the real credential at the network boundary. | + +NemoClaw never gives the sandbox a raw provider key. +At onboard time it registers credentials with OpenShell's provider/placeholder system, and the L7 proxy substitutes the real value into outbound requests at egress. +The CLI helper `isInferenceRouteReady` (in `src/lib/onboard.ts`) is a host-side readiness check used by the resume flow to decide whether the active route already covers the chosen provider and model — it is not a runtime component. + +For the DGX Spark-specific variant of this topology (cgroup v2, aarch64, unified memory), refer to the [NVIDIA Spark playbook](https://build.nvidia.com/spark/nemoclaw). + +## NemoClaw Plugin + +The plugin is a thin TypeScript package that registers an inference provider and the `/nemoclaw` slash command. +It runs in-process with the OpenClaw gateway inside the sandbox. +It also registers runtime hooks that keep the agent aware of its environment. +Before an agent turn starts, the plugin prepends a short context block with the active sandbox name, sandbox phase, network policy summary, and filesystem policy summary. +When the policy or phase changes during a session, the plugin sends a smaller update block instead of repeating the full context. + +```text +nemoclaw/ +├── src/ +│ ├── index.ts Plugin entry: registers all commands +│ ├── cli.ts Commander.js subcommand wiring +│ ├── runtime-context.ts Sandbox and policy context injection +│ ├── commands/ +│ │ ├── launch.ts Fresh install into OpenShell +│ │ ├── connect.ts Interactive shell into sandbox +│ │ ├── status.ts Blueprint run state + sandbox health +│ │ ├── logs.ts Stream blueprint and sandbox logs +│ │ └── slash.ts /nemoclaw chat command handler +│ └── blueprint/ +│ ├── resolve.ts Version resolution, cache management +│ ├── fetch.ts Download blueprint from OCI registry +│ ├── verify.ts Digest verification, compatibility checks +│ ├── exec.ts Subprocess execution of blueprint runner +│ └── state.ts Persistent state (run IDs) +├── openclaw.plugin.json Plugin manifest +└── package.json Commands declared under openclaw.extensions +``` + +## NemoClaw Blueprint + +The blueprint is a versioned YAML package with its own release stream. +The runner resolves, verifies, and applies the blueprint through the OpenShell CLI. +The blueprint defines the sandbox shape, default policies, and inference profiles; the runner performs the OpenShell operations. + +```text +nemoclaw-blueprint/ +├── blueprint.yaml Manifest: version, profiles, compatibility +├── model-specific-setup/ Agent-scoped model/provider compatibility manifests +├── router/ Model Router config and routing engine +├── policies/ +│ └── openclaw-sandbox.yaml Default network + filesystem policy +``` + +The blueprint runtime (TypeScript) lives in the plugin source tree: + +```text +nemoclaw/src/blueprint/ +├── runner.ts CLI runner: plan / apply / status / rollback +├── ssrf.ts SSRF endpoint validation (IP + DNS checks) +├── snapshot.ts Migration snapshot / restore lifecycle +├── state.ts Persistent run state management +``` + +### Blueprint Lifecycle + +```mermaid +flowchart LR + A[resolve] --> B[verify digest] + B --> C[plan] + C --> D[apply] + D --> E[status] +``` + +1. Resolve. The plugin locates the blueprint artifact and checks the version against `min_openshell_version` and `min_openclaw_version` constraints in `blueprint.yaml`. +2. Verify. The plugin checks the artifact digest against the expected value. +3. Plan. The runner determines what OpenShell resources to create or update, such as the gateway, providers, sandbox, inference route, and policy. +4. Apply. The runner executes the plan by calling `openshell` CLI commands. +5. Status. The runner reports current state. + +## Sandbox Environment + +Normal NemoClaw onboarding builds from the +[`ghcr.io/nvidia/nemoclaw/sandbox-base`](https://github.com/NVIDIA/NemoClaw/pkgs/container/nemoclaw%2Fsandbox-base) +base image and layers the NemoClaw runtime Dockerfile on top. The direct blueprint +runner still carries a pinned OpenShell Community OpenClaw image for legacy +`openshell sandbox create --from` compatibility. Inside the sandbox: + +- OpenClaw runs with the NemoClaw plugin pre-installed. +- Inference calls are routed through OpenShell to the configured provider. +- Network egress is restricted by the baseline policy in `openclaw-sandbox.yaml`. +- Filesystem access is confined to `/sandbox` and `/tmp` for read-write access, with system paths read-only. +- The NemoClaw plugin injects sandbox and policy context into agent turns so the agent can report policy blocks accurately. +- The image exposes a Docker health check that probes the in-sandbox gateway, so container runtimes can report whether the agent service is responding. +- The image includes common runtime compatibility helpers such as Homebrew and a `python` to `python3` symlink for tools that still invoke `python`. + +## Inference Routing + +Inference requests from the agent never leave the sandbox directly. +OpenShell intercepts them and routes to the configured provider: + +```text +Agent (sandbox) ──▶ OpenShell gateway ──▶ NVIDIA Endpoint (build.nvidia.com) +``` + +When you select the Model Router provider, the OpenShell gateway routes to a host-side router process instead of a single upstream model. +The router selects from the configured pool, then calls the upstream NVIDIA endpoint with the credential held outside the sandbox. + +Some model and provider combinations need agent-specific compatibility setup. +NemoClaw keeps those declarations under `nemoclaw-blueprint/model-specific-setup//` so OpenClaw and Hermes fixes can be tested and reviewed independently. + +Refer to Inference Options (use the `nemoclaw-user-configure-inference` skill) for provider configuration details. + +## Provider Credential Storage + +Provider credentials live in the OpenShell gateway store, not on the host filesystem. +NemoClaw never writes them to host disk; the OpenShell L7 proxy injects values at egress. +See Credential Storage (use the `nemoclaw-user-configure-security` skill) for the inspection, rotation, and migration flow. + +## Host-Side State and Config + +NemoClaw keeps non-secret operator-facing state on the host rather than inside the sandbox. + +| Path | Purpose | +|---|---| +| `~/.nemoclaw/sandboxes.json` | Registered sandbox metadata, including the default sandbox selection. | +| `~/.openclaw/openclaw.json` | Host OpenClaw configuration that NemoClaw snapshots or restores during migration flows. | + +The following environment variables configure optional services and local access. + +| Variable | Purpose | +|---|---| +| `TELEGRAM_BOT_TOKEN` | Telegram bot token you provide before `nemoclaw onboard`. OpenShell stores it in a provider; the sandbox receives placeholders, not the raw secret. | +| `TELEGRAM_ALLOWED_IDS` | Comma-separated Telegram user or chat IDs for allowlists when onboarding applies channel restrictions. | +| `SLACK_BOT_TOKEN` | Slack bot token (`xoxb-...`) you provide before `nemoclaw onboard`. Stored as an OpenShell provider; never passed directly to the sandbox. | +| `SLACK_APP_TOKEN` | Slack app-level token (`xapp-...`) required for Socket Mode. Stored alongside `SLACK_BOT_TOKEN` during onboarding. | +| `SLACK_ALLOWED_USERS` | Comma-separated Slack member IDs for DM and channel `@mention` user allowlisting. | +| `SLACK_ALLOWED_CHANNELS` | Comma-separated Slack channel IDs where channel `@mention` events are enabled (e.g. `C012AB3CD,C987ZY6XW`). Baked into the sandbox image at build time. Combine with `SLACK_ALLOWED_USERS` to restrict both channel and member. | +| `CHAT_UI_URL` | URL for the optional chat UI endpoint. | +| `NEMOCLAW_DISABLE_DEVICE_AUTH` | Build-time-only toggle that disables gateway device pairing when set to `1` before the sandbox image is created. | + +For normal setup and reconfiguration, prefer `nemoclaw onboard` over editing these files by hand. +Do not treat `NEMOCLAW_DISABLE_DEVICE_AUTH` as a runtime setting for an already-created sandbox. + ## References -- **Load [references/architecture.md](references/architecture.md)** when looking up architecture, plugin structure, or blueprint design. Describes the NemoClaw plugin and blueprint architecture and how they orchestrate the OpenClaw sandbox. - **[references/cli-selection-guide.md](references/cli-selection-guide.md)** — Explains when to use `nemoclaw` versus `openshell` for NemoClaw-managed sandboxes, including lifecycle, inference, policy, monitoring, file transfer, and gateway operations. - **Load [references/commands.md](references/commands.md)** when looking up a specific `nemoclaw` or `/nemoclaw` subcommand, flag, argument, or exit code. Includes the full CLI reference for slash commands and standalone NemoClaw commands. - **Load [references/network-policies.md](references/network-policies.md)** when looking up a specific default endpoint, filesystem path, or the runtime approval sequence NemoClaw applies on blocked requests. Covers the baseline network policy, filesystem rules, and operator approval flow. diff --git a/.agents/skills/nemoclaw-user-reference/references/architecture.md b/.agents/skills/nemoclaw-user-reference/references/architecture.md deleted file mode 100644 index 07e193434a..0000000000 --- a/.agents/skills/nemoclaw-user-reference/references/architecture.md +++ /dev/null @@ -1,260 +0,0 @@ - - -# Architecture Details - -NemoClaw combines a host CLI, a TypeScript plugin that runs with OpenClaw inside the sandbox, and a versioned YAML blueprint that defines the sandbox image, policies, and inference profiles applied through OpenShell. - -## System Overview - -NVIDIA OpenShell is a general-purpose agent runtime. It provides sandbox containers, a credential-storing gateway, inference proxying, and policy enforcement, but has no opinions about what runs inside. NemoClaw is an opinionated reference stack built on OpenShell that handles what goes in the sandbox and makes the setup accessible. - -```mermaid -graph LR - classDef nemoclaw fill:#76b900,stroke:#5a8f00,color:#fff,stroke-width:2px,font-weight:bold - classDef openshell fill:#1a1a1a,stroke:#1a1a1a,color:#fff,stroke-width:2px,font-weight:bold - classDef sandbox fill:#444,stroke:#76b900,color:#fff,stroke-width:2px,font-weight:bold - classDef agent fill:#f5f5f5,stroke:#e0e0e0,color:#1a1a1a,stroke-width:1px - classDef external fill:#f5f5f5,stroke:#e0e0e0,color:#1a1a1a,stroke-width:1px - classDef user fill:#fff,stroke:#76b900,color:#1a1a1a,stroke-width:2px,font-weight:bold - - USER(["👤 User"]):::user - - subgraph EXTERNAL["External Services"] - INFERENCE["Inference Provider
NVIDIA Endpoints · OpenAI
Anthropic · Ollama · vLLM · Model Router
"]:::external - MSGAPI["Messaging Platforms
Telegram · Discord · Slack"]:::external - INTERNET["Internet
PyPI · npm · GitHub · APIs"]:::external - end - - subgraph HOST["Host Machine"] - - subgraph NEMOCLAW["NemoClaw"] - direction TB - NCLI["CLI + Onboarding
Guided setup · provider selection
credential validation · deploy
"]:::nemoclaw - BP["Blueprint
Hardened Dockerfile
Network policies · Presets
Security configuration
"]:::nemoclaw - MIGRATE["State Management
Migration snapshots
Credential stripping
Integrity verification
"]:::nemoclaw - end - - subgraph OPENSHELL["OpenShell"] - direction TB - GW["Gateway
Credential store
Inference proxy
Policy engine
Device auth
"]:::openshell - OSCLI["openshell CLI
provider · sandbox
gateway · policy
"]:::openshell - CHMSG["Channel messaging
OpenShell-managed
Telegram · Discord · Slack
"]:::openshell - - subgraph SANDBOX["Sandbox Container 🔒"] - direction TB - AGENT["Agent
OpenClaw or any
compatible agent
"]:::agent - PLUG["NemoClaw Plugin
Extends agent with
managed configuration
"]:::sandbox - end - end - end - - USER -->|"nemoclaw onboard
nemoclaw connect"| NCLI - USER -->|"Chat messages"| MSGAPI - - NCLI -->|"Orchestrates"| OSCLI - BP -->|"Defines sandbox
shape + policies"| SANDBOX - MIGRATE -->|"Safe state
transfer"| SANDBOX - - AGENT -->|"Inference requests
no credentials"| GW - GW -->|"Proxied with
credential injected"| INFERENCE - - MSGAPI -->|"Platform APIs"| CHMSG - CHMSG -->|"Deliver to agent"| AGENT - - AGENT -.->|"Policy-gated"| INTERNET - GW -.->|"Enforced by
gateway"| INTERNET -``` - -## Deployment Topology - -The logical diagram above shows how components relate. -This section shows what actually runs where on the host. -NemoClaw's default Docker-driver topology does not place the sandbox in an embedded k3s cluster. -On Linux and Apple Silicon macOS, NemoClaw starts the OpenShell Docker-driver gateway and creates the sandbox as a Docker container. -The gateway normally runs as a host process; Linux hosts that need the gateway compatibility patch may run the same gateway binary inside a small container. -In both Docker-driver modes, the sandbox is a Docker container, not a Kubernetes pod. -Legacy non-Docker-driver installs still use the k3s-based gateway path; the diagram below shows the standard Docker-driver topology. - -```mermaid -graph TB - classDef host fill:#fff,stroke:#76b900,stroke-width:2px,color:#1a1a1a,font-weight:bold - classDef cli fill:#76b900,stroke:#5a8f00,color:#fff,stroke-width:2px,font-weight:bold - classDef docker fill:#2496ed,stroke:#1577c2,color:#fff,stroke-width:2px,font-weight:bold - classDef gateway fill:#1a1a1a,stroke:#1a1a1a,color:#fff,stroke-width:2px,font-weight:bold - classDef sandbox fill:#444,stroke:#76b900,color:#fff,stroke-width:2px - classDef external fill:#f5f5f5,stroke:#e0e0e0,color:#1a1a1a,stroke-width:1px - - subgraph HOST["Host machine · Linux / Apple Silicon macOS / DGX Spark / DGX Station"] - direction TB - CLI["nemoclaw CLI
bin/nemoclaw.js → dist/
onboard · connect · status · logs
"]:::cli - GW["OpenShell gateway
host process by default
credential store · lifecycle · L7 proxy
"]:::gateway - - subgraph DOCKER["Docker daemon"] - direction TB - SANDBOX["Sandbox container 🔒
Landlock + seccomp + netns
OpenClaw agent + NemoClaw plugin
"]:::sandbox - end - end - - INFER["Inference provider
NVIDIA Endpoints · OpenAI
Anthropic · Ollama · vLLM · Model Router
"]:::external - - CLI -->|"openshell CLI
(orchestrates)"| GW - GW -->|"creates/recreates
Docker-driver sandbox"| SANDBOX - SANDBOX -->|"inference requests
placeholder credentials"| GW - GW -->|"egress with real credentials
injected at the L7 proxy"| INFER - - class HOST host - class DOCKER docker - class GW gateway - class SANDBOX sandbox -``` - -Layering from top to bottom: - -| Layer | Runs as | Role | -|---|---|---| -| Host CLI | Host process (`nemoclaw` on Node.js) | Orchestrates OpenShell via `openshell` CLI calls. | -| OpenShell gateway | Host process by default; optional Linux compatibility container when the gateway binary needs a newer host ABI | Hosts the credential store, owns sandbox lifecycle coordination, and provides the L7 proxy. | -| Docker daemon | Host service | Runs the Docker-driver sandbox container and, on affected Linux hosts, the optional gateway compatibility container. | -| Sandbox container | Docker container | Runs the OpenClaw agent and the NemoClaw plugin under Landlock + seccomp + netns. | -| OpenShell L7 proxy | Gateway process | Intercepts agent egress and rewrites `Authorization` headers (Bearer/Bot) and URL-path segments to inject the real credential at the network boundary. | - -NemoClaw never gives the sandbox a raw provider key. -At onboard time it registers credentials with OpenShell's provider/placeholder system, and the L7 proxy substitutes the real value into outbound requests at egress. -The CLI helper `isInferenceRouteReady` (in `src/lib/onboard.ts`) is a host-side readiness check used by the resume flow to decide whether the active route already covers the chosen provider and model — it is not a runtime component. - -For the DGX Spark-specific variant of this topology (cgroup v2, aarch64, unified memory), refer to the [NVIDIA Spark playbook](https://build.nvidia.com/spark/nemoclaw). - -## NemoClaw Plugin - -The plugin is a thin TypeScript package that registers an inference provider and the `/nemoclaw` slash command. -It runs in-process with the OpenClaw gateway inside the sandbox. -It also registers runtime hooks that keep the agent aware of its environment. -Before an agent turn starts, the plugin prepends a short context block with the active sandbox name, sandbox phase, network policy summary, and filesystem policy summary. -When the policy or phase changes during a session, the plugin sends a smaller update block instead of repeating the full context. - -```text -nemoclaw/ -├── src/ -│ ├── index.ts Plugin entry: registers all commands -│ ├── cli.ts Commander.js subcommand wiring -│ ├── runtime-context.ts Sandbox and policy context injection -│ ├── commands/ -│ │ ├── launch.ts Fresh install into OpenShell -│ │ ├── connect.ts Interactive shell into sandbox -│ │ ├── status.ts Blueprint run state + sandbox health -│ │ ├── logs.ts Stream blueprint and sandbox logs -│ │ └── slash.ts /nemoclaw chat command handler -│ └── blueprint/ -│ ├── resolve.ts Version resolution, cache management -│ ├── fetch.ts Download blueprint from OCI registry -│ ├── verify.ts Digest verification, compatibility checks -│ ├── exec.ts Subprocess execution of blueprint runner -│ └── state.ts Persistent state (run IDs) -├── openclaw.plugin.json Plugin manifest -└── package.json Commands declared under openclaw.extensions -``` - -## NemoClaw Blueprint - -The blueprint is a versioned YAML package with its own release stream. -The runner resolves, verifies, and applies the blueprint through the OpenShell CLI. -The blueprint defines the sandbox shape, default policies, and inference profiles; the runner performs the OpenShell operations. - -```text -nemoclaw-blueprint/ -├── blueprint.yaml Manifest: version, profiles, compatibility -├── model-specific-setup/ Agent-scoped model/provider compatibility manifests -├── router/ Model Router config and routing engine -├── policies/ -│ └── openclaw-sandbox.yaml Default network + filesystem policy -``` - -The blueprint runtime (TypeScript) lives in the plugin source tree: - -```text -nemoclaw/src/blueprint/ -├── runner.ts CLI runner: plan / apply / status / rollback -├── ssrf.ts SSRF endpoint validation (IP + DNS checks) -├── snapshot.ts Migration snapshot / restore lifecycle -├── state.ts Persistent run state management -``` - -### Blueprint Lifecycle - -```mermaid -flowchart LR - A[resolve] --> B[verify digest] - B --> C[plan] - C --> D[apply] - D --> E[status] -``` - -1. Resolve. The plugin locates the blueprint artifact and checks the version against `min_openshell_version` and `min_openclaw_version` constraints in `blueprint.yaml`. -2. Verify. The plugin checks the artifact digest against the expected value. -3. Plan. The runner determines what OpenShell resources to create or update, such as the gateway, providers, sandbox, inference route, and policy. -4. Apply. The runner executes the plan by calling `openshell` CLI commands. -5. Status. The runner reports current state. - -## Sandbox Environment - -Normal NemoClaw onboarding builds from the -[`ghcr.io/nvidia/nemoclaw/sandbox-base`](https://github.com/NVIDIA/NemoClaw/pkgs/container/nemoclaw%2Fsandbox-base) -base image and layers the NemoClaw runtime Dockerfile on top. The direct blueprint -runner still carries a pinned OpenShell Community OpenClaw image for legacy -`openshell sandbox create --from` compatibility. Inside the sandbox: - -- OpenClaw runs with the NemoClaw plugin pre-installed. -- Inference calls are routed through OpenShell to the configured provider. -- Network egress is restricted by the baseline policy in `openclaw-sandbox.yaml`. -- Filesystem access is confined to `/sandbox` and `/tmp` for read-write access, with system paths read-only. -- The NemoClaw plugin injects sandbox and policy context into agent turns so the agent can report policy blocks accurately. -- The image exposes a Docker health check that probes the in-sandbox gateway, so container runtimes can report whether the agent service is responding. -- The image includes common runtime compatibility helpers such as Homebrew and a `python` to `python3` symlink for tools that still invoke `python`. - -## Inference Routing - -Inference requests from the agent never leave the sandbox directly. -OpenShell intercepts them and routes to the configured provider: - -```text -Agent (sandbox) ──▶ OpenShell gateway ──▶ NVIDIA Endpoint (build.nvidia.com) -``` - -When you select the Model Router provider, the OpenShell gateway routes to a host-side router process instead of a single upstream model. -The router selects from the configured pool, then calls the upstream NVIDIA endpoint with the credential held outside the sandbox. - -Some model and provider combinations need agent-specific compatibility setup. -NemoClaw keeps those declarations under `nemoclaw-blueprint/model-specific-setup//` so OpenClaw and Hermes fixes can be tested and reviewed independently. - -Refer to Inference Options (use the `nemoclaw-user-configure-inference` skill) for provider configuration details. - -## Provider Credential Storage - -Provider credentials live in the OpenShell gateway store, not on the host filesystem. -NemoClaw never writes them to host disk; the OpenShell L7 proxy injects values at egress. -See Credential Storage (use the `nemoclaw-user-configure-security` skill) for the inspection, rotation, and migration flow. - -## Host-Side State and Config - -NemoClaw keeps non-secret operator-facing state on the host rather than inside the sandbox. - -| Path | Purpose | -|---|---| -| `~/.nemoclaw/sandboxes.json` | Registered sandbox metadata, including the default sandbox selection. | -| `~/.openclaw/openclaw.json` | Host OpenClaw configuration that NemoClaw snapshots or restores during migration flows. | - -The following environment variables configure optional services and local access. - -| Variable | Purpose | -|---|---| -| `TELEGRAM_BOT_TOKEN` | Telegram bot token you provide before `nemoclaw onboard`. OpenShell stores it in a provider; the sandbox receives placeholders, not the raw secret. | -| `TELEGRAM_ALLOWED_IDS` | Comma-separated Telegram user or chat IDs for allowlists when onboarding applies channel restrictions. | -| `SLACK_BOT_TOKEN` | Slack bot token (`xoxb-...`) you provide before `nemoclaw onboard`. Stored as an OpenShell provider; never passed directly to the sandbox. | -| `SLACK_APP_TOKEN` | Slack app-level token (`xapp-...`) required for Socket Mode. Stored alongside `SLACK_BOT_TOKEN` during onboarding. | -| `SLACK_ALLOWED_USERS` | Comma-separated Slack member IDs for DM and channel `@mention` user allowlisting. | -| `SLACK_ALLOWED_CHANNELS` | Comma-separated Slack channel IDs where channel `@mention` events are enabled (e.g. `C012AB3CD,C987ZY6XW`). Baked into the sandbox image at build time. Combine with `SLACK_ALLOWED_USERS` to restrict both channel and member. | -| `CHAT_UI_URL` | URL for the optional chat UI endpoint. | -| `NEMOCLAW_DISABLE_DEVICE_AUTH` | Build-time-only toggle that disables gateway device pairing when set to `1` before the sandbox image is created. | - -For normal setup and reconfiguration, prefer `nemoclaw onboard` over editing these files by hand. -Do not treat `NEMOCLAW_DISABLE_DEVICE_AUTH` as a runtime setting for an already-created sandbox. diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 2c7836d7d4..eba55e56d3 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -47,10 +47,10 @@ The current generated skills and their source pages are: |---|---| | `nemoclaw-user-overview` | `docs/about/overview.mdx`, `docs/about/ecosystem.mdx`, `docs/about/how-it-works.mdx`, `docs/about/release-notes.mdx` | | `nemoclaw-user-agent-skills` | `docs/resources/agent-skills.mdx` | -| `nemoclaw-user-deploy-remote` | `docs/deployment/deploy-to-remote-gpu.mdx`, `docs/deployment/install-openclaw-plugins.mdx`, `docs/deployment/sandbox-hardening.mdx` | +| `nemoclaw-user-deploy-remote` | `docs/deployment/deploy-to-remote-gpu.mdx`, `docs/deployment/brev-web-ui.mdx`, `docs/deployment/install-openclaw-plugins.mdx`, `docs/deployment/sandbox-hardening.mdx` | | `nemoclaw-user-get-started` | `docs/get-started/prerequisites.mdx`, `docs/get-started/quickstart.mdx`, `docs/get-started/quickstart-hermes.mdx`, `docs/get-started/windows-preparation.mdx` | -| `nemoclaw-user-configure-inference` | `docs/inference/inference-options.mdx`, `docs/inference/use-local-inference.mdx`, `docs/inference/switch-inference-providers.mdx`, `docs/inference/set-up-sub-agent.mdx` | -| `nemoclaw-user-manage-sandboxes` | `docs/manage-sandboxes/lifecycle.mdx`, `docs/manage-sandboxes/messaging-channels.mdx`, `docs/manage-sandboxes/workspace-files.mdx`, `docs/manage-sandboxes/backup-restore.mdx` | +| `nemoclaw-user-configure-inference` | `docs/inference/inference-options.mdx`, `docs/inference/use-local-inference.mdx`, `docs/inference/switch-inference-providers.mdx`, `docs/inference/set-up-sub-agent.mdx`, `docs/inference/tool-calling-reliability.mdx` | +| `nemoclaw-user-manage-sandboxes` | `docs/manage-sandboxes/lifecycle.mdx`, `docs/manage-sandboxes/runtime-controls.mdx`, `docs/manage-sandboxes/messaging-channels.mdx`, `docs/manage-sandboxes/workspace-files.mdx`, `docs/manage-sandboxes/backup-restore.mdx` | | `nemoclaw-user-monitor-sandbox` | `docs/monitoring/monitor-sandbox-activity.mdx` | | `nemoclaw-user-manage-policy` | `docs/network-policy/customize-network-policy.mdx`, `docs/network-policy/integration-policy-examples.mdx`, `docs/network-policy/approve-network-requests.mdx` | | `nemoclaw-user-reference` | `docs/reference/architecture.mdx`, `docs/reference/commands.mdx`, `docs/reference/cli-selection-guide.mdx`, `docs/reference/network-policies.mdx`, `docs/reference/troubleshooting.mdx` | @@ -87,16 +87,18 @@ Other useful flags: | Flag | Purpose | |------|---------| -| `--strategy ` | Grouping strategy: `smart` (default), `grouped`, or `individual`. | +| `--strategy ` | Grouping strategy: `grouped` (default) or `individual`. | | `--doc-platform ` | Source format: `fern-mdx` for migrated Fern pages or `myst-md` for legacy Markdown. | | `--name-map CAT=NAME` | Override a generated skill name (e.g. `--name-map about=overview`). | | `--exclude ` | Skip specific files (e.g. `--exclude "release-notes.mdx"`). | ### How the Script Works -The script reads YAML frontmatter from each doc page to determine its content type (`how_to`, `concept`, `reference`, `get_started`), then groups pages into skills using the `smart` strategy by default. -Within each group, the procedure page (`how_to`, `get_started`, or `tutorial`) with the lowest `skill.priority` becomes the main body of the skill. -Sibling procedure pages, concept pages, and reference pages go into a `references/` subdirectory for progressive disclosure, keeping `SKILL.md` concise while preserving access to the full docs. +The script reads YAML frontmatter from each doc page to determine its content type (`how_to`, `concept`, `reference`, `get_started`), then groups pages into skills using the `grouped` strategy by default. +Within each directory group, the page with the lowest `skill.priority` value (highest priority) becomes the full body of `SKILL.md`. +Sibling pages are written unchanged to `references/`. + +Use `--strategy individual` to emit one skill per `how_to`, `get_started`, or `tutorial` page and collect all other pages into a single reference skill. Cross-references between doc pages are rewritten as skill-to-skill pointers so agents can navigate between skills. Fern MDX components and MyST/Sphinx directives are converted to standard markdown. diff --git a/scripts/docs-to-skills.py b/scripts/docs-to-skills.py index c8bb85fb7a..daa2791b89 100755 --- a/scripts/docs-to-skills.py +++ b/scripts/docs-to-skills.py @@ -18,22 +18,17 @@ 1. Scans a docs directory for Markdown or Fern MDX files with YAML frontmatter. 2. Classifies each page by content type (how_to, concept, reference, get_started) using the frontmatter `content.type` field. - 3. Groups pages into skills using one of three strategies: - - smart (default): groups by directory; the procedure page with the - lowest frontmatter `skill.priority` becomes the main SKILL.md body, - while sibling procedure, concept, and reference pages ride along as - reference files. - - grouped: groups all pages in the same parent directory. - - individual: each doc page becomes its own skill. + 3. Groups pages into skills using one of two strategies: + - grouped (default): groups by parent directory; the page with the + lowest frontmatter ``skill.priority`` (highest priority) becomes the + full SKILL.md body; siblings go to ``references/``. + - individual: each ``how_to``, ``get_started``, or ``tutorial`` page + becomes its own skill; all other pages are collected into one + reference skill. 4. Generates a skill directory per group containing: - - SKILL.md with frontmatter (name, description), prerequisites, - procedural steps for the primary procedure page, a References - section that links to sibling pages, and a Related Skills section. - Sibling procedure, concept, and reference bodies are not inlined, - so SKILL.md stays small and nothing is truncated mid-table or - mid-code-fence. - - references/ with the full sibling procedure, concept, and reference - content for progressive disclosure (loaded by the agent on demand). + - SKILL.md with frontmatter (name, description), the lead page body, + a References section linking sibling pages, and Related Skills links. + - references/ with full sibling page content for progressive disclosure. 5. Resolves all relative doc paths to repo-root-relative paths, and converts cross-references between docs into skill-to-skill pointers so agents can navigate between skills. @@ -1434,8 +1429,10 @@ def _to_third_person(sentence: str) -> str: "concept": "context", "reference": "reference", } +PROCEDURE_CONTENT_TYPES = frozenset({"how_to", "get_started", "tutorial"}) +SKIP_SKILL_SECTIONS = frozenset({"prerequisites", "before you begin", "troubleshooting"}) +RELATED_SKILL_SECTIONS = frozenset({"related topics", "next steps"}) SKILL_FRONTMATTER_LICENSE = "Apache-2.0" -MAX_SKILL_MD_CHARS = 11_500 def markdown_spdx_header() -> str: @@ -1449,37 +1446,6 @@ def markdown_spdx_header() -> str: ) -def split_markdown_h3_sections(content: str) -> tuple[str, list[tuple[str, str]]]: - """Split an H2 body into preamble plus H3 subsection blocks.""" - preamble: list[str] = [] - sections: list[tuple[str, str]] = [] - current_heading: str | None = None - current_lines: list[str] = [] - - def _flush() -> None: - nonlocal current_heading, current_lines - if current_heading is None: - return - body = "\n".join(current_lines).strip() - sections.append((current_heading, body)) - current_heading = None - current_lines = [] - - for line in content.split("\n"): - if line.startswith("### "): - _flush() - current_heading = line[4:].strip() - current_lines = [line] - continue - if current_heading is None: - preamble.append(line) - else: - current_lines.append(line) - _flush() - - return "\n".join(preamble).strip(), sections - - _SECTION_HEADING_RE = re.compile(r"(?m)^(#{2,6})\s+(.+)$") @@ -1522,37 +1488,47 @@ def canonicalize_leading_h1(body: str, title: str) -> str: return f"# {title}\n\n{body}".rstrip() -def partition_skill_pages( - pages: list[DocPage], -) -> tuple[list[DocPage], list[DocPage], list[DocPage], list[DocPage]]: - """Split a doc group into inline procedures and deferred references. - - The converter preserves the existing one-skill-per-docs-area grouping, but - keeps SKILL.md focused by inlining only one primary procedure. The primary - procedure is the page with the lowest frontmatter ``skill.priority``; - additional how-to/tutorial pages still contribute triggers through the - skill description and are written to references/ for progressive disclosure. - """ - procedures = [ - p for p in pages if CONTENT_TYPE_ROLE.get(p.content_type) == "procedure" - ] - # Pages without a recognized content_type default to procedure. - procedures.extend([p for p in pages if p.content_type not in CONTENT_TYPE_ROLE]) - context_pages = [ - p for p in pages if CONTENT_TYPE_ROLE.get(p.content_type) == "context" - ] - reference_pages = [ - p for p in pages if CONTENT_TYPE_ROLE.get(p.content_type) == "reference" - ] +def partition_skill_pages(pages: list[DocPage]) -> tuple[DocPage, list[DocPage]]: + """Split a skill group into the lead page and reference siblings. + + The lead page has the lowest ``skill.priority`` value (highest priority). + """ + ordered = sorted(pages, key=lambda p: (p.skill_priority, str(p.path))) + return ordered[0], ordered[1:] - if not procedures: - return [], [], context_pages, reference_pages - procedures = sorted(procedures, key=lambda p: (p.skill_priority, str(p.path))) - primary = [procedures[0]] - deferred = procedures[1:] - return primary, deferred, context_pages, reference_pages +def _append_page_sections_to_skill( + page: DocPage, + lines: list[str], + *, + clean_fn, + skill_md_images: list[tuple[Path, str]], + skill_md_local_links: dict[str, str], + collected_related: list[str], +) -> None: + """Append a doc page body to SKILL.md lines.""" + for heading, content in page.sections: + heading_lower = heading.lower() + if heading_lower in SKIP_SKILL_SECTIONS: + continue + if heading_lower in RELATED_SKILL_SECTIONS: + collected_related.append( + clean_fn(content, page, skill_md_images, skill_md_local_links) + ) + continue + cleaned = clean_fn(content, page, skill_md_images, skill_md_local_links) + if not heading: + cleaned = re.sub(r"^#\s+.+(?:\n|$)", "", cleaned) + if cleaned.strip(): + lines.append(cleaned.strip()) + lines.append("") + continue + lines.append(f"## {heading}") + lines.append("") + if cleaned.strip(): + lines.append(cleaned.strip()) + lines.append("") def generate_skill( @@ -1566,18 +1542,7 @@ def generate_skill( doc_platform: str = "myst-md", dry_run: bool = False, ) -> dict: - """Generate a complete skill directory from a group of doc pages. - - Writes identical output to each directory in *output_dirs*. Since - inter-doc links are rewritten to either skill cross-references or - absolute HTTPS URLs (see :func:`rewrite_doc_paths`), the emitted - content is independent of where it is written and can safely be - mirrored across multiple output roots. Image assets referenced by - the source pages are copied alongside the file that links them so - the rendered skill works without network access. - - Returns a summary dict for reporting. - """ + """Generate a complete skill directory from a group of doc pages.""" skill_md_images: list[tuple[Path, str]] = [] ref_images: dict[str, list[tuple[Path, str]]] = {} @@ -1587,7 +1552,6 @@ def _clean( image_acc: list[tuple[Path, str]], local_doc_links: dict[str, str] | None = None, ) -> str: - """Apply directive cleanup and path rewriting for a source page.""" if doc_platform == "fern-mdx": result = clean_fern_mdx(text) else: @@ -1605,10 +1569,7 @@ def _clean( image_acc.extend(copies) return result - procedures, deferred_procedures, context_pages, reference_pages = ( - partition_skill_pages(pages) - ) - ref_section_pages = deferred_procedures + context_pages + reference_pages + primary_page, reference_pages = partition_skill_pages(pages) def _page_rel(page: DocPage) -> str | None: if docs_dir is None: @@ -1620,135 +1581,20 @@ def _page_rel(page: DocPage) -> str | None: skill_md_local_links: dict[str, str] = {} reference_local_links: dict[str, str] = {} - for page in ref_section_pages: + for page in reference_pages: rel = _page_rel(page) if rel is None: continue ref_name = page.path.stem + ".md" skill_md_local_links[rel] = f"references/{ref_name}" reference_local_links[rel] = ref_name - for page in procedures: - rel = _page_rel(page) - if rel is not None: - reference_local_links[rel] = "../SKILL.md" - - description_pages = ( - procedures + deferred_procedures + context_pages + reference_pages - if procedures - else pages - ) - description = build_skill_description(name, description_pages) - generated_ref_sections: dict[str, list[str]] = {} - generated_ref_topics: dict[str, list[str]] = {} - generated_ref_images: dict[str, list[tuple[Path, str]]] = {} - generated_ref_names: dict[Path, str] = {} - reserved_ref_names = {page.path.stem + ".md" for page in ref_section_pages} - - def _unique_generated_ref_name(page: DocPage) -> str: - cached = generated_ref_names.get(page.path) - if cached: - return cached - base = re.sub(r"[^a-z0-9-]", "-", page.path.stem.lower()).strip("-") - candidate = f"{base}-details.md" - suffix = 2 - while candidate in reserved_ref_names: - candidate = f"{base}-details-{suffix}.md" - suffix += 1 - reserved_ref_names.add(candidate) - generated_ref_names[page.path] = candidate - return candidate - - def _defer_detail( - page: DocPage, - section_heading: str, - content: str, - topic: str | None = None, - ) -> str: - """Store overflow procedure detail in a generated reference file.""" - ref_name = _unique_generated_ref_name(page) - if ref_name not in generated_ref_sections: - title = page.title or _brand_case(page.path.stem.replace("-", " ").title()) - generated_ref_sections[ref_name] = [f"# {title}: Details"] - generated_ref_topics[ref_name] = [] - generated_ref_images[ref_name] = [] - block = content.strip() - if not block: - return ref_name - # Overflow content was cleaned for SKILL.md first, where same-skill - # reference targets live under references/. Once moved into a generated - # reference file, those targets are siblings. - block = re.sub(r"\]\(references/([^)]+)\)", r"](\1)", block) - if not block.startswith("#"): - block = f"## {section_heading}\n\n{block}" - generated_ref_sections[ref_name].append(block) - topic_text = topic or section_heading - if topic_text and topic_text not in generated_ref_topics[ref_name]: - generated_ref_topics[ref_name].append(topic_text) - return ref_name - - def _current_skill_size() -> int: - return len("\n".join(lines)) - - def _append_section_or_defer( - page: DocPage, - heading: str, - cleaned_content: str, - ) -> None: - """Append a procedure section, moving overflow detail to references.""" - section_lines = [f"## {heading}", "", cleaned_content, ""] - if ( - _current_skill_size() + len("\n".join(section_lines)) - <= MAX_SKILL_MD_CHARS - ): - lines.extend(section_lines) - return - - preamble, subsections = split_markdown_h3_sections(cleaned_content) - if not subsections: - ref_name = _defer_detail(page, heading, cleaned_content) - lines.extend( - [ - f"## {heading}", - "", - f"Load [references/{ref_name}](references/{ref_name}) for detailed steps.", - "", - ] - ) - return + primary_rel = _page_rel(primary_page) + if primary_rel is not None: + reference_local_links[primary_rel] = "../SKILL.md" - lines.append(f"## {heading}") - lines.append("") - if preamble: - lines.append(preamble) - lines.append("") - - deferred_topics: list[str] = [] - for subheading, block in subsections: - block_lines = [block, ""] - if ( - _current_skill_size() + len("\n".join(block_lines)) - <= MAX_SKILL_MD_CHARS - ): - lines.extend(block_lines) - continue - ref_name = _defer_detail(page, heading, block, topic=subheading) - if subheading not in deferred_topics: - deferred_topics.append(subheading) - - if deferred_topics: - ref_name = _unique_generated_ref_name(page) - topic_text = ", ".join(deferred_topics[:3]) - if len(deferred_topics) > 3: - topic_text += ", and related details" - lines.append( - f"Load [references/{ref_name}](references/{ref_name}) for detailed steps on {topic_text}." - ) - lines.append("") - - # Build SKILL.md content + description = build_skill_description(name, pages) lines: list[str] = [] - # Frontmatter lines.append("---") lines.append(f"name: {yaml_scalar(name)}") lines.append(f"description: {yaml_scalar(description)}") @@ -1758,49 +1604,35 @@ def _append_section_or_defer( lines.append(markdown_spdx_header().rstrip("\n")) lines.append("") - # Title — prefer the lead page's frontmatter `title.page` (or H1) - # verbatim so the SKILL.md heading matches the source doc instead of - # echoing the auto-generated, prefix-laden skill name. - lead_page = procedures[0] if procedures else pages[0] if pages else None - if lead_page and lead_page.title: - skill_title = lead_page.title - else: - skill_title = _brand_case(name.replace("-", " ").title()) + skill_title = primary_page.title or _brand_case(name.replace("-", " ").title()) lines.append(f"# {skill_title}") lines.append("") - # Gotchas — surface :::{warning} admonitions from the source procedure - # pages at the top so the agent sees non-obvious corrections before it - # commits to a path through the steps. The warnings stay in place - # inline; this section is a directed summary, not a replacement. - gotchas = _extract_gotchas(procedures, doc_platform=doc_platform) + gotchas = _extract_gotchas([primary_page], doc_platform=doc_platform) if gotchas: lines.append("## Gotchas") lines.append("") - for g in gotchas: - lines.append(g) + for gotcha in gotchas: + lines.append(gotcha) lines.append("") - # Prerequisites (merged from all procedure pages, deduplicated) prereq_items: list[str] = [] seen_prereqs: set[str] = set() - for pp in procedures: - for heading, content in pp.sections: - if heading.lower() in ("prerequisites", "before you begin"): - cleaned = _clean( - content, pp, skill_md_images, skill_md_local_links - ) - for item_line in cleaned.split("\n"): - stripped = item_line.strip() - if stripped.startswith("- "): - if prereq_items and not prereq_items[-1].startswith("- "): - prereq_items.append("") - norm = stripped.lower().strip("- .") - if norm not in seen_prereqs: - seen_prereqs.add(norm) - prereq_items.append(stripped) - elif stripped and not prereq_items: - prereq_items.append(stripped) + for heading, content in primary_page.sections: + if heading.lower() not in ("prerequisites", "before you begin"): + continue + cleaned = _clean(content, primary_page, skill_md_images, skill_md_local_links) + for item_line in cleaned.split("\n"): + stripped = item_line.strip() + if stripped.startswith("- "): + if prereq_items and not prereq_items[-1].startswith("- "): + prereq_items.append("") + norm = stripped.lower().strip("- .") + if norm not in seen_prereqs: + seen_prereqs.add(norm) + prereq_items.append(stripped) + elif stripped and not prereq_items: + prereq_items.append(stripped) if prereq_items: lines.append("## Prerequisites") @@ -1809,73 +1641,44 @@ def _append_section_or_defer( lines.append(item) lines.append("") - # Procedural sections from how_to and get_started pages - skip_sections = {"prerequisites", "before you begin", "troubleshooting"} - related_sections = {"related topics", "next steps"} - collected_related: list[str] = [] # raw content from related sections - for idx, pp in enumerate(procedures): - # When merging multiple docs, add a transition heading - if len(procedures) > 1 and idx > 0 and pp.title: - lines.append("---") - lines.append("") - - for heading, content in pp.sections: - if heading.lower() in skip_sections: - continue - if heading.lower() in related_sections: - collected_related.append( - _clean(content, pp, skill_md_images, skill_md_local_links) - ) - continue - if not heading: - cleaned = _clean(content, pp, skill_md_images, skill_md_local_links) - cleaned = re.sub(r"^#\s+.+\n+", "", cleaned) - if cleaned.strip(): - lines.append(cleaned) - lines.append("") - continue - - cleaned_content = _clean( - content, pp, skill_md_images, skill_md_local_links - ) - _append_section_or_defer(pp, heading, cleaned_content) + collected_related: list[str] = [] + _append_page_sections_to_skill( + primary_page, + lines, + clean_fn=_clean, + skill_md_images=skill_md_images, + skill_md_local_links=skill_md_local_links, + collected_related=collected_related, + ) - # Build Related Skills from collected sections + any remaining in body raw_md = "\n".join(lines) raw_md, body_related = extract_related_skills(raw_md) lines = raw_md.rstrip("\n").split("\n") - # Also extract from the collected_related content all_related_text = "\n".join( f"## Related Topics\n\n{block}" for block in collected_related ) _, section_related = extract_related_skills(all_related_text) - # Merge and deduplicate seen_skills: set[str] = set() merged_entries: list[str] = [] for entry in section_related + body_related: skill_match = re.search(r"`([a-z0-9-]+)`", entry) key = skill_match.group(1) if skill_match else entry if key == name: - continue # skip self-references + continue if key not in seen_skills: seen_skills.add(key) merged_entries.append(entry) - # References section — point at the full concept/reference files that - # ship alongside SKILL.md. Each bullet leads with the activation - # trigger from description.agent (the "Use when ..." clause) so the - # agent can decide on-sight whether to load the file, which is how - # progressive disclosure is supposed to work. - if ref_section_pages or generated_ref_topics: + if reference_pages: lines.append("") lines.append("## References") lines.append("") - for rp in ref_section_pages: - ref_name = rp.path.stem + ".md" + for ref_page in reference_pages: + ref_name = ref_page.path.stem + ".md" file_link = f"[references/{ref_name}](references/{ref_name})" - covers, trigger = _split_description_trigger(rp.description or "") + covers, trigger = _split_description_trigger(ref_page.description or "") if trigger: bullet = f"- **Load {file_link}** {trigger}." if covers: @@ -1885,14 +1688,6 @@ def _append_section_or_defer( else: bullet = f"- {file_link}" lines.append(bullet) - for ref_name, topics in generated_ref_topics.items(): - file_link = f"[references/{ref_name}](references/{ref_name})" - topic_text = ", ".join(topics[:3]) - if len(topics) > 3: - topic_text += ", and related details" - lines.append( - f"- **Load {file_link}** when you need detailed steps for {topic_text}." - ) if merged_entries: lines.append("") @@ -1904,28 +1699,20 @@ def _append_section_or_defer( skill_md = normalize_heading_levels("\n".join(lines)) - # --- Build reference files --- ref_files: dict[str, str] = {} - for ref_name, sections in generated_ref_sections.items(): - body = "\n\n".join(sections) - body = normalize_heading_levels(dedupe_repeated_heading_sections(body)) - ref_files[ref_name] = body - ref_images[ref_name] = generated_ref_images.get(ref_name, []) - - for rp in deferred_procedures + reference_pages + context_pages: - ref_name = rp.path.stem + ".md" + for ref_page in reference_pages: + ref_name = ref_page.path.stem + ".md" ref_image_acc: list[tuple[Path, str]] = [] - body = _clean(rp.body, rp, ref_image_acc, reference_local_links) - if doc_platform == "myst-md" and rp.title: - body = canonicalize_leading_h1(body, rp.title) - elif doc_platform == "fern-mdx" and rp.title and not body.startswith("# "): - body = f"# {rp.title}\n\n{body}".rstrip() + body = _clean(ref_page.body, ref_page, ref_image_acc, reference_local_links) + if doc_platform == "myst-md" and ref_page.title: + body = canonicalize_leading_h1(body, ref_page.title) + elif doc_platform == "fern-mdx" and ref_page.title and not body.startswith("# "): + body = f"# {ref_page.title}\n\n{body}".rstrip() body = normalize_heading_levels(body) body = dedupe_repeated_heading_sections(body) ref_files[ref_name] = body ref_images[ref_name] = ref_image_acc - # --- Write output --- summary = { "name": name, "dirs": [str(d / name) for d in output_dirs], @@ -1947,16 +1734,19 @@ def _append_section_or_defer( _copy_skill_images(skill_dir, skill_md_images) spdx_ref = markdown_spdx_header() - - + refs_dir = skill_dir / "references" if ref_files: - refs_dir = skill_dir / "references" refs_dir.mkdir(exist_ok=True) + for existing in refs_dir.glob("*.md"): + if existing.name not in ref_files: + existing.unlink() for fname, content in ref_files.items(): (refs_dir / fname).write_text( spdx_ref + content.rstrip("\n") + "\n", encoding="utf-8" ) _copy_skill_images(refs_dir, ref_images.get(fname, [])) + elif refs_dir.is_dir(): + shutil.rmtree(refs_dir) return summary @@ -2003,36 +1793,22 @@ def group_by_directory(pages: list[DocPage]) -> dict[str, list[DocPage]]: def group_individual(pages: list[DocPage]) -> dict[str, list[DocPage]]: - """Each page becomes its own skill.""" - return {page.path.stem: [page] for page in pages} - - -def group_by_content_type(pages: list[DocPage]) -> dict[str, list[DocPage]]: - """Group pages by directory when an area has procedural content.""" - # First pass: group by directory - dir_groups = group_by_directory(pages) - - # Second pass: keep each procedural docs area together. generate_skill() - # decides which page to inline and which sibling pages to defer. - result: dict[str, list[DocPage]] = {} - for cat, group_pages in dir_groups.items(): - has_procedures = any( - CONTENT_TYPE_ROLE.get(p.content_type) == "procedure" for p in group_pages - ) - if has_procedures or len(group_pages) > 1: - result[cat] = group_pages + """Give each procedure page its own skill; collect the rest into reference.""" + groups: dict[str, list[DocPage]] = {} + reference_pages: list[DocPage] = [] + for page in pages: + if page.content_type in PROCEDURE_CONTENT_TYPES: + groups[page.path.stem] = [page] else: - # Individual concept/reference pages become their own skill - for p in group_pages: - result[p.path.stem] = [p] - - return result + reference_pages.append(page) + if reference_pages: + groups["reference"] = reference_pages + return groups STRATEGIES = { "grouped": group_by_directory, "individual": group_individual, - "smart": group_by_content_type, } @@ -2105,10 +1881,9 @@ def main(): formatter_class=argparse.RawDescriptionHelpFormatter, epilog=textwrap.dedent("""\ Strategies: - grouped Group docs by parent directory - individual Each doc page becomes its own skill - smart Group by directory, inline the lowest-priority procedure, - defer siblings + grouped Group docs by parent directory (default) + individual One skill per how_to/get_started/tutorial page; one + reference skill for all other pages Examples: %(prog)s docs/ .agents/skills/ --prefix nemoclaw-user --doc-platform fern-mdx @@ -2129,8 +1904,8 @@ def main(): parser.add_argument( "--strategy", choices=list(STRATEGIES.keys()), - default="smart", - help="Grouping strategy (default: smart)", + default="grouped", + help="Grouping strategy (default: grouped)", ) parser.add_argument( "--doc-platform", diff --git a/test/docs-to-skills.test.py b/test/docs-to-skills.test.py new file mode 100644 index 0000000000..b5e2a7daba --- /dev/null +++ b/test/docs-to-skills.test.py @@ -0,0 +1,70 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""Unit tests for scripts/docs-to-skills.py helpers.""" + +from __future__ import annotations + +import importlib.util +import sys +import unittest +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parents[1] +SCRIPT_PATH = REPO_ROOT / "scripts" / "docs-to-skills.py" + + +def load_docs_to_skills(): + module_name = "docs_to_skills_test_module" + spec = importlib.util.spec_from_file_location(module_name, SCRIPT_PATH) + if spec is None or spec.loader is None: + raise RuntimeError(f"Unable to load {SCRIPT_PATH}") + module = importlib.util.module_from_spec(spec) + sys.modules[module_name] = module + spec.loader.exec_module(module) + return module + + +def make_page(stem: str, *, content_type: str = "how_to", priority: int = 100) -> object: + mod = load_docs_to_skills() + page = mod.DocPage(path=Path(f"docs/example/{stem}.mdx"), raw="") + page.content_type = content_type + page.skill_priority = priority + page.category = "example" + return page + + +class DocsToSkillsHelpersTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.mod = load_docs_to_skills() + + def test_partition_skill_pages_uses_lowest_priority_value(self): + high = make_page("high", priority=10) + low = make_page("low", priority=50) + lead, refs = self.mod.partition_skill_pages([low, high]) + self.assertEqual(lead.path.stem, "high") + self.assertEqual([page.path.stem for page in refs], ["low"]) + + def test_group_individual_splits_procedure_and_reference_pages(self): + procedure = make_page("quickstart", content_type="get_started") + concept = make_page("overview", content_type="concept") + reference = make_page("commands", content_type="reference") + groups = self.mod.group_individual([procedure, concept, reference]) + self.assertEqual(groups["quickstart"], [procedure]) + self.assertEqual( + {page.path.stem for page in groups["reference"]}, + {"overview", "commands"}, + ) + + def test_group_by_directory_keeps_siblings_together(self): + page_a = make_page("a") + page_b = make_page("b") + page_a.category = "get-started" + page_b.category = "get-started" + groups = self.mod.group_by_directory([page_a, page_b]) + self.assertEqual(len(groups["get-started"]), 2) + + +if __name__ == "__main__": + unittest.main() From 5864b8c2101db806c1cc461483aca61b2a837dbe Mon Sep 17 00:00:00 2001 From: Miyoung Choi Date: Fri, 29 May 2026 15:22:31 -0700 Subject: [PATCH 2/3] fix: refactor more --- .../SKILL.md | 2 +- .../nemoclaw-user-configure-security/SKILL.md | 510 +---------------- .../references/best-practices.md | 511 ++++++++++++++++++ .../references/credential-storage.md | 2 +- .../references/openclaw-controls.md | 2 +- .../nemoclaw-user-deploy-remote/SKILL.md | 2 +- .../skills/nemoclaw-user-get-started/SKILL.md | 2 +- .../nemoclaw-user-manage-policy/SKILL.md | 2 +- .../nemoclaw-user-manage-sandboxes/SKILL.md | 2 +- .../skills/nemoclaw-user-overview/SKILL.md | 98 +--- .../references/ecosystem.md | 94 ++++ .../references/how-it-works.md | 2 +- .../references/overview.md | 2 +- .../skills/nemoclaw-user-reference/SKILL.md | 260 +-------- .../references/architecture.md | 260 +++++++++ docs/CONTRIBUTING.md | 5 +- docs/about/overview.mdx | 2 + docs/resources/agent-skills.mdx | 2 +- scripts/docs-to-skills.py | 215 +++++--- test/docs-to-skills.test.py | 49 +- 20 files changed, 1071 insertions(+), 953 deletions(-) create mode 100644 .agents/skills/nemoclaw-user-configure-security/references/best-practices.md create mode 100644 .agents/skills/nemoclaw-user-overview/references/ecosystem.md create mode 100644 .agents/skills/nemoclaw-user-reference/references/architecture.md diff --git a/.agents/skills/nemoclaw-user-configure-inference/SKILL.md b/.agents/skills/nemoclaw-user-configure-inference/SKILL.md index 07f7a20d98..af6255ceda 100644 --- a/.agents/skills/nemoclaw-user-configure-inference/SKILL.md +++ b/.agents/skills/nemoclaw-user-configure-inference/SKILL.md @@ -1,6 +1,6 @@ --- name: "nemoclaw-user-configure-inference" -description: "Lists all inference providers offered during NemoClaw onboarding. Use when explaining which providers are available, what the onboard wizard presents, or how inference routing works. Trigger keywords - nemoclaw inference options, nemoclaw onboarding providers, nemoclaw inference routing, nemoclaw additional model, nemoclaw sub-agent model, openclaw sub-agent, agents.list, sessions_spawn, vlm-demo, switch nemoclaw inference model, change inference runtime, nemoclaw tool calling, ollama tool calls, vllm tool-call-parser, raw json in tui, nemoclaw local inference, ollama nemoclaw, vllm nemoclaw, local model server, openai compatible endpoint." +description: "Connects NemoClaw to a local inference server. Use when setting up Ollama, vLLM, TensorRT-LLM, NIM, or any OpenAI-compatible local model server with NemoClaw. Trigger keywords - nemoclaw local inference, ollama nemoclaw, vllm nemoclaw, local model server, openai compatible endpoint, switch nemoclaw inference model, change inference runtime, nemoclaw additional model, nemoclaw sub-agent model, openclaw sub-agent, agents.list, sessions_spawn, vlm-demo, nemoclaw inference options, nemoclaw onboarding providers, nemoclaw inference routing, nemoclaw tool calling, ollama tool calls, vllm tool-call-parser, raw json in tui." license: "Apache-2.0" --- diff --git a/.agents/skills/nemoclaw-user-configure-security/SKILL.md b/.agents/skills/nemoclaw-user-configure-security/SKILL.md index 2d00a28a84..d4e22bf269 100644 --- a/.agents/skills/nemoclaw-user-configure-security/SKILL.md +++ b/.agents/skills/nemoclaw-user-configure-security/SKILL.md @@ -7,516 +7,10 @@ license: "Apache-2.0" -# NemoClaw Security Best Practices: Controls, Risks, and Posture Profiles - -NemoClaw ships with deny-by-default security controls across four layers: network, filesystem, process, and inference. -You can tune every control, but each change shifts the risk profile. -This page documents every configurable knob, its default, what it protects, the concrete risk of relaxing it, and a recommendation for common use cases. - -For background on how the layers fit together, refer to How It Works (use the `nemoclaw-user-overview` skill). - -## Protection Layers at a Glance - -NemoClaw enforces security at four layers. -NemoClaw locks some when it creates the sandbox and requires a restart to change them. -You can hot-reload others while the sandbox runs. - -The following diagram shows the default posture immediately after `nemoclaw onboard`, before you approve any endpoints or apply any presets. - -```mermaid -flowchart TB - subgraph HOST["Your Machine: default posture after nemoclaw onboard"] - direction TB - - YOU["👤 Operator"] - - subgraph NC["NemoClaw + OpenShell"] - direction TB - - subgraph SB["Sandbox: the agent's isolated world"] - direction LR - PROC["⚙️ Process Layer
Controls what the agent can execute"] - FS["📁 Filesystem Layer
Controls what the agent can read and write"] - AGENT["🤖 Agent"] - end - - subgraph GW["Gateway: the gatekeeper"] - direction LR - NET["🌐 Network Layer
Controls where the agent can connect"] - INF["🧠 Inference Layer
Controls which AI models the agent can use"] - end - end - end - - OUTSIDE["🌍 Outside World
Internet · AI Providers · APIs"] - - AGENT -- "all requests" --> GW - GW -- "approved only" --> OUTSIDE - YOU -. "approve / deny" .-> GW - - classDef agent fill:#76b900,stroke:#5a8f00,color:#fff,stroke-width:2px,font-weight:bold - classDef locked fill:#1a1a1a,stroke:#76b900,color:#fff,stroke-width:2px - classDef hot fill:#333,stroke:#76b900,color:#e6f2cc,stroke-width:2px - classDef external fill:#f5f5f5,stroke:#ccc,color:#1a1a1a,stroke-width:1px - classDef operator fill:#fff,stroke:#76b900,color:#1a1a1a,stroke-width:2px,font-weight:bold - - class AGENT agent - class PROC,FS locked - class NET,INF hot - class OUTSIDE external - class YOU operator - - style HOST fill:none,stroke:#76b900,stroke-width:2px,color:#1a1a1a - style NC fill:none,stroke:#76b900,stroke-width:1px,stroke-dasharray:5 5,color:#1a1a1a - style SB fill:#f5faed,stroke:#76b900,stroke-width:2px,color:#1a1a1a - style GW fill:#2a2a2a,stroke:#76b900,stroke-width:2px,color:#fff -``` - -| Layer | What it protects | Enforcement point | Changeable at runtime | -| --- | --- | --- | --- | -| Network | Unauthorized outbound connections and data exfiltration. | OpenShell gateway | Yes. Use `openshell policy set` or operator approval. | -| Filesystem | System binary tampering, credential theft, config manipulation. | Landlock LSM + container mounts | Landlock layout: no. Requires sandbox re-creation. Use host-side NemoClaw commands for durable config changes. | -| Process | Privilege escalation, fork bombs, syscall abuse. | Container runtime (Docker/K8s `securityContext`) | No. Requires sandbox re-creation. | -| Inference | Credential exposure, unauthorized model access, cost overruns. | OpenShell gateway | Yes. Use `nemoclaw inference set`. | - -## Network Controls - -NemoClaw controls which hosts, ports, and HTTP methods the sandbox can reach, and lets operators approve or deny requests in real time. - -### Deny-by-Default Egress - -The sandbox blocks all outbound connections unless you explicitly list the endpoint in the policy file `nemoclaw-blueprint/policies/openclaw-sandbox.yaml`. - -| Aspect | Detail | -|---|---| -| Default | All egress denied. Only endpoints in the baseline policy can receive traffic. | -| What you can change | Add endpoints to the policy file (static) or with `openshell policy set` (dynamic). | -| Risk if relaxed | Each allowed endpoint is a potential data exfiltration path. The agent can send workspace content, credentials, or conversation history to any reachable host. | -| Recommendation | Add only endpoints the agent needs for its task. Prefer operator approval for one-off requests over permanently widening the baseline. | - -### Binary-Scoped Endpoint Rules - -Each network policy entry restricts which executables can reach the endpoint using the `binaries` field. - -OpenShell identifies the calling binary by reading `/proc//exe` (the kernel-trusted executable path, not `argv[0]`), walking the process tree for ancestor binaries, and computing a SHA256 hash of each binary on first use. -If someone replaces a binary while the sandbox runs, the hash mismatch triggers an immediate deny. - -| Aspect | Detail | -|---|---| -| Default | Each endpoint restricts access to specific binaries. For example, the `github` preset restricts access so only `/usr/bin/git` can reach `github.com`. Binary paths support glob patterns (`*` matches one path component, `**` matches recursively). | -| What you can change | Add binaries to an endpoint entry, or omit the `binaries` field to allow any executable. | -| Risk if relaxed | Removing binary restrictions lets any process in the sandbox reach the endpoint. An agent could use `curl`, `wget`, or a Python script to exfiltrate data to an allowed host, bypassing the intended usage pattern. | -| Recommendation | Always scope endpoints to the binaries that need them. If the agent needs a host from a new binary, add that binary explicitly rather than removing the restriction. | - -### Path-Scoped HTTP Rules - -Endpoint rules restrict allowed HTTP methods and URL paths. - -| Aspect | Detail | -|---|---| -| Default | Some endpoints allow GET and POST on `/**` (for example, `clawhub.ai`). Others restrict methods and paths to specific API routes (for example, `integrate.api.nvidia.com` allows POST only to inference and embedding paths and GET to model listings). Read-only endpoints such as `docs.openclaw.ai`, the `npm_registry` baseline entry, and the `pypi` preset allow GET only (PyPI also allows HEAD). The `npm` preset is an intentional exception: npm/Yarn registry traffic uses L4 pass-through for Node 22 undici CONNECT compatibility. | -| What you can change | Add methods (PUT, DELETE, PATCH) or restrict paths to specific prefixes. | -| Risk if relaxed | Allowing all methods on an API endpoint gives the agent write and delete access. For example, allowing DELETE on `api.github.com` lets the agent delete repositories. | -| Recommendation | Use GET-only rules for endpoints that the agent only reads. Add write methods only for endpoints where the agent must create or modify resources. Restrict paths to specific API routes when possible. | - -### L4-Only vs L7 Inspection (`protocol` Field) - -All sandbox egress goes through OpenShell's CONNECT proxy. -The `protocol` field on an endpoint controls whether the proxy also inspects individual HTTP requests inside the tunnel. - -| Aspect | Detail | -|---|---| -| Default | Endpoints without a `protocol` field use L4-only enforcement: the proxy checks host, port, and binary identity, then relays the TCP stream without inspecting payloads. Setting `protocol: rest` enables L7 inspection: the proxy auto-detects and terminates TLS, then evaluates each HTTP request's method and path against the endpoint's `rules` or `access` preset. | -| What you can change | Add `protocol: rest` to an endpoint to enable per-request HTTP inspection. Use the `access` preset (`full`, `read-only`, `read-write`) or explicit `rules` to control allowed methods and paths. | -| Risk if relaxed | L4-only endpoints (no `protocol` field) allow the agent to send any data through the tunnel after the initial connection is permitted. The proxy cannot see or filter the HTTP method, path, or body. The `access: full` preset with `protocol: rest` enables inspection but allows all methods and paths, so it does not restrict what the agent can do at the HTTP level. | -| Recommendation | Use `protocol: rest` with specific `rules` for REST APIs where you want method and path control. Use `protocol: rest` with `access: read-only` for read-only endpoints. Omit `protocol` only for non-HTTP protocols (WebSocket, gRPC streaming), endpoints that do not need HTTP inspection, or documented compatibility exceptions that require a client-managed CONNECT tunnel. | - -### Operator Approval Flow - -When the agent reaches an unlisted endpoint, OpenShell blocks the request and prompts the operator in the TUI. - -| Aspect | Detail | -|---|---| -| Default | Enabled. The gateway blocks all unlisted endpoints and requires approval. | -| What you can change | The system merges approved endpoints into the sandbox's policy as a new durable revision. They persist across sandbox restarts within the same sandbox instance. However, when you destroy and recreate the sandbox (for example, by running `nemoclaw onboard`), the policy resets to the baseline defined in the blueprint. | -| Risk if relaxed | Approving an endpoint permanently widens the running sandbox's policy. If you approve a broad domain (such as a CDN that hosts arbitrary content), the agent can fetch anything from that domain until you destroy and recreate the sandbox. | -| Recommendation | Review each blocked request before approving. If you find yourself approving the same endpoint repeatedly, add it to the baseline policy with appropriate binary and path restrictions. To reset approved endpoints, destroy and recreate the sandbox. | - -### Policy Presets - -NemoClaw ships preset policy files in `nemoclaw-blueprint/policies/presets/` for common integrations. - -| Preset | What it enables | Key risk | -|---|---|---| -| `brave` | Brave Search API. | Agent can issue search queries. | -| `brew` | Homebrew (Linuxbrew) package manager. The sandbox base image includes the `brew` binary; this preset opens network egress to GitHub and the Homebrew formulae index so `brew install` can fetch bottles. | Allows installing arbitrary Homebrew packages, which may contain malicious code. | -| `discord` | Discord REST API, WebSocket gateway, CDN. | CDN endpoint (`cdn.discordapp.com`) allows GET to any path. WebSocket uses `access: full` (no inspection). | -| `github` | GitHub and GitHub REST API. | Gives agent read/write access to repositories and issues via `git`. | -| `huggingface` | Hugging Face Hub (download-only) and inference router. | Allows downloading arbitrary models and datasets. POST is restricted to the inference router only. | -| `jira` | Atlassian Jira API. | Gives agent read/write access to project issues and comments. | -| `local-inference` | Local Ollama and vLLM through the host gateway. | Allows sandbox access to host-side local inference ports covered by the preset. | -| `npm` | npm and Yarn registries via L4 pass-through. | Allows installing arbitrary npm packages, which may contain malicious code. OpenShell still gates by host, port, and binary, but does not inspect HTTP method, path, or body for this preset. | -| `outlook` | Microsoft 365, Outlook. | Gives agent access to email. | -| `pypi` | Python Package Index (GET and HEAD only). | Allows installing arbitrary Python packages, which may contain malicious code. Publishing is blocked. | -| `slack` | Slack API, Socket Mode, webhooks. | WebSocket uses `access: full`. Agent can post to any channel the bot token has access to. | -| `telegram` | Telegram Bot API. | Agent can send messages to any chat the bot token has access to. | - -**Recommendation:** Apply presets only when the agent's task requires the integration. Review the preset's YAML file before applying to understand the endpoints, methods, and binary restrictions it adds. - -## Filesystem Controls - -NemoClaw restricts which paths the agent can read and write, protecting system binaries, configuration files, and gateway credentials. - -### Read-Only System Paths - -The container mounts system directories read-only to prevent the agent from modifying binaries, libraries, or configuration files. - -| Aspect | Detail | -|---|---| -| Default | `/usr`, `/lib`, `/proc`, `/dev/urandom`, `/app`, `/etc`, `/var/log` are read-only. | -| What you can change | Add or remove paths in the `filesystem_policy.read_only` section of the policy file. | -| Risk if relaxed | Making `/usr` or `/lib` writable lets the agent replace system binaries (such as `curl` or `node`) with trojanized versions. Making `/etc` writable lets the agent modify DNS resolution, TLS trust stores, or user accounts. | -| Recommendation | Never make system paths writable. If the agent needs a writable location for generated files, use a subdirectory of `/sandbox`. | - -### Agent Config Directory - -The `/sandbox/.openclaw` directory contains the OpenClaw gateway configuration (model routing, CORS settings, channel config). -The current entrypoint reads the gateway auth token from OpenClaw config when present, exports it as `OPENCLAW_GATEWAY_TOKEN`, and writes it to `/tmp/nemoclaw-proxy-env.sh` so interactive sandbox sessions can reach the gateway through system-wide shell hooks. -In root mode, the gateway process still runs as the separate `gateway` user, but the token is intentionally available to sandbox shells for local gateway access. - -Writable agent state such as plugins, skills, hooks, and workspace metadata lives directly under `/sandbox/.openclaw`. - -By default, this directory starts writable so the agent can manage its own config, install skills, and write to standard home-directory paths natively. -For sensitive workloads, use a reviewed host-side immutability workflow after initial setup so config and writable state entry points cannot be changed by the sandbox user. - -- **DAC permissions (default).** The sandbox user owns `/sandbox/.openclaw` with mode `2770` (setgid `sandbox:sandbox`) and `openclaw.json` with mode `660`, so the agent and its group can read and write config directly. A reviewed host-side immutability workflow should compare the intended ownership and mode with the live sandbox filesystem before treating the config tree as locked. -- **Config integrity hash.** The image includes a SHA256 hash of `openclaw.json`. In the default mutable state, `.config-hash` is sandbox-owned and is not a tamper-proof trust anchor, so startup does not fail closed on that hash. When the hash is root-owned and read-only, startup enforces it and refuses to start if the hash does not match. -- **Gateway token environment.** The gateway exports `OPENCLAW_GATEWAY_TOKEN` and writes it to `/tmp/nemoclaw-proxy-env.sh` for interactive sandbox sessions. Keep this in mind when deciding whether a workload should run with mutable config or an immutable config posture. - -| Aspect | Detail | -|---|---| -| Default | The sandbox keeps `/sandbox/.openclaw` writable (`2770 sandbox:sandbox`), sets `openclaw.json` to `660 sandbox:sandbox`, lets the agent manage state directly, and has the gateway place `OPENCLAW_GATEWAY_TOKEN` in `/tmp/nemoclaw-proxy-env.sh` for interactive shells. | -| What you can change | Apply a reviewed host-side immutability workflow to lock config and state directories with DAC permissions and the immutable flag where available. | -| Risk of default | A writable `.openclaw` directory lets the agent modify its own gateway config: disabling CORS or redirecting inference to an attacker-controlled endpoint. | -| Recommendation | For always-on assistants handling sensitive workloads, lock config after initial setup. For development workflows, the writable default is appropriate. | - -### Writable Paths - -The agent has read-write access to `/sandbox`, `/tmp`, and `/dev/null`. - -| Aspect | Detail | -|---|---| -| Default | `/sandbox` (agent workspace), `/tmp` (temporary files), `/dev/null`. | -| What you can change | Add additional writable paths in `filesystem_policy.read_write`. | -| Risk if relaxed | Each additional writable path expands the agent's ability to persist data and potentially modify system behavior. Adding `/var` lets the agent write to log directories. Adding `/home` gives access to other user directories. | -| Recommendation | Keep writable paths to `/sandbox` and `/tmp`. If the agent needs a persistent working directory, create a subdirectory under `/sandbox`. | - -### Landlock LSM Enforcement - -Landlock is a Linux Security Module that enforces filesystem access rules at the kernel level. - -| Aspect | Detail | -|---|---| -| Default | `compatibility: best_effort`. The entrypoint applies Landlock rules when the kernel supports them and silently skips them on older kernels. | -| What you can change | This is a NemoClaw default, not a user-facing knob. | -| Risk if relaxed | On kernels without Landlock support (pre-5.13), filesystem restrictions rely solely on container mount configuration, which is less granular. | -| Recommendation | Run on a kernel that supports Landlock (5.13+). Ubuntu 22.04 LTS and later include Landlock support. | - -## Process Controls - -NemoClaw limits the capabilities, user privileges, and resource quotas available to processes inside the sandbox. - -### Capability Drops - -The entrypoint drops dangerous Linux capabilities from the bounding set at startup using `capsh`. -This limits what capabilities any child process (gateway, sandbox, agent) can ever acquire. -When the entrypoint switches from root to the `sandbox` and `gateway` users, it uses `setpriv` when available to remove the remaining privilege-separation capabilities from the child process at the same time as the user change. - -The initial entrypoint drop removes `cap_sys_admin`, `cap_sys_ptrace`, `cap_net_raw`, `cap_dac_override`, `cap_sys_chroot`, `cap_fsetid`, `cap_setfcap`, `cap_mknod`, `cap_audit_write`, and `cap_net_bind_service`. -During `setpriv` step-down, the child process also loses `cap_setuid`, `cap_setgid`, `cap_fowner`, `cap_chown`, and `cap_kill`. - -This is best-effort: if `capsh` is not available or `CAP_SETPCAP` is not in the bounding set, the entrypoint logs a warning and continues with the default capability set. -If `setpriv` is unavailable, the entrypoint falls back to `gosu` and logs a warning that the remaining bounding-set capabilities were retained for the child process. -For additional protection, pass `--cap-drop=ALL` with `docker run` or Compose (see Sandbox Hardening (use the `nemoclaw-user-deploy-remote` skill)). - -| Aspect | Detail | -|---|---| -| Default | The entrypoint drops dangerous capabilities at startup using `capsh`, then uses `setpriv` during user step-down when possible. Best-effort. | -| What you can change | When launching with `docker run` directly, pass `--cap-drop=ALL --cap-add=NET_BIND_SERVICE` for stricter enforcement. In the standard NemoClaw flow (with `nemoclaw onboard`), the entrypoint handles capability dropping automatically. | -| Risk if relaxed | `CAP_SYS_ADMIN` and `CAP_SYS_PTRACE` expand kernel and process attack surface. `CAP_NET_RAW` allows raw socket access for network sniffing. `CAP_DAC_OVERRIDE` bypasses filesystem permission checks. If `capsh` or `setpriv` cannot run, the container retains more of the runtime-provided capability set. | -| Recommendation | Run on an image that includes `capsh` and `setpriv` (the NemoClaw image includes them). For defense-in-depth, also pass `--cap-drop=ALL` at the container runtime level. | - -### Gateway Process Isolation - -The OpenClaw gateway runs as a separate `gateway` user, not as the `sandbox` user that runs the agent. - -| Aspect | Detail | -|---|---| -| Default | The entrypoint starts the gateway process using `gosu gateway`, isolating it from the agent's `sandbox` user. | -| What you can change | This is not a user-facing knob. The entrypoint enforces it when running as root. In non-root mode (when OpenShell sets `no-new-privileges`), gateway process isolation does not work because `gosu` cannot change users. | -| Risk if relaxed | If the gateway and agent run as the same user, the agent can kill the gateway process and restart it with a tampered configuration (the "fake-HOME" attack). | -| Recommendation | No action needed. The entrypoint handles this automatically. Be aware that non-root mode disables this isolation. | - -### No New Privileges - -The `no-new-privileges` flag prevents processes from gaining additional privileges through setuid binaries or capability inheritance. - -| Aspect | Detail | -|---|---| -| Default | OpenShell sets `PR_SET_NO_NEW_PRIVS` using `prctl()` inside the sandbox process as part of the seccomp filter setup. The NemoClaw Compose example also shows the equivalent `security_opt: no-new-privileges:true` setting. | -| What you can change | OpenShell's seccomp path enforces this inside the sandbox. It is not a user-facing knob. | -| Risk if relaxed | Without this flag, a compromised process could execute a setuid binary to escalate to root inside the container, then attempt container escape techniques. | -| Recommendation | No action needed. OpenShell enforces this automatically when the sandbox network policy is active. This flag prevents `gosu` from switching users, so non-root mode disables gateway process isolation in the NemoClaw entrypoint. | - -### Process Limit - -A process limit caps the number of processes the sandbox user can spawn. -The entrypoint sets both soft and hard limits using `ulimit -u 512`. -This is best-effort: if the container runtime restricts `ulimit` modification, the entrypoint logs a security warning and continues without the limit. - -| Aspect | Detail | -|---|---| -| Default | 512 processes (`ulimit -u 512`), best-effort. | -| What you can change | Increase or decrease the limit with `--ulimit nproc=N:N` in `docker run` or the `ulimits` section in Compose. The runtime-level ulimit takes precedence over the entrypoint's setting. | -| Risk if relaxed | Removing or raising the limit makes the sandbox vulnerable to fork-bomb attacks, where a runaway process spawns children until the host runs out of resources. If the entrypoint cannot set the limit (logs `[SECURITY] Could not set soft/hard nproc limit`), the container runs without process limits. | -| Recommendation | Keep the default at 512. If the agent runs workloads that spawn many child processes (such as parallel test runners), increase to 1024 and monitor host resource usage. If the entrypoint logs a warning about ulimit restrictions, set the limit through the container runtime instead. | - -### Non-Root User - -The sandbox runs agent processes as a dedicated `sandbox` user and group. -The entrypoint starts as root for privilege separation, then drops to the `sandbox` user for all agent commands. - -| Aspect | Detail | -|---|---| -| Default | `run_as_user: sandbox`, `run_as_group: sandbox`. A separate `gateway` user runs the gateway process. | -| What you can change | Change the `process` section in the policy file to run as a different user. | -| Risk if relaxed | Running as `root` inside the container gives the agent access to modify any file in the container filesystem and increases the impact of container escape vulnerabilities. | -| Recommendation | Never run as root. Keep the `sandbox` user. | - -### PATH Hardening - -The entrypoint locks the `PATH` environment variable to system directories, preventing the agent from injecting malicious binaries into command resolution. - -| Aspect | Detail | -|---|---| -| Default | The entrypoint sets `PATH` to `/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin` at startup. | -| What you can change | This is not a user-facing knob. The entrypoint enforces it. | -| Risk if relaxed | Without PATH hardening, the agent could create an executable named `curl` or `git` in a writable directory earlier in the PATH, intercepting commands run by the entrypoint or other processes. | -| Recommendation | No action needed. The entrypoint handles this automatically. | - -### Build Toolchain Removal - -The Dockerfile removes compilers and network probes from the runtime image. - -| Aspect | Detail | -|---|---| -| Default | The Dockerfile purges `gcc`, `gcc-12`, `g++`, `g++-12`, `cpp`, `cpp-12`, `make`, `netcat-openbsd`, `netcat-traditional`, and `ncat` from the sandbox image. | -| What you can change | Modify the Dockerfile to keep these tools, or install them at runtime if package manager access is allowed. | -| Risk if relaxed | A compiler lets the agent build arbitrary native code, including kernel exploits or custom network tools. `netcat` enables arbitrary TCP connections that bypass HTTP-level policy enforcement. | -| Recommendation | Keep build tools removed. If the agent needs to compile code, run the build in a separate, purpose-built container and copy artifacts into the sandbox. | - -### Image Digest Pinning - -The blueprint references the sandbox image by an immutable `@sha256:` digest instead of a mutable tag such as `:latest`. -A registry compromise or accidental force-push cannot silently swap the sandbox image. - -| Aspect | Detail | -|---|---| -| Default | `nemoclaw-blueprint/blueprint.yaml` pins the sandbox image by digest. A CI regression test blocks any mutable-tag reference from merging. | -| What you can change | Contributors bumping the sandbox image must update the digest in `blueprint.yaml`. Release tooling should rewrite the digest automatically. | -| Risk if relaxed | Reverting to a mutable tag (`:latest`) allows a registry-side change to replace the sandbox image without any blueprint update, which is a supply-chain risk. | -| Recommendation | Always reference the sandbox image by digest. If you build a custom image with `nemoclaw onboard --from`, the digest constraint does not apply to your local build. | - -### Auth Profile Permissions - -The entrypoint and migration flows enforce `chmod 600` on all `auth-profiles.json` files under `~/.openclaw`. -This prevents other users on the host from reading stored credentials. - -| Aspect | Detail | -|---|---| -| Default | `600` permissions applied recursively at startup and after migration restores. | -| What you can change | This is not a user-facing knob. The entrypoint enforces it. | -| Risk if relaxed | Looser permissions let other users or processes on the host read provider API keys and tokens stored in auth profiles. | -| Recommendation | No action needed. If you see a `permission denied` error when reading auth profiles, verify that you are running as the same user who created them. | - -## Gateway Authentication Controls - -The OpenClaw gateway authenticates devices that connect to the Control UI dashboard. -NemoClaw hardens these defaults at image build time. - -### Device Authentication - -Device authentication requires each connecting device to go through a pairing flow before it can interact with the gateway. - -| Aspect | Detail | -|---|---| -| Default | Enabled. The gateway requires device pairing for all connections. | -| What you can change | Set `NEMOCLAW_DISABLE_DEVICE_AUTH=1` as a Docker build argument to disable device authentication. This is a build-time setting baked into `openclaw.json` and verified by hash at startup. | -| Risk if relaxed | Disabling device auth allows any device on the network to connect to the gateway without proving identity. This is dangerous when combined with LAN-bind changes or cloudflared tunnels in remote deployments, resulting in an unauthenticated, publicly reachable dashboard. | -| Recommendation | Keep device auth enabled (the default). Only disable it for headless or development environments where no untrusted devices can reach the gateway. | - -### Gateway Bind Address - -NemoClaw binds the OpenShell gateway to loopback by default. - -| Aspect | Detail | -|---|---| -| Default | `NEMOCLAW_GATEWAY_BIND_ADDRESS=127.0.0.1`. | -| What you can change | Set `NEMOCLAW_GATEWAY_BIND_ADDRESS=0.0.0.0` before onboarding to listen on all IPv4 interfaces. | -| Risk if relaxed | Other hosts on the network may be able to reach the OpenShell gateway. | -| Recommendation | Keep the loopback default unless the gateway must be reachable from another host. | - -### Insecure Auth Derivation - -The `allowInsecureAuth` setting controls whether the gateway permits non-HTTPS authentication. - -| Aspect | Detail | -|---|---| -| Default | Derived from the `CHAT_UI_URL` scheme at build time. When the URL uses `http://` (local development), insecure auth is allowed. When it uses `https://` (remote or production), insecure auth is blocked. | -| What you can change | This is derived automatically from `CHAT_UI_URL`. Set `CHAT_UI_URL` to an `https://` URL to enforce secure auth. | -| Risk if relaxed | Allowing insecure auth over HTTPS defeats the purpose of TLS, because authentication tokens transit in cleartext. | -| Recommendation | Use `https://` for any deployment accessible beyond `localhost`. The default local URL (`http://127.0.0.1:18789`) correctly allows insecure auth for local development. | - -### Auto-Pair Client Allowlist - -The auto-pair watcher automatically approves device pairing requests from recognized clients, so you do not need to manually approve the Control UI. - -| Aspect | Detail | -|---|---| -| Default | The watcher approves devices with `clientId` set to `openclaw-control-ui` or `clientMode` set to `webchat`. All other clients are rejected and logged. | -| What you can change | This is not a user-facing knob. The allowlist is defined in the entrypoint script. | -| Risk if relaxed | Approving all device types without validation lets rogue or unexpected clients pair with the gateway unchallenged. | -| Recommendation | No action needed. The entrypoint handles this automatically. If you see `[auto-pair] rejected unknown client=...` in the logs, investigate the source of the unexpected connection. | - -### CLI Secret Redaction - -The CLI automatically redacts secret patterns (API keys, bearer tokens, provider credentials) from command output and error messages before logging them. - -| Aspect | Detail | -|---|---| -| Default | Enabled. The runner redacts secrets from stdout, stderr, and thrown error messages. | -| What you can change | This is not a user-facing knob. The CLI enforces it on all command output paths. | -| Risk if relaxed | Without redaction, secrets could appear in terminal scrollback, log files, or debug output shared in bug reports. | -| Recommendation | No action needed. If you share `nemoclaw debug` output, verify that no secrets appear in the collected diagnostics. | - -### Memory Secret Scanner - -The NemoClaw plugin blocks the agent from writing likely secrets (API keys, tokens, private keys) into persistent memory files. -The scanner intercepts Write, Edit, and similar tool calls targeting memory and workspace paths before they reach disk. - -| Aspect | Detail | -|---|---| -| Default | Enabled. The plugin registers a `before_tool_call` hook that scans for 14 high-confidence secret patterns. | -| What it covers | Three classifiers, all enforced through `isMemoryPath()`: (1) absolute `MEMORY_PATH_SEGMENTS` such as `/.openclaw/memory/`, `/.openclaw/workspace/`, `/.openclaw/agents/`, `/.openclaw/skills/`, `/.openclaw/hooks/`, `/.openclaw/credentials/`, `/.openclaw/openclaw.json`, `/.nemoclaw/`; (2) canonical workspace basenames in `MEMORY_BASENAMES` (`IDENTITY.md`, `MEMORY.md`, `SOUL.md`, `USER.md`, `AGENTS.md`) matched regardless of the surrounding path; and (3) lexically-normalized workspace-relative writes matching `MEMORY_RELATIVE_PREFIXES` (`.openclaw/`, `.nemoclaw/`, `memory/`) or named workspace daily memory paths, for embedded-fallback mode where the host's path resolver is unavailable. | -| What you can change | This is not a user-facing knob. The plugin enforces it automatically. | -| Risk if relaxed | Without scanning, the agent could persist API keys or tokens in memory files that survive across sessions and backups. | -| Recommendation | No action needed. If a write is blocked, the agent receives an actionable error listing the detected patterns. | - -## Inference Controls - -OpenShell routes all inference traffic through the gateway to isolate provider credentials from the sandbox. - -### Routed Inference through `inference.local` - -The OpenShell gateway intercepts all inference requests from the agent and routes them to the configured provider. -The agent never receives the provider API key. - -| Aspect | Detail | -|---|---| -| Default | The agent talks to `inference.local`. The host owns the credential and upstream endpoint. | -| What you can change | You cannot configure this architecture. The system always enforces it. | -| Risk if bypassed | If the agent could reach an inference endpoint directly (by adding it to the network policy), it would need an API key. Since the sandbox does not contain credentials, this acts as defense-in-depth. However, adding an inference provider's host to the network policy without going through OpenShell routing could let the agent use a stolen or hardcoded key. | -| Recommendation | Do not add inference provider hosts (such as `api.openai.com` or `api.anthropic.com`) to the network policy. Use OpenShell inference routing instead. | - -### Provider Trust Tiers - -Different inference providers have different trust and cost profiles. - -| Provider | Trust level | Cost risk | Data handling | -|---|---|---|---| -| NVIDIA Endpoints | High. Hosted on `build.nvidia.com`. | Pay-per-token with an API key. Unattended agents can accumulate cost. | NVIDIA infrastructure processes requests. | -| OpenAI | High. Commercial API. | Pay-per-token. Same cost risk as NVIDIA Endpoints. | Subject to OpenAI data policies. | -| Anthropic | High. Commercial API. | Pay-per-token. Same cost risk as NVIDIA Endpoints. | Subject to Anthropic data policies. | -| Google Gemini | High. Commercial API. | Pay-per-token. Same cost risk as NVIDIA Endpoints. | Subject to Google data policies. | -| Local Ollama | Self-hosted. No data leaves the machine. | No per-token cost. GPU/CPU resource cost. | Data stays local. | -| Custom compatible endpoint | Varies. Depends on the proxy or gateway. | Varies. | Depends on the endpoint operator. | - -**Recommendation:** For sensitive workloads, use local Ollama to keep data on-premise. For general use, NVIDIA Endpoints provide a good balance of capability and trust. Review the data policies of any cloud provider you use. - -### Experimental Providers - -The `NEMOCLAW_EXPERIMENTAL=1` environment variable gates local NVIDIA NIM and generic Linux managed vLLM install/start. DGX Spark and DGX Station managed vLLM entries are offered by default, and an already-running vLLM server on `localhost:8000` is offered in the menu without a flag, because selecting either is an explicit user action. - -| Aspect | Detail | -|---|---| -| Default | Local NVIDIA NIM and generic Linux managed vLLM install/start are hidden. DGX Spark and DGX Station managed vLLM entries, plus already-running vLLM on `localhost:8000`, are offered when detected. | -| What you can change | Set `NEMOCLAW_EXPERIMENTAL=1` before running `nemoclaw onboard` to surface Local NIM and generic Linux managed vLLM. To request only the managed vLLM path non-interactively, set `NEMOCLAW_PROVIDER=install-vllm`. | -| Risk if selected | NemoClaw has not fully validated these providers. NIM requires a NIM-capable GPU. The managed vLLM path pulls a container image and starts it on a supported NVIDIA GPU host. Misconfiguration can cause failed inference or unexpected behavior. | -| Recommendation | Use experimental providers only for evaluation. Do not rely on them for always-on assistants. | - -## Posture Profiles - -The following profiles describe how to configure NemoClaw for different use cases. -These are not separate policy files. -They provide guidance on which controls to keep tight or relax. - -### Locked-Down (Default) - -Use for always-on assistants with minimal external access. - -- Keep all defaults. Do not add presets. -- Use operator approval for any endpoint the agent requests. -- Use NVIDIA Endpoints or local Ollama for inference. -- Monitor the TUI for unexpected network requests. - -### Development - -Use when the agent needs package registries, Docker Hub, or broader GitHub access during development tasks. - -- Apply the `pypi` and `npm` presets for package installation. -- Keep binary restrictions on all presets. -- Review the agent's network activity periodically with `openshell term`. -- Use operator approval for any endpoint not covered by a preset. - -### Integration Testing - -Use when the agent talks to internal APIs or third-party services during testing. - -- Add custom endpoint entries with tight path and method restrictions. -- Use `protocol: rest` for all HTTP APIs to maintain inspection. -- Use operator approval for unknown endpoints during test runs. -- Review and clean up the baseline policy after testing. Remove endpoints that are no longer needed. - -## Common Mistakes - -The following patterns weaken security without providing meaningful benefit. - -| Mistake | Why it matters | What to do instead | -|---------|---------------|-------------------| -| Omitting `protocol: rest` on REST API endpoints without a compatibility reason | Endpoints without a `protocol` field use L4-only enforcement. The proxy allows the TCP stream through after checking host, port, and binary, but cannot see or filter individual HTTP requests. | Add `protocol: rest` with explicit `rules` to enable per-request method and path control on REST APIs. Use L4 pass-through only for documented cases such as npm/Yarn on Node 22, where the client requires a CONNECT tunnel that L7 inspection would break. | -| Adding endpoints to the baseline policy for one-off requests | Adding an endpoint to the baseline policy makes it permanently reachable across all sandbox instances. | Use operator approval. Approved endpoints persist within the sandbox instance but reset when you destroy and recreate the sandbox. | -| Relying solely on the entrypoint for capability drops | The entrypoint drops dangerous capabilities using `capsh`, but this is best-effort. If `capsh` is unavailable or `CAP_SETPCAP` is not in the bounding set, the container runs with the default capability set. | Pass `--cap-drop=ALL` at the container runtime level as defense-in-depth. | -| Leaving `/sandbox/.openclaw` writable on sensitive workloads | This directory contains the OpenClaw gateway configuration. A writable `.openclaw` lets the agent disable CORS, redirect inference routing, or weaken gateway protections. | Lock config for always-on assistants handling sensitive data. | -| Adding inference provider hosts to the network policy | Direct network access to an inference host bypasses credential isolation and usage tracking. | Use OpenShell inference routing instead of adding hosts like `api.openai.com` or `api.anthropic.com` to the network policy. | -| Disabling device auth for remote deployments | Without device auth, any device on the network can connect to the gateway without pairing. Combined with a cloudflared tunnel, this makes the dashboard publicly accessible and unauthenticated. | Keep `NEMOCLAW_DISABLE_DEVICE_AUTH` at its default (`0`). Only set it to `1` for local headless or development environments. | - -## Known Limitations - -| Limitation | Impact | Mitigation | -|-----------|--------|------------| -| `openclaw agent --local` bypasses gateway | Secret scanning, network policy, and inference auth are not enforced when the agent runs in local mode. | A runtime warning is emitted when `--local` is detected. Avoid `--local` for production workflows. A future OpenClaw-level hook will close this gap. | -| Direct filesystem writes bypass secret scanner | The scanner intercepts OpenClaw tool calls, not raw filesystem writes (e.g., `echo secret > file`). | Landlock restricts writable paths. The scanner is application-layer defense-in-depth, not a filesystem-level control. | -| Base64/hex-encoded secrets are not detected | Content-based regex scanning cannot detect encoded or obfuscated secrets. | Use environment variables or credential stores instead of writing secrets to files. | +# NemoClaw User Configure Security ## References +- **Load [references/best-practices.md](references/best-practices.md)** when evaluating security posture, reviewing sandbox security defaults, or assessing control trade-offs. Presents a risk framework for every configurable security control in NemoClaw. - **Load [references/credential-storage.md](references/credential-storage.md)** when reviewing how credentials are handled, locating a stored credential, or assessing the storage threat model. Covers where NemoClaw stores provider credentials, why nothing is persisted to host disk, and how the OpenShell gateway acts as the single system of record. - **Load [references/openclaw-controls.md](references/openclaw-controls.md)** when reviewing the security boundary between NemoClaw and OpenClaw or assessing what NemoClaw does not cover. Lists OpenClaw security controls that operate independently of NemoClaw, including prompt injection detection, tool access control, rate limiting, environment variable policy, audit framework, supply chain scanning, messaging access policy, context visibility, and safe regex. - -## Related Skills - -- `nemoclaw-user-reference` — Network Policies (use the `nemoclaw-user-reference` skill) for the full baseline policy reference -- `nemoclaw-user-manage-policy` — Customize the Network Policy (use the `nemoclaw-user-manage-policy` skill) for static and dynamic policy changes -- `nemoclaw-user-deploy-remote` — Sandbox Hardening (use the `nemoclaw-user-deploy-remote` skill) for container-level security measures -- `nemoclaw-user-configure-inference` — Inference Options (use the `nemoclaw-user-configure-inference` skill) for provider configuration details -- `nemoclaw-user-overview` — How It Works (use the `nemoclaw-user-overview` skill) for the protection layer architecture diff --git a/.agents/skills/nemoclaw-user-configure-security/references/best-practices.md b/.agents/skills/nemoclaw-user-configure-security/references/best-practices.md new file mode 100644 index 0000000000..440571e4d2 --- /dev/null +++ b/.agents/skills/nemoclaw-user-configure-security/references/best-practices.md @@ -0,0 +1,511 @@ + + +# NemoClaw Security Best Practices: Controls, Risks, and Posture Profiles + +NemoClaw ships with deny-by-default security controls across four layers: network, filesystem, process, and inference. +You can tune every control, but each change shifts the risk profile. +This page documents every configurable knob, its default, what it protects, the concrete risk of relaxing it, and a recommendation for common use cases. + +For background on how the layers fit together, refer to How It Works (use the `nemoclaw-user-overview` skill). + +## Protection Layers at a Glance + +NemoClaw enforces security at four layers. +NemoClaw locks some when it creates the sandbox and requires a restart to change them. +You can hot-reload others while the sandbox runs. + +The following diagram shows the default posture immediately after `nemoclaw onboard`, before you approve any endpoints or apply any presets. + +```mermaid +flowchart TB + subgraph HOST["Your Machine: default posture after nemoclaw onboard"] + direction TB + + YOU["👤 Operator"] + + subgraph NC["NemoClaw + OpenShell"] + direction TB + + subgraph SB["Sandbox: the agent's isolated world"] + direction LR + PROC["⚙️ Process Layer
Controls what the agent can execute"] + FS["📁 Filesystem Layer
Controls what the agent can read and write"] + AGENT["🤖 Agent"] + end + + subgraph GW["Gateway: the gatekeeper"] + direction LR + NET["🌐 Network Layer
Controls where the agent can connect"] + INF["🧠 Inference Layer
Controls which AI models the agent can use"] + end + end + end + + OUTSIDE["🌍 Outside World
Internet · AI Providers · APIs"] + + AGENT -- "all requests" --> GW + GW -- "approved only" --> OUTSIDE + YOU -. "approve / deny" .-> GW + + classDef agent fill:#76b900,stroke:#5a8f00,color:#fff,stroke-width:2px,font-weight:bold + classDef locked fill:#1a1a1a,stroke:#76b900,color:#fff,stroke-width:2px + classDef hot fill:#333,stroke:#76b900,color:#e6f2cc,stroke-width:2px + classDef external fill:#f5f5f5,stroke:#ccc,color:#1a1a1a,stroke-width:1px + classDef operator fill:#fff,stroke:#76b900,color:#1a1a1a,stroke-width:2px,font-weight:bold + + class AGENT agent + class PROC,FS locked + class NET,INF hot + class OUTSIDE external + class YOU operator + + style HOST fill:none,stroke:#76b900,stroke-width:2px,color:#1a1a1a + style NC fill:none,stroke:#76b900,stroke-width:1px,stroke-dasharray:5 5,color:#1a1a1a + style SB fill:#f5faed,stroke:#76b900,stroke-width:2px,color:#1a1a1a + style GW fill:#2a2a2a,stroke:#76b900,stroke-width:2px,color:#fff +``` + +| Layer | What it protects | Enforcement point | Changeable at runtime | +| --- | --- | --- | --- | +| Network | Unauthorized outbound connections and data exfiltration. | OpenShell gateway | Yes. Use `openshell policy set` or operator approval. | +| Filesystem | System binary tampering, credential theft, config manipulation. | Landlock LSM + container mounts | Landlock layout: no. Requires sandbox re-creation. Use host-side NemoClaw commands for durable config changes. | +| Process | Privilege escalation, fork bombs, syscall abuse. | Container runtime (Docker/K8s `securityContext`) | No. Requires sandbox re-creation. | +| Inference | Credential exposure, unauthorized model access, cost overruns. | OpenShell gateway | Yes. Use `nemoclaw inference set`. | + +## Network Controls + +NemoClaw controls which hosts, ports, and HTTP methods the sandbox can reach, and lets operators approve or deny requests in real time. + +### Deny-by-Default Egress + +The sandbox blocks all outbound connections unless you explicitly list the endpoint in the policy file `nemoclaw-blueprint/policies/openclaw-sandbox.yaml`. + +| Aspect | Detail | +|---|---| +| Default | All egress denied. Only endpoints in the baseline policy can receive traffic. | +| What you can change | Add endpoints to the policy file (static) or with `openshell policy set` (dynamic). | +| Risk if relaxed | Each allowed endpoint is a potential data exfiltration path. The agent can send workspace content, credentials, or conversation history to any reachable host. | +| Recommendation | Add only endpoints the agent needs for its task. Prefer operator approval for one-off requests over permanently widening the baseline. | + +### Binary-Scoped Endpoint Rules + +Each network policy entry restricts which executables can reach the endpoint using the `binaries` field. + +OpenShell identifies the calling binary by reading `/proc//exe` (the kernel-trusted executable path, not `argv[0]`), walking the process tree for ancestor binaries, and computing a SHA256 hash of each binary on first use. +If someone replaces a binary while the sandbox runs, the hash mismatch triggers an immediate deny. + +| Aspect | Detail | +|---|---| +| Default | Each endpoint restricts access to specific binaries. For example, the `github` preset restricts access so only `/usr/bin/git` can reach `github.com`. Binary paths support glob patterns (`*` matches one path component, `**` matches recursively). | +| What you can change | Add binaries to an endpoint entry, or omit the `binaries` field to allow any executable. | +| Risk if relaxed | Removing binary restrictions lets any process in the sandbox reach the endpoint. An agent could use `curl`, `wget`, or a Python script to exfiltrate data to an allowed host, bypassing the intended usage pattern. | +| Recommendation | Always scope endpoints to the binaries that need them. If the agent needs a host from a new binary, add that binary explicitly rather than removing the restriction. | + +### Path-Scoped HTTP Rules + +Endpoint rules restrict allowed HTTP methods and URL paths. + +| Aspect | Detail | +|---|---| +| Default | Some endpoints allow GET and POST on `/**` (for example, `clawhub.ai`). Others restrict methods and paths to specific API routes (for example, `integrate.api.nvidia.com` allows POST only to inference and embedding paths and GET to model listings). Read-only endpoints such as `docs.openclaw.ai`, the `npm_registry` baseline entry, and the `pypi` preset allow GET only (PyPI also allows HEAD). The `npm` preset is an intentional exception: npm/Yarn registry traffic uses L4 pass-through for Node 22 undici CONNECT compatibility. | +| What you can change | Add methods (PUT, DELETE, PATCH) or restrict paths to specific prefixes. | +| Risk if relaxed | Allowing all methods on an API endpoint gives the agent write and delete access. For example, allowing DELETE on `api.github.com` lets the agent delete repositories. | +| Recommendation | Use GET-only rules for endpoints that the agent only reads. Add write methods only for endpoints where the agent must create or modify resources. Restrict paths to specific API routes when possible. | + +### L4-Only vs L7 Inspection (`protocol` Field) + +All sandbox egress goes through OpenShell's CONNECT proxy. +The `protocol` field on an endpoint controls whether the proxy also inspects individual HTTP requests inside the tunnel. + +| Aspect | Detail | +|---|---| +| Default | Endpoints without a `protocol` field use L4-only enforcement: the proxy checks host, port, and binary identity, then relays the TCP stream without inspecting payloads. Setting `protocol: rest` enables L7 inspection: the proxy auto-detects and terminates TLS, then evaluates each HTTP request's method and path against the endpoint's `rules` or `access` preset. | +| What you can change | Add `protocol: rest` to an endpoint to enable per-request HTTP inspection. Use the `access` preset (`full`, `read-only`, `read-write`) or explicit `rules` to control allowed methods and paths. | +| Risk if relaxed | L4-only endpoints (no `protocol` field) allow the agent to send any data through the tunnel after the initial connection is permitted. The proxy cannot see or filter the HTTP method, path, or body. The `access: full` preset with `protocol: rest` enables inspection but allows all methods and paths, so it does not restrict what the agent can do at the HTTP level. | +| Recommendation | Use `protocol: rest` with specific `rules` for REST APIs where you want method and path control. Use `protocol: rest` with `access: read-only` for read-only endpoints. Omit `protocol` only for non-HTTP protocols (WebSocket, gRPC streaming), endpoints that do not need HTTP inspection, or documented compatibility exceptions that require a client-managed CONNECT tunnel. | + +### Operator Approval Flow + +When the agent reaches an unlisted endpoint, OpenShell blocks the request and prompts the operator in the TUI. + +| Aspect | Detail | +|---|---| +| Default | Enabled. The gateway blocks all unlisted endpoints and requires approval. | +| What you can change | The system merges approved endpoints into the sandbox's policy as a new durable revision. They persist across sandbox restarts within the same sandbox instance. However, when you destroy and recreate the sandbox (for example, by running `nemoclaw onboard`), the policy resets to the baseline defined in the blueprint. | +| Risk if relaxed | Approving an endpoint permanently widens the running sandbox's policy. If you approve a broad domain (such as a CDN that hosts arbitrary content), the agent can fetch anything from that domain until you destroy and recreate the sandbox. | +| Recommendation | Review each blocked request before approving. If you find yourself approving the same endpoint repeatedly, add it to the baseline policy with appropriate binary and path restrictions. To reset approved endpoints, destroy and recreate the sandbox. | + +### Policy Presets + +NemoClaw ships preset policy files in `nemoclaw-blueprint/policies/presets/` for common integrations. + +| Preset | What it enables | Key risk | +|---|---|---| +| `brave` | Brave Search API. | Agent can issue search queries. | +| `brew` | Homebrew (Linuxbrew) package manager. The sandbox base image includes the `brew` binary; this preset opens network egress to GitHub and the Homebrew formulae index so `brew install` can fetch bottles. | Allows installing arbitrary Homebrew packages, which may contain malicious code. | +| `discord` | Discord REST API, WebSocket gateway, CDN. | CDN endpoint (`cdn.discordapp.com`) allows GET to any path. WebSocket uses `access: full` (no inspection). | +| `github` | GitHub and GitHub REST API. | Gives agent read/write access to repositories and issues via `git`. | +| `huggingface` | Hugging Face Hub (download-only) and inference router. | Allows downloading arbitrary models and datasets. POST is restricted to the inference router only. | +| `jira` | Atlassian Jira API. | Gives agent read/write access to project issues and comments. | +| `local-inference` | Local Ollama and vLLM through the host gateway. | Allows sandbox access to host-side local inference ports covered by the preset. | +| `npm` | npm and Yarn registries via L4 pass-through. | Allows installing arbitrary npm packages, which may contain malicious code. OpenShell still gates by host, port, and binary, but does not inspect HTTP method, path, or body for this preset. | +| `outlook` | Microsoft 365, Outlook. | Gives agent access to email. | +| `pypi` | Python Package Index (GET and HEAD only). | Allows installing arbitrary Python packages, which may contain malicious code. Publishing is blocked. | +| `slack` | Slack API, Socket Mode, webhooks. | WebSocket uses `access: full`. Agent can post to any channel the bot token has access to. | +| `telegram` | Telegram Bot API. | Agent can send messages to any chat the bot token has access to. | + +**Recommendation:** Apply presets only when the agent's task requires the integration. Review the preset's YAML file before applying to understand the endpoints, methods, and binary restrictions it adds. + +## Filesystem Controls + +NemoClaw restricts which paths the agent can read and write, protecting system binaries, configuration files, and gateway credentials. + +### Read-Only System Paths + +The container mounts system directories read-only to prevent the agent from modifying binaries, libraries, or configuration files. + +| Aspect | Detail | +|---|---| +| Default | `/usr`, `/lib`, `/proc`, `/dev/urandom`, `/app`, `/etc`, `/var/log` are read-only. | +| What you can change | Add or remove paths in the `filesystem_policy.read_only` section of the policy file. | +| Risk if relaxed | Making `/usr` or `/lib` writable lets the agent replace system binaries (such as `curl` or `node`) with trojanized versions. Making `/etc` writable lets the agent modify DNS resolution, TLS trust stores, or user accounts. | +| Recommendation | Never make system paths writable. If the agent needs a writable location for generated files, use a subdirectory of `/sandbox`. | + +### Agent Config Directory + +The `/sandbox/.openclaw` directory contains the OpenClaw gateway configuration (model routing, CORS settings, channel config). +The current entrypoint reads the gateway auth token from OpenClaw config when present, exports it as `OPENCLAW_GATEWAY_TOKEN`, and writes it to `/tmp/nemoclaw-proxy-env.sh` so interactive sandbox sessions can reach the gateway through system-wide shell hooks. +In root mode, the gateway process still runs as the separate `gateway` user, but the token is intentionally available to sandbox shells for local gateway access. + +Writable agent state such as plugins, skills, hooks, and workspace metadata lives directly under `/sandbox/.openclaw`. + +By default, this directory starts writable so the agent can manage its own config, install skills, and write to standard home-directory paths natively. +For sensitive workloads, use a reviewed host-side immutability workflow after initial setup so config and writable state entry points cannot be changed by the sandbox user. + +- **DAC permissions (default).** The sandbox user owns `/sandbox/.openclaw` with mode `2770` (setgid `sandbox:sandbox`) and `openclaw.json` with mode `660`, so the agent and its group can read and write config directly. A reviewed host-side immutability workflow should compare the intended ownership and mode with the live sandbox filesystem before treating the config tree as locked. +- **Config integrity hash.** The image includes a SHA256 hash of `openclaw.json`. In the default mutable state, `.config-hash` is sandbox-owned and is not a tamper-proof trust anchor, so startup does not fail closed on that hash. When the hash is root-owned and read-only, startup enforces it and refuses to start if the hash does not match. +- **Gateway token environment.** The gateway exports `OPENCLAW_GATEWAY_TOKEN` and writes it to `/tmp/nemoclaw-proxy-env.sh` for interactive sandbox sessions. Keep this in mind when deciding whether a workload should run with mutable config or an immutable config posture. + +| Aspect | Detail | +|---|---| +| Default | The sandbox keeps `/sandbox/.openclaw` writable (`2770 sandbox:sandbox`), sets `openclaw.json` to `660 sandbox:sandbox`, lets the agent manage state directly, and has the gateway place `OPENCLAW_GATEWAY_TOKEN` in `/tmp/nemoclaw-proxy-env.sh` for interactive shells. | +| What you can change | Apply a reviewed host-side immutability workflow to lock config and state directories with DAC permissions and the immutable flag where available. | +| Risk of default | A writable `.openclaw` directory lets the agent modify its own gateway config: disabling CORS or redirecting inference to an attacker-controlled endpoint. | +| Recommendation | For always-on assistants handling sensitive workloads, lock config after initial setup. For development workflows, the writable default is appropriate. | + +### Writable Paths + +The agent has read-write access to `/sandbox`, `/tmp`, and `/dev/null`. + +| Aspect | Detail | +|---|---| +| Default | `/sandbox` (agent workspace), `/tmp` (temporary files), `/dev/null`. | +| What you can change | Add additional writable paths in `filesystem_policy.read_write`. | +| Risk if relaxed | Each additional writable path expands the agent's ability to persist data and potentially modify system behavior. Adding `/var` lets the agent write to log directories. Adding `/home` gives access to other user directories. | +| Recommendation | Keep writable paths to `/sandbox` and `/tmp`. If the agent needs a persistent working directory, create a subdirectory under `/sandbox`. | + +### Landlock LSM Enforcement + +Landlock is a Linux Security Module that enforces filesystem access rules at the kernel level. + +| Aspect | Detail | +|---|---| +| Default | `compatibility: best_effort`. The entrypoint applies Landlock rules when the kernel supports them and silently skips them on older kernels. | +| What you can change | This is a NemoClaw default, not a user-facing knob. | +| Risk if relaxed | On kernels without Landlock support (pre-5.13), filesystem restrictions rely solely on container mount configuration, which is less granular. | +| Recommendation | Run on a kernel that supports Landlock (5.13+). Ubuntu 22.04 LTS and later include Landlock support. | + +## Process Controls + +NemoClaw limits the capabilities, user privileges, and resource quotas available to processes inside the sandbox. + +### Capability Drops + +The entrypoint drops dangerous Linux capabilities from the bounding set at startup using `capsh`. +This limits what capabilities any child process (gateway, sandbox, agent) can ever acquire. +When the entrypoint switches from root to the `sandbox` and `gateway` users, it uses `setpriv` when available to remove the remaining privilege-separation capabilities from the child process at the same time as the user change. + +The initial entrypoint drop removes `cap_sys_admin`, `cap_sys_ptrace`, `cap_net_raw`, `cap_dac_override`, `cap_sys_chroot`, `cap_fsetid`, `cap_setfcap`, `cap_mknod`, `cap_audit_write`, and `cap_net_bind_service`. +During `setpriv` step-down, the child process also loses `cap_setuid`, `cap_setgid`, `cap_fowner`, `cap_chown`, and `cap_kill`. + +This is best-effort: if `capsh` is not available or `CAP_SETPCAP` is not in the bounding set, the entrypoint logs a warning and continues with the default capability set. +If `setpriv` is unavailable, the entrypoint falls back to `gosu` and logs a warning that the remaining bounding-set capabilities were retained for the child process. +For additional protection, pass `--cap-drop=ALL` with `docker run` or Compose (see Sandbox Hardening (use the `nemoclaw-user-deploy-remote` skill)). + +| Aspect | Detail | +|---|---| +| Default | The entrypoint drops dangerous capabilities at startup using `capsh`, then uses `setpriv` during user step-down when possible. Best-effort. | +| What you can change | When launching with `docker run` directly, pass `--cap-drop=ALL --cap-add=NET_BIND_SERVICE` for stricter enforcement. In the standard NemoClaw flow (with `nemoclaw onboard`), the entrypoint handles capability dropping automatically. | +| Risk if relaxed | `CAP_SYS_ADMIN` and `CAP_SYS_PTRACE` expand kernel and process attack surface. `CAP_NET_RAW` allows raw socket access for network sniffing. `CAP_DAC_OVERRIDE` bypasses filesystem permission checks. If `capsh` or `setpriv` cannot run, the container retains more of the runtime-provided capability set. | +| Recommendation | Run on an image that includes `capsh` and `setpriv` (the NemoClaw image includes them). For defense-in-depth, also pass `--cap-drop=ALL` at the container runtime level. | + +### Gateway Process Isolation + +The OpenClaw gateway runs as a separate `gateway` user, not as the `sandbox` user that runs the agent. + +| Aspect | Detail | +|---|---| +| Default | The entrypoint starts the gateway process using `gosu gateway`, isolating it from the agent's `sandbox` user. | +| What you can change | This is not a user-facing knob. The entrypoint enforces it when running as root. In non-root mode (when OpenShell sets `no-new-privileges`), gateway process isolation does not work because `gosu` cannot change users. | +| Risk if relaxed | If the gateway and agent run as the same user, the agent can kill the gateway process and restart it with a tampered configuration (the "fake-HOME" attack). | +| Recommendation | No action needed. The entrypoint handles this automatically. Be aware that non-root mode disables this isolation. | + +### No New Privileges + +The `no-new-privileges` flag prevents processes from gaining additional privileges through setuid binaries or capability inheritance. + +| Aspect | Detail | +|---|---| +| Default | OpenShell sets `PR_SET_NO_NEW_PRIVS` using `prctl()` inside the sandbox process as part of the seccomp filter setup. The NemoClaw Compose example also shows the equivalent `security_opt: no-new-privileges:true` setting. | +| What you can change | OpenShell's seccomp path enforces this inside the sandbox. It is not a user-facing knob. | +| Risk if relaxed | Without this flag, a compromised process could execute a setuid binary to escalate to root inside the container, then attempt container escape techniques. | +| Recommendation | No action needed. OpenShell enforces this automatically when the sandbox network policy is active. This flag prevents `gosu` from switching users, so non-root mode disables gateway process isolation in the NemoClaw entrypoint. | + +### Process Limit + +A process limit caps the number of processes the sandbox user can spawn. +The entrypoint sets both soft and hard limits using `ulimit -u 512`. +This is best-effort: if the container runtime restricts `ulimit` modification, the entrypoint logs a security warning and continues without the limit. + +| Aspect | Detail | +|---|---| +| Default | 512 processes (`ulimit -u 512`), best-effort. | +| What you can change | Increase or decrease the limit with `--ulimit nproc=N:N` in `docker run` or the `ulimits` section in Compose. The runtime-level ulimit takes precedence over the entrypoint's setting. | +| Risk if relaxed | Removing or raising the limit makes the sandbox vulnerable to fork-bomb attacks, where a runaway process spawns children until the host runs out of resources. If the entrypoint cannot set the limit (logs `[SECURITY] Could not set soft/hard nproc limit`), the container runs without process limits. | +| Recommendation | Keep the default at 512. If the agent runs workloads that spawn many child processes (such as parallel test runners), increase to 1024 and monitor host resource usage. If the entrypoint logs a warning about ulimit restrictions, set the limit through the container runtime instead. | + +### Non-Root User + +The sandbox runs agent processes as a dedicated `sandbox` user and group. +The entrypoint starts as root for privilege separation, then drops to the `sandbox` user for all agent commands. + +| Aspect | Detail | +|---|---| +| Default | `run_as_user: sandbox`, `run_as_group: sandbox`. A separate `gateway` user runs the gateway process. | +| What you can change | Change the `process` section in the policy file to run as a different user. | +| Risk if relaxed | Running as `root` inside the container gives the agent access to modify any file in the container filesystem and increases the impact of container escape vulnerabilities. | +| Recommendation | Never run as root. Keep the `sandbox` user. | + +### PATH Hardening + +The entrypoint locks the `PATH` environment variable to system directories, preventing the agent from injecting malicious binaries into command resolution. + +| Aspect | Detail | +|---|---| +| Default | The entrypoint sets `PATH` to `/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin` at startup. | +| What you can change | This is not a user-facing knob. The entrypoint enforces it. | +| Risk if relaxed | Without PATH hardening, the agent could create an executable named `curl` or `git` in a writable directory earlier in the PATH, intercepting commands run by the entrypoint or other processes. | +| Recommendation | No action needed. The entrypoint handles this automatically. | + +### Build Toolchain Removal + +The Dockerfile removes compilers and network probes from the runtime image. + +| Aspect | Detail | +|---|---| +| Default | The Dockerfile purges `gcc`, `gcc-12`, `g++`, `g++-12`, `cpp`, `cpp-12`, `make`, `netcat-openbsd`, `netcat-traditional`, and `ncat` from the sandbox image. | +| What you can change | Modify the Dockerfile to keep these tools, or install them at runtime if package manager access is allowed. | +| Risk if relaxed | A compiler lets the agent build arbitrary native code, including kernel exploits or custom network tools. `netcat` enables arbitrary TCP connections that bypass HTTP-level policy enforcement. | +| Recommendation | Keep build tools removed. If the agent needs to compile code, run the build in a separate, purpose-built container and copy artifacts into the sandbox. | + +### Image Digest Pinning + +The blueprint references the sandbox image by an immutable `@sha256:` digest instead of a mutable tag such as `:latest`. +A registry compromise or accidental force-push cannot silently swap the sandbox image. + +| Aspect | Detail | +|---|---| +| Default | `nemoclaw-blueprint/blueprint.yaml` pins the sandbox image by digest. A CI regression test blocks any mutable-tag reference from merging. | +| What you can change | Contributors bumping the sandbox image must update the digest in `blueprint.yaml`. Release tooling should rewrite the digest automatically. | +| Risk if relaxed | Reverting to a mutable tag (`:latest`) allows a registry-side change to replace the sandbox image without any blueprint update, which is a supply-chain risk. | +| Recommendation | Always reference the sandbox image by digest. If you build a custom image with `nemoclaw onboard --from`, the digest constraint does not apply to your local build. | + +### Auth Profile Permissions + +The entrypoint and migration flows enforce `chmod 600` on all `auth-profiles.json` files under `~/.openclaw`. +This prevents other users on the host from reading stored credentials. + +| Aspect | Detail | +|---|---| +| Default | `600` permissions applied recursively at startup and after migration restores. | +| What you can change | This is not a user-facing knob. The entrypoint enforces it. | +| Risk if relaxed | Looser permissions let other users or processes on the host read provider API keys and tokens stored in auth profiles. | +| Recommendation | No action needed. If you see a `permission denied` error when reading auth profiles, verify that you are running as the same user who created them. | + +## Gateway Authentication Controls + +The OpenClaw gateway authenticates devices that connect to the Control UI dashboard. +NemoClaw hardens these defaults at image build time. + +### Device Authentication + +Device authentication requires each connecting device to go through a pairing flow before it can interact with the gateway. + +| Aspect | Detail | +|---|---| +| Default | Enabled. The gateway requires device pairing for all connections. | +| What you can change | Set `NEMOCLAW_DISABLE_DEVICE_AUTH=1` as a Docker build argument to disable device authentication. This is a build-time setting baked into `openclaw.json` and verified by hash at startup. | +| Risk if relaxed | Disabling device auth allows any device on the network to connect to the gateway without proving identity. This is dangerous when combined with LAN-bind changes or cloudflared tunnels in remote deployments, resulting in an unauthenticated, publicly reachable dashboard. | +| Recommendation | Keep device auth enabled (the default). Only disable it for headless or development environments where no untrusted devices can reach the gateway. | + +### Gateway Bind Address + +NemoClaw binds the OpenShell gateway to loopback by default. + +| Aspect | Detail | +|---|---| +| Default | `NEMOCLAW_GATEWAY_BIND_ADDRESS=127.0.0.1`. | +| What you can change | Set `NEMOCLAW_GATEWAY_BIND_ADDRESS=0.0.0.0` before onboarding to listen on all IPv4 interfaces. | +| Risk if relaxed | Other hosts on the network may be able to reach the OpenShell gateway. | +| Recommendation | Keep the loopback default unless the gateway must be reachable from another host. | + +### Insecure Auth Derivation + +The `allowInsecureAuth` setting controls whether the gateway permits non-HTTPS authentication. + +| Aspect | Detail | +|---|---| +| Default | Derived from the `CHAT_UI_URL` scheme at build time. When the URL uses `http://` (local development), insecure auth is allowed. When it uses `https://` (remote or production), insecure auth is blocked. | +| What you can change | This is derived automatically from `CHAT_UI_URL`. Set `CHAT_UI_URL` to an `https://` URL to enforce secure auth. | +| Risk if relaxed | Allowing insecure auth over HTTPS defeats the purpose of TLS, because authentication tokens transit in cleartext. | +| Recommendation | Use `https://` for any deployment accessible beyond `localhost`. The default local URL (`http://127.0.0.1:18789`) correctly allows insecure auth for local development. | + +### Auto-Pair Client Allowlist + +The auto-pair watcher automatically approves device pairing requests from recognized clients, so you do not need to manually approve the Control UI. + +| Aspect | Detail | +|---|---| +| Default | The watcher approves devices with `clientId` set to `openclaw-control-ui` or `clientMode` set to `webchat`. All other clients are rejected and logged. | +| What you can change | This is not a user-facing knob. The allowlist is defined in the entrypoint script. | +| Risk if relaxed | Approving all device types without validation lets rogue or unexpected clients pair with the gateway unchallenged. | +| Recommendation | No action needed. The entrypoint handles this automatically. If you see `[auto-pair] rejected unknown client=...` in the logs, investigate the source of the unexpected connection. | + +### CLI Secret Redaction + +The CLI automatically redacts secret patterns (API keys, bearer tokens, provider credentials) from command output and error messages before logging them. + +| Aspect | Detail | +|---|---| +| Default | Enabled. The runner redacts secrets from stdout, stderr, and thrown error messages. | +| What you can change | This is not a user-facing knob. The CLI enforces it on all command output paths. | +| Risk if relaxed | Without redaction, secrets could appear in terminal scrollback, log files, or debug output shared in bug reports. | +| Recommendation | No action needed. If you share `nemoclaw debug` output, verify that no secrets appear in the collected diagnostics. | + +### Memory Secret Scanner + +The NemoClaw plugin blocks the agent from writing likely secrets (API keys, tokens, private keys) into persistent memory files. +The scanner intercepts Write, Edit, and similar tool calls targeting memory and workspace paths before they reach disk. + +| Aspect | Detail | +|---|---| +| Default | Enabled. The plugin registers a `before_tool_call` hook that scans for 14 high-confidence secret patterns. | +| What it covers | Three classifiers, all enforced through `isMemoryPath()`: (1) absolute `MEMORY_PATH_SEGMENTS` such as `/.openclaw/memory/`, `/.openclaw/workspace/`, `/.openclaw/agents/`, `/.openclaw/skills/`, `/.openclaw/hooks/`, `/.openclaw/credentials/`, `/.openclaw/openclaw.json`, `/.nemoclaw/`; (2) canonical workspace basenames in `MEMORY_BASENAMES` (`IDENTITY.md`, `MEMORY.md`, `SOUL.md`, `USER.md`, `AGENTS.md`) matched regardless of the surrounding path; and (3) lexically-normalized workspace-relative writes matching `MEMORY_RELATIVE_PREFIXES` (`.openclaw/`, `.nemoclaw/`, `memory/`) or named workspace daily memory paths, for embedded-fallback mode where the host's path resolver is unavailable. | +| What you can change | This is not a user-facing knob. The plugin enforces it automatically. | +| Risk if relaxed | Without scanning, the agent could persist API keys or tokens in memory files that survive across sessions and backups. | +| Recommendation | No action needed. If a write is blocked, the agent receives an actionable error listing the detected patterns. | + +## Inference Controls + +OpenShell routes all inference traffic through the gateway to isolate provider credentials from the sandbox. + +### Routed Inference through `inference.local` + +The OpenShell gateway intercepts all inference requests from the agent and routes them to the configured provider. +The agent never receives the provider API key. + +| Aspect | Detail | +|---|---| +| Default | The agent talks to `inference.local`. The host owns the credential and upstream endpoint. | +| What you can change | You cannot configure this architecture. The system always enforces it. | +| Risk if bypassed | If the agent could reach an inference endpoint directly (by adding it to the network policy), it would need an API key. Since the sandbox does not contain credentials, this acts as defense-in-depth. However, adding an inference provider's host to the network policy without going through OpenShell routing could let the agent use a stolen or hardcoded key. | +| Recommendation | Do not add inference provider hosts (such as `api.openai.com` or `api.anthropic.com`) to the network policy. Use OpenShell inference routing instead. | + +### Provider Trust Tiers + +Different inference providers have different trust and cost profiles. + +| Provider | Trust level | Cost risk | Data handling | +|---|---|---|---| +| NVIDIA Endpoints | High. Hosted on `build.nvidia.com`. | Pay-per-token with an API key. Unattended agents can accumulate cost. | NVIDIA infrastructure processes requests. | +| OpenAI | High. Commercial API. | Pay-per-token. Same cost risk as NVIDIA Endpoints. | Subject to OpenAI data policies. | +| Anthropic | High. Commercial API. | Pay-per-token. Same cost risk as NVIDIA Endpoints. | Subject to Anthropic data policies. | +| Google Gemini | High. Commercial API. | Pay-per-token. Same cost risk as NVIDIA Endpoints. | Subject to Google data policies. | +| Local Ollama | Self-hosted. No data leaves the machine. | No per-token cost. GPU/CPU resource cost. | Data stays local. | +| Custom compatible endpoint | Varies. Depends on the proxy or gateway. | Varies. | Depends on the endpoint operator. | + +**Recommendation:** For sensitive workloads, use local Ollama to keep data on-premise. For general use, NVIDIA Endpoints provide a good balance of capability and trust. Review the data policies of any cloud provider you use. + +### Experimental Providers + +The `NEMOCLAW_EXPERIMENTAL=1` environment variable gates local NVIDIA NIM and generic Linux managed vLLM install/start. DGX Spark and DGX Station managed vLLM entries are offered by default, and an already-running vLLM server on `localhost:8000` is offered in the menu without a flag, because selecting either is an explicit user action. + +| Aspect | Detail | +|---|---| +| Default | Local NVIDIA NIM and generic Linux managed vLLM install/start are hidden. DGX Spark and DGX Station managed vLLM entries, plus already-running vLLM on `localhost:8000`, are offered when detected. | +| What you can change | Set `NEMOCLAW_EXPERIMENTAL=1` before running `nemoclaw onboard` to surface Local NIM and generic Linux managed vLLM. To request only the managed vLLM path non-interactively, set `NEMOCLAW_PROVIDER=install-vllm`. | +| Risk if selected | NemoClaw has not fully validated these providers. NIM requires a NIM-capable GPU. The managed vLLM path pulls a container image and starts it on a supported NVIDIA GPU host. Misconfiguration can cause failed inference or unexpected behavior. | +| Recommendation | Use experimental providers only for evaluation. Do not rely on them for always-on assistants. | + +## Posture Profiles + +The following profiles describe how to configure NemoClaw for different use cases. +These are not separate policy files. +They provide guidance on which controls to keep tight or relax. + +### Locked-Down (Default) + +Use for always-on assistants with minimal external access. + +- Keep all defaults. Do not add presets. +- Use operator approval for any endpoint the agent requests. +- Use NVIDIA Endpoints or local Ollama for inference. +- Monitor the TUI for unexpected network requests. + +### Development + +Use when the agent needs package registries, Docker Hub, or broader GitHub access during development tasks. + +- Apply the `pypi` and `npm` presets for package installation. +- Keep binary restrictions on all presets. +- Review the agent's network activity periodically with `openshell term`. +- Use operator approval for any endpoint not covered by a preset. + +### Integration Testing + +Use when the agent talks to internal APIs or third-party services during testing. + +- Add custom endpoint entries with tight path and method restrictions. +- Use `protocol: rest` for all HTTP APIs to maintain inspection. +- Use operator approval for unknown endpoints during test runs. +- Review and clean up the baseline policy after testing. Remove endpoints that are no longer needed. + +## Common Mistakes + +The following patterns weaken security without providing meaningful benefit. + +| Mistake | Why it matters | What to do instead | +|---------|---------------|-------------------| +| Omitting `protocol: rest` on REST API endpoints without a compatibility reason | Endpoints without a `protocol` field use L4-only enforcement. The proxy allows the TCP stream through after checking host, port, and binary, but cannot see or filter individual HTTP requests. | Add `protocol: rest` with explicit `rules` to enable per-request method and path control on REST APIs. Use L4 pass-through only for documented cases such as npm/Yarn on Node 22, where the client requires a CONNECT tunnel that L7 inspection would break. | +| Adding endpoints to the baseline policy for one-off requests | Adding an endpoint to the baseline policy makes it permanently reachable across all sandbox instances. | Use operator approval. Approved endpoints persist within the sandbox instance but reset when you destroy and recreate the sandbox. | +| Relying solely on the entrypoint for capability drops | The entrypoint drops dangerous capabilities using `capsh`, but this is best-effort. If `capsh` is unavailable or `CAP_SETPCAP` is not in the bounding set, the container runs with the default capability set. | Pass `--cap-drop=ALL` at the container runtime level as defense-in-depth. | +| Leaving `/sandbox/.openclaw` writable on sensitive workloads | This directory contains the OpenClaw gateway configuration. A writable `.openclaw` lets the agent disable CORS, redirect inference routing, or weaken gateway protections. | Lock config for always-on assistants handling sensitive data. | +| Adding inference provider hosts to the network policy | Direct network access to an inference host bypasses credential isolation and usage tracking. | Use OpenShell inference routing instead of adding hosts like `api.openai.com` or `api.anthropic.com` to the network policy. | +| Disabling device auth for remote deployments | Without device auth, any device on the network can connect to the gateway without pairing. Combined with a cloudflared tunnel, this makes the dashboard publicly accessible and unauthenticated. | Keep `NEMOCLAW_DISABLE_DEVICE_AUTH` at its default (`0`). Only set it to `1` for local headless or development environments. | + +## Known Limitations + +| Limitation | Impact | Mitigation | +|-----------|--------|------------| +| `openclaw agent --local` bypasses gateway | Secret scanning, network policy, and inference auth are not enforced when the agent runs in local mode. | A runtime warning is emitted when `--local` is detected. Avoid `--local` for production workflows. A future OpenClaw-level hook will close this gap. | +| Direct filesystem writes bypass secret scanner | The scanner intercepts OpenClaw tool calls, not raw filesystem writes (e.g., `echo secret > file`). | Landlock restricts writable paths. The scanner is application-layer defense-in-depth, not a filesystem-level control. | +| Base64/hex-encoded secrets are not detected | Content-based regex scanning cannot detect encoded or obfuscated secrets. | Use environment variables or credential stores instead of writing secrets to files. | + +## Related Topics + +- Network Policies (use the `nemoclaw-user-reference` skill) for the full baseline policy reference. +- Customize the Network Policy (use the `nemoclaw-user-manage-policy` skill) for static and dynamic policy changes. +- Approve or Deny Network Requests (use the `nemoclaw-user-manage-policy` skill) for the operator approval flow. +- Sandbox Hardening (use the `nemoclaw-user-deploy-remote` skill) for container-level security measures. +- Inference Options (use the `nemoclaw-user-configure-inference` skill) for provider configuration details. +- How It Works (use the `nemoclaw-user-overview` skill) for the protection layer architecture. diff --git a/.agents/skills/nemoclaw-user-configure-security/references/credential-storage.md b/.agents/skills/nemoclaw-user-configure-security/references/credential-storage.md index 45ef77e3e0..b40b676a20 100644 --- a/.agents/skills/nemoclaw-user-configure-security/references/credential-storage.md +++ b/.agents/skills/nemoclaw-user-configure-security/references/credential-storage.md @@ -107,4 +107,4 @@ On the next run NemoClaw prompts again unless the credential is supplied through ## Related Files -For the broader sandbox security model and operational trade-offs, see [Security Best Practices](../SKILL.md) and Architecture (use the `nemoclaw-user-reference` skill). +For the broader sandbox security model and operational trade-offs, see [Security Best Practices](best-practices.md) and Architecture (use the `nemoclaw-user-reference` skill). diff --git a/.agents/skills/nemoclaw-user-configure-security/references/openclaw-controls.md b/.agents/skills/nemoclaw-user-configure-security/references/openclaw-controls.md index f65f32869e..2ced0c76de 100644 --- a/.agents/skills/nemoclaw-user-configure-security/references/openclaw-controls.md +++ b/.agents/skills/nemoclaw-user-configure-security/references/openclaw-controls.md @@ -117,5 +117,5 @@ The implementation detects unsafe nested quantifiers, bounds input length, and c ## Next Steps -- [Security Best Practices](../SKILL.md) for NemoClaw's own security controls and risk framework. +- [Security Best Practices](best-practices.md) for NemoClaw's own security controls and risk framework. - [Credential Storage](credential-storage.md) for how NemoClaw stores and protects provider credentials. diff --git a/.agents/skills/nemoclaw-user-deploy-remote/SKILL.md b/.agents/skills/nemoclaw-user-deploy-remote/SKILL.md index 810915f4fd..d2ac40e834 100644 --- a/.agents/skills/nemoclaw-user-deploy-remote/SKILL.md +++ b/.agents/skills/nemoclaw-user-deploy-remote/SKILL.md @@ -1,6 +1,6 @@ --- name: "nemoclaw-user-deploy-remote" -description: "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. Trigger keywords - nemoclaw brev web ui, nemoclaw getting started, brev quickstart, nvidia nemotron agent, deploy nemoclaw remote gpu, nemoclaw brev cloud deployment, nemoclaw plugins, openclaw plugins, install openclaw plugin, nemoclaw onboard from dockerfile, nemoclaw sandbox hardening, container security, docker capabilities, process limits." +description: "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. Trigger keywords - deploy nemoclaw remote gpu, nemoclaw brev cloud deployment, nemoclaw plugins, openclaw plugins, install openclaw plugin, nemoclaw onboard from dockerfile, nemoclaw brev web ui, nemoclaw getting started, brev quickstart, nvidia nemotron agent, nemoclaw sandbox hardening, container security, docker capabilities, process limits." license: "Apache-2.0" --- diff --git a/.agents/skills/nemoclaw-user-get-started/SKILL.md b/.agents/skills/nemoclaw-user-get-started/SKILL.md index 7d47188ab9..bab2b2d0e2 100644 --- a/.agents/skills/nemoclaw-user-get-started/SKILL.md +++ b/.agents/skills/nemoclaw-user-get-started/SKILL.md @@ -1,6 +1,6 @@ --- name: "nemoclaw-user-get-started" -description: "Lists the hardware, software, and container runtime requirements for running NemoClaw. Use when verifying prerequisites before installation. Trigger keywords - nemoclaw prerequisites, nemoclaw supported platforms, nemoclaw hardware software, nemohermes quickstart, hermes agent nemoclaw, run hermes openshell sandbox, nemoclaw quickstart, install nemoclaw openclaw sandbox, nemoclaw windows wsl2 setup, nemoclaw install windows docker desktop." +description: "Installs NemoClaw, launches a sandbox, and runs the first agent prompt. Use when onboarding, installing, or launching a NemoClaw sandbox for the first time. Trigger keywords - nemoclaw quickstart, install nemoclaw openclaw sandbox, nemohermes quickstart, hermes agent nemoclaw, run hermes openshell sandbox, nemoclaw prerequisites, nemoclaw supported platforms, nemoclaw hardware software, nemoclaw windows wsl2 setup, nemoclaw install windows docker desktop." license: "Apache-2.0" --- diff --git a/.agents/skills/nemoclaw-user-manage-policy/SKILL.md b/.agents/skills/nemoclaw-user-manage-policy/SKILL.md index 9498bc8fea..5f62d8d49f 100644 --- a/.agents/skills/nemoclaw-user-manage-policy/SKILL.md +++ b/.agents/skills/nemoclaw-user-manage-policy/SKILL.md @@ -1,6 +1,6 @@ --- name: "nemoclaw-user-manage-policy" -description: "Reviews and approves blocked agent network requests in the TUI. Use when approving or denying sandbox egress requests, managing blocked network calls, or using the approval TUI. Trigger keywords - nemoclaw approve network requests, sandbox egress approval tui, customize nemoclaw network policy, sandbox egress policy configuration, nemoclaw integration policy examples, post-install policy setup, openshell approval workflow, policy preset." +description: "Adds, removes, or modifies allowed endpoints in the sandbox policy. Use when customizing network policy, changing egress rules, or configuring sandbox endpoint access. Trigger keywords - customize nemoclaw network policy, sandbox egress policy configuration, nemoclaw integration policy examples, post-install policy setup, openshell approval workflow, policy preset, nemoclaw approve network requests, sandbox egress approval tui." license: "Apache-2.0" --- diff --git a/.agents/skills/nemoclaw-user-manage-sandboxes/SKILL.md b/.agents/skills/nemoclaw-user-manage-sandboxes/SKILL.md index 1dc4bae284..766db03f54 100644 --- a/.agents/skills/nemoclaw-user-manage-sandboxes/SKILL.md +++ b/.agents/skills/nemoclaw-user-manage-sandboxes/SKILL.md @@ -1,6 +1,6 @@ --- name: "nemoclaw-user-manage-sandboxes" -description: "Backs up and restores OpenClaw workspace files before destructive operations such as sandbox rebuilds. Use when downloading workspace files from a sandbox, uploading restored files into a new sandbox, or preserving sandbox state across rebuilds. Trigger keywords - nemoclaw backup, nemoclaw restore, workspace backup, openshell sandbox download upload, manage nemoclaw sandboxes, nemoclaw status, nemoclaw list, nemoclaw dashboard port, nemoclaw rebuild, nemoclaw upgrade sandboxes, nemoclaw uninstall, nemoclaw messaging channels, nemoclaw telegram, nemoclaw discord, nemoclaw slack, nemoclaw wechat, nemoclaw whatsapp, openshell channel messaging, sandbox mutability, sandbox runtime configuration, sandbox rebuild, nemoclaw workspace files, soul.md, user.md, identity.md, agents.md, sandbox persistence." +description: "Explains operational tasks after the quickstart: listing sandboxes, status and health checks, logs, diagnostics, port forwards, multiple sandboxes, credential reset, rebuilds, network presets, upgrades, and uninstall. Trigger keywords - manage nemoclaw sandboxes, nemoclaw status, nemoclaw list, nemoclaw dashboard port, nemoclaw rebuild, nemoclaw upgrade sandboxes, nemoclaw uninstall, sandbox mutability, sandbox runtime configuration, sandbox rebuild, nemoclaw backup, nemoclaw restore, workspace backup, openshell sandbox download upload, nemoclaw messaging channels, nemoclaw telegram, nemoclaw discord, nemoclaw slack, nemoclaw wechat, nemoclaw whatsapp, openshell channel messaging, nemoclaw workspace files, soul.md, user.md, identity.md, agents.md, sandbox persistence." license: "Apache-2.0" --- diff --git a/.agents/skills/nemoclaw-user-overview/SKILL.md b/.agents/skills/nemoclaw-user-overview/SKILL.md index 11e7ba010a..01ed184ec2 100644 --- a/.agents/skills/nemoclaw-user-overview/SKILL.md +++ b/.agents/skills/nemoclaw-user-overview/SKILL.md @@ -1,107 +1,17 @@ --- name: "nemoclaw-user-overview" -description: "Explains how OpenClaw, OpenShell, and NemoClaw form the ecosystem, NemoClaw's position in the stack, what NemoClaw adds beyond the community sandbox, and when to prefer NemoClaw versus integrating OpenShell and OpenClaw directly. Use when users ask about the relationship between OpenClaw, OpenShell, and NemoClaw, or when to use NemoClaw versus OpenShell. Trigger keywords - nemoclaw ecosystem, openclaw openshell, nemoclaw vs openshell, sandboxed openclaw, how nemoclaw works, nemoclaw sandbox lifecycle blueprint, nemoclaw overview, openclaw always-on assistants, nvidia openshell, nvidia nemotron, nemoclaw release notes, nemoclaw changelog." +description: "Explains what NemoClaw covers: onboarding, lifecycle management, and OpenClaw operations within OpenShell containers, plus capabilities and why it exists. Use when users ask what NemoClaw is or what the project provides. For ecosystem placement or OpenShell-only paths, use the Ecosystem page; for internal mechanics, use How It Works. Trigger keywords - nemoclaw overview, openclaw always-on assistants, nvidia openshell, nvidia nemotron, nemoclaw ecosystem, openclaw openshell, nemoclaw vs openshell, sandboxed openclaw, how nemoclaw works, nemoclaw sandbox lifecycle blueprint, nemoclaw release notes, nemoclaw changelog." license: "Apache-2.0" --- -# Ecosystem - -NemoClaw provides onboarding, lifecycle management, and OpenClaw operations within OpenShell containers. - -This page describes how the ecosystem is formed across projects, where NemoClaw sits relative to [OpenShell](https://github.com/NVIDIA/OpenShell) and [OpenClaw](https://openclaw.ai), and how to choose between NemoClaw and OpenShell. - -## How the Stack Fits Together - -There are three pieces that are put together in a NemoClaw deployment: OpenClaw, OpenShell, and NemoClaw, each with a distinct scope. -The following diagram shows how they fit together. - -```mermaid -flowchart TB - NC["🦞 NVIDIA NemoClaw
CLI, plugin, blueprint"] - OS["🐚 NVIDIA OpenShell
Gateway, policy, inference routing"] - OC["🦞 OpenClaw
Assistant in sandbox"] - - NC -->|orchestrates| OS - OS -->|isolates and runs| OC - - classDef nv fill:#76b900,stroke:#333,color:#fff - classDef nvLight fill:#e6f2cc,stroke:#76b900,color:#1a1a1a - classDef nvDark fill:#333,stroke:#76b900,color:#fff - - class NC nv - class OS nv - class OC nvDark - - linkStyle 0 stroke:#76b900,stroke-width:2px - linkStyle 1 stroke:#76b900,stroke-width:2px -``` - -NemoClaw sits above OpenShell in the operator workflow. -It drives OpenShell APIs and CLI to create and configure the sandbox that runs OpenClaw. -Models and endpoints sit behind OpenShell's inference routing. -NemoClaw onboarding wires provider choice into that routing. - -The following table shows the scope of each component in the stack. - -| Project | Scope | -|---------|--------| -| [OpenClaw](https://openclaw.ai) | The assistant: runtime, tools, memory, and behavior inside the container. It does not define the sandbox or the host gateway. | -| [OpenShell](https://github.com/NVIDIA/OpenShell) | The execution environment: sandbox lifecycle, network, filesystem, and process policy, inference routing, and the operator-facing `openshell` CLI for those primitives. | -| NemoClaw | The NVIDIA reference stack that implements the definition above on the host: `nemoclaw` CLI and plugin, versioned blueprint, channel messaging configured for OpenShell-managed delivery, and state migration helpers so OpenClaw runs inside OpenShell in a documented, repeatable way. | - -## NemoClaw Path versus OpenShell Path - -Both paths assume OpenShell can sandbox a workload. -The difference is who owns the integration work. - -| Path | What it means | -|------|---------------| -| **NemoClaw path** | You adopt the reference stack. NemoClaw's blueprint encodes a hardened image, default policies, and orchestration so `nemoclaw onboard` can stand up a known-good OpenClaw-on-OpenShell setup with less custom glue. | -| **OpenShell path** | You use OpenShell as the platform and supply your own container, install steps for OpenClaw, policy YAML, provider setup, and any host bridges. OpenShell stays the sandbox and policy engine; nothing requires NemoClaw's blueprint or CLI. | - -## What NemoClaw Adds Beyond the OpenShell Community Sandbox - -OpenShell ships a community sandbox for OpenClaw. -Running `openshell sandbox create --from openclaw` pulls that package, builds the image, applies the bundled policy, and starts a working sandbox. -This is a valid path, and it produces a running OpenClaw environment with OpenShell isolation. - -NemoClaw builds on that foundation with additional security hardening, automation, and lifecycle tooling. -The following table compares the two paths. - -| Capability | `openshell sandbox create --from openclaw` | `nemoclaw onboard` | -|---|---|---| -| Sandbox isolation | Yes. OpenShell applies seccomp filters, Landlock filesystem restrictions, privilege dropping, network namespace isolation, and no-new-privileges enforcement. The community sandbox bundles its own policy tailored for OpenClaw. | Yes. NemoClaw applies these through the blueprint and layers a more restrictive policy on top (see rows below). | -| Credential handling | OpenShell's provider system replaces real credentials with placeholder tokens in the sandbox environment. The L7 proxy resolves placeholders to real values at egress. You create providers manually with `openshell provider create`. | NemoClaw creates OpenShell providers automatically during onboarding. It also filters sensitive host environment variables (provider API keys, `DISCORD_BOT_TOKEN`, `SLACK_BOT_TOKEN`, `TELEGRAM_BOT_TOKEN`) from the sandbox creation command to prevent accidental leakage through build args. | -| Image hardening | The community image includes standard system tools for general-purpose use. | NemoClaw strips build toolchains (`gcc`, `g++`, `make`) and network probes (`netcat`) from the runtime image to reduce attack surface. | -| Filesystem policy | The community sandbox bundles a policy for OpenClaw. | NemoClaw defines a targeted read-only and read-write layout. System paths (`/usr`, `/lib`, `/etc`) are read-only. The agent's home directory (`/sandbox`) and config directory (`/sandbox/.openclaw`) are writable by default so the agent can manage config, install skills, and write to standard paths natively. | -| Inference setup | The community sandbox includes an `openclaw-start` script that runs OpenClaw's onboarding wizard inside the sandbox. You can also create providers and configure OpenShell inference routing manually from the host. | NemoClaw's onboarding wizard validates your credential from the host, lets you select a provider (NVIDIA Endpoints, OpenAI, Anthropic, Google Gemini, Ollama, and compatible endpoints), and configures OpenShell's inference routing automatically. Credentials stay on the host and are delivered through OpenShell's provider system. | -| Channel messaging | OpenShell provides the credential provider system and L7 proxy that delivers channel tokens securely (including path-based resolution for Telegram's `/bot/` URL pattern). You create providers and configure OpenClaw's channel settings manually. | NemoClaw automates channel setup during onboarding: it collects bot tokens, registers them as OpenShell providers, and bakes OpenClaw channel config with placeholder tokens that OpenShell's proxy resolves at egress. No separate bridge process runs on the host. | -| Blueprint versioning | No blueprint. The community sandbox uses whatever image version is currently published. | NemoClaw downloads the blueprint artifact, checks version compatibility, and verifies its digest before applying. Running `nemoclaw onboard` on different machines produces the same sandbox. | -| State migration | Not included. | NemoClaw migrates agent state across machines with credential stripping and integrity verification. | -| Process count limits | OpenShell applies seccomp and privilege dropping. You set process count limits manually with `--ulimit` or orchestrator config. | NemoClaw applies `ulimit -u 512` in the container entrypoint to cap the process count and mitigate fork-bomb attacks, on top of OpenShell's seccomp and privilege dropping. | - -## When to Use Which - -Use the following table to decide when to use NemoClaw versus OpenShell. - -| Situation | Prefer | -|-----------|--------| -| You want OpenClaw with minimal assembly, NVIDIA defaults, and the documented install and onboard flow. | NemoClaw | -| You need maximum flexibility for custom images, a layout that does not match the NemoClaw blueprint, or a workload outside this reference stack. | OpenShell with your own integration | -| You are standardizing on the NVIDIA reference for always-on assistants with policy and inference routing. | NemoClaw | -| You are building internal platform abstractions where the NemoClaw CLI or blueprint is not the right fit. | OpenShell (and your orchestration) | +# NemoClaw User Overview ## References -- **Load [references/how-it-works.md](references/how-it-works.md)** for sandbox lifecycle and architecture mechanics; not for product definition (Overview) or multi-project placement (Ecosystem). Describes how NemoClaw works internally: CLI, plugin, blueprint runner, OpenShell orchestration, inference routing, and protection layers. - **Load [references/overview.md](references/overview.md)** when users ask what NemoClaw is or what the project provides. For ecosystem placement or OpenShell-only paths, use the Ecosystem page; for internal mechanics, use How It Works. Explains what NemoClaw covers: onboarding, lifecycle management, and OpenClaw operations within OpenShell containers, plus capabilities and why it exists. +- **Load [references/ecosystem.md](references/ecosystem.md)** when users ask about the relationship between OpenClaw, OpenShell, and NemoClaw, or when to use NemoClaw versus OpenShell. Explains how OpenClaw, OpenShell, and NemoClaw form the ecosystem, NemoClaw's position in the stack, what NemoClaw adds beyond the community sandbox, and when to prefer NemoClaw versus integrating OpenShell and OpenClaw directly. +- **Load [references/how-it-works.md](references/how-it-works.md)** for sandbox lifecycle and architecture mechanics; not for product definition (Overview) or multi-project placement (Ecosystem). Describes how NemoClaw works internally: CLI, plugin, blueprint runner, OpenShell orchestration, inference routing, and protection layers. - **Load [references/release-notes.md](references/release-notes.md)** when users ask about recent changes, the release cadence, or where to track versioned assets on GitHub. Includes the NemoClaw release notes. - -## Related Skills - -- [Overview](references/overview.md) contains what NemoClaw is, capabilities, benefits, and use cases. -- [How It Works](references/how-it-works.md) describes how NemoClaw runs, plugin, blueprint, sandbox creation, routing, protection layers. -- `nemoclaw-user-reference` — Architecture (use the `nemoclaw-user-reference` skill) shows the repository structure and technical diagrams diff --git a/.agents/skills/nemoclaw-user-overview/references/ecosystem.md b/.agents/skills/nemoclaw-user-overview/references/ecosystem.md new file mode 100644 index 0000000000..b1d97c4a22 --- /dev/null +++ b/.agents/skills/nemoclaw-user-overview/references/ecosystem.md @@ -0,0 +1,94 @@ + + +# Ecosystem + +NemoClaw provides onboarding, lifecycle management, and OpenClaw operations within OpenShell containers. + +This page describes how the ecosystem is formed across projects, where NemoClaw sits relative to [OpenShell](https://github.com/NVIDIA/OpenShell) and [OpenClaw](https://openclaw.ai), and how to choose between NemoClaw and OpenShell. + +## How the Stack Fits Together + +There are three pieces that are put together in a NemoClaw deployment: OpenClaw, OpenShell, and NemoClaw, each with a distinct scope. +The following diagram shows how they fit together. + +```mermaid +flowchart TB + NC["🦞 NVIDIA NemoClaw
CLI, plugin, blueprint"] + OS["🐚 NVIDIA OpenShell
Gateway, policy, inference routing"] + OC["🦞 OpenClaw
Assistant in sandbox"] + + NC -->|orchestrates| OS + OS -->|isolates and runs| OC + + classDef nv fill:#76b900,stroke:#333,color:#fff + classDef nvLight fill:#e6f2cc,stroke:#76b900,color:#1a1a1a + classDef nvDark fill:#333,stroke:#76b900,color:#fff + + class NC nv + class OS nv + class OC nvDark + + linkStyle 0 stroke:#76b900,stroke-width:2px + linkStyle 1 stroke:#76b900,stroke-width:2px +``` + +NemoClaw sits above OpenShell in the operator workflow. +It drives OpenShell APIs and CLI to create and configure the sandbox that runs OpenClaw. +Models and endpoints sit behind OpenShell's inference routing. +NemoClaw onboarding wires provider choice into that routing. + +The following table shows the scope of each component in the stack. + +| Project | Scope | +|---------|--------| +| [OpenClaw](https://openclaw.ai) | The assistant: runtime, tools, memory, and behavior inside the container. It does not define the sandbox or the host gateway. | +| [OpenShell](https://github.com/NVIDIA/OpenShell) | The execution environment: sandbox lifecycle, network, filesystem, and process policy, inference routing, and the operator-facing `openshell` CLI for those primitives. | +| NemoClaw | The NVIDIA reference stack that implements the definition above on the host: `nemoclaw` CLI and plugin, versioned blueprint, channel messaging configured for OpenShell-managed delivery, and state migration helpers so OpenClaw runs inside OpenShell in a documented, repeatable way. | + +## NemoClaw Path versus OpenShell Path + +Both paths assume OpenShell can sandbox a workload. +The difference is who owns the integration work. + +| Path | What it means | +|------|---------------| +| **NemoClaw path** | You adopt the reference stack. NemoClaw's blueprint encodes a hardened image, default policies, and orchestration so `nemoclaw onboard` can stand up a known-good OpenClaw-on-OpenShell setup with less custom glue. | +| **OpenShell path** | You use OpenShell as the platform and supply your own container, install steps for OpenClaw, policy YAML, provider setup, and any host bridges. OpenShell stays the sandbox and policy engine; nothing requires NemoClaw's blueprint or CLI. | + +## What NemoClaw Adds Beyond the OpenShell Community Sandbox + +OpenShell ships a community sandbox for OpenClaw. +Running `openshell sandbox create --from openclaw` pulls that package, builds the image, applies the bundled policy, and starts a working sandbox. +This is a valid path, and it produces a running OpenClaw environment with OpenShell isolation. + +NemoClaw builds on that foundation with additional security hardening, automation, and lifecycle tooling. +The following table compares the two paths. + +| Capability | `openshell sandbox create --from openclaw` | `nemoclaw onboard` | +|---|---|---| +| Sandbox isolation | Yes. OpenShell applies seccomp filters, Landlock filesystem restrictions, privilege dropping, network namespace isolation, and no-new-privileges enforcement. The community sandbox bundles its own policy tailored for OpenClaw. | Yes. NemoClaw applies these through the blueprint and layers a more restrictive policy on top (see rows below). | +| Credential handling | OpenShell's provider system replaces real credentials with placeholder tokens in the sandbox environment. The L7 proxy resolves placeholders to real values at egress. You create providers manually with `openshell provider create`. | NemoClaw creates OpenShell providers automatically during onboarding. It also filters sensitive host environment variables (provider API keys, `DISCORD_BOT_TOKEN`, `SLACK_BOT_TOKEN`, `TELEGRAM_BOT_TOKEN`) from the sandbox creation command to prevent accidental leakage through build args. | +| Image hardening | The community image includes standard system tools for general-purpose use. | NemoClaw strips build toolchains (`gcc`, `g++`, `make`) and network probes (`netcat`) from the runtime image to reduce attack surface. | +| Filesystem policy | The community sandbox bundles a policy for OpenClaw. | NemoClaw defines a targeted read-only and read-write layout. System paths (`/usr`, `/lib`, `/etc`) are read-only. The agent's home directory (`/sandbox`) and config directory (`/sandbox/.openclaw`) are writable by default so the agent can manage config, install skills, and write to standard paths natively. | +| Inference setup | The community sandbox includes an `openclaw-start` script that runs OpenClaw's onboarding wizard inside the sandbox. You can also create providers and configure OpenShell inference routing manually from the host. | NemoClaw's onboarding wizard validates your credential from the host, lets you select a provider (NVIDIA Endpoints, OpenAI, Anthropic, Google Gemini, Ollama, and compatible endpoints), and configures OpenShell's inference routing automatically. Credentials stay on the host and are delivered through OpenShell's provider system. | +| Channel messaging | OpenShell provides the credential provider system and L7 proxy that delivers channel tokens securely (including path-based resolution for Telegram's `/bot/` URL pattern). You create providers and configure OpenClaw's channel settings manually. | NemoClaw automates channel setup during onboarding: it collects bot tokens, registers them as OpenShell providers, and bakes OpenClaw channel config with placeholder tokens that OpenShell's proxy resolves at egress. No separate bridge process runs on the host. | +| Blueprint versioning | No blueprint. The community sandbox uses whatever image version is currently published. | NemoClaw downloads the blueprint artifact, checks version compatibility, and verifies its digest before applying. Running `nemoclaw onboard` on different machines produces the same sandbox. | +| State migration | Not included. | NemoClaw migrates agent state across machines with credential stripping and integrity verification. | +| Process count limits | OpenShell applies seccomp and privilege dropping. You set process count limits manually with `--ulimit` or orchestrator config. | NemoClaw applies `ulimit -u 512` in the container entrypoint to cap the process count and mitigate fork-bomb attacks, on top of OpenShell's seccomp and privilege dropping. | + +## When to Use Which + +Use the following table to decide when to use NemoClaw versus OpenShell. + +| Situation | Prefer | +|-----------|--------| +| You want OpenClaw with minimal assembly, NVIDIA defaults, and the documented install and onboard flow. | NemoClaw | +| You need maximum flexibility for custom images, a layout that does not match the NemoClaw blueprint, or a workload outside this reference stack. | OpenShell with your own integration | +| You are standardizing on the NVIDIA reference for always-on assistants with policy and inference routing. | NemoClaw | +| You are building internal platform abstractions where the NemoClaw CLI or blueprint is not the right fit. | OpenShell (and your orchestration) | + +## Related topics + +- [Overview](overview.md) contains what NemoClaw is, capabilities, benefits, and use cases. +- [How It Works](how-it-works.md) describes how NemoClaw runs, plugin, blueprint, sandbox creation, routing, protection layers. +- Architecture (use the `nemoclaw-user-reference` skill) shows the repository structure and technical diagrams. diff --git a/.agents/skills/nemoclaw-user-overview/references/how-it-works.md b/.agents/skills/nemoclaw-user-overview/references/how-it-works.md index 8ffb5f4ea3..b0f9f4a240 100644 --- a/.agents/skills/nemoclaw-user-overview/references/how-it-works.md +++ b/.agents/skills/nemoclaw-user-overview/references/how-it-works.md @@ -98,7 +98,7 @@ For details on the baseline rules, refer to Network Policies (use the `nemoclaw- ## Next Steps -- Read [Ecosystem](../SKILL.md) for stack-level relationships and NemoClaw versus OpenShell-only paths. +- Read [Ecosystem](ecosystem.md) for stack-level relationships and NemoClaw versus OpenShell-only paths. - Follow the Quickstart (use the `nemoclaw-user-get-started` skill) to launch your first sandbox. - Refer to the Architecture (use the `nemoclaw-user-reference` skill) for the full technical structure, including file layouts and the blueprint lifecycle. - Refer to Inference Options (use the `nemoclaw-user-configure-inference` skill) for detailed provider configuration. diff --git a/.agents/skills/nemoclaw-user-overview/references/overview.md b/.agents/skills/nemoclaw-user-overview/references/overview.md index e2ef08440a..39c387a537 100644 --- a/.agents/skills/nemoclaw-user-overview/references/overview.md +++ b/.agents/skills/nemoclaw-user-overview/references/overview.md @@ -59,7 +59,7 @@ You can use NemoClaw for various use cases including the following. Navigate to the following topics to learn more about NemoClaw and how to install and use it. - [Architecture Overview](how-it-works.md) to understand how NemoClaw works. -- [Ecosystem](../SKILL.md) to understand how OpenClaw, OpenShell, and NemoClaw relate in the wider stack, and when to use NemoClaw versus OpenShell. +- [Ecosystem](ecosystem.md) to understand how OpenClaw, OpenShell, and NemoClaw relate in the wider stack, and when to use NemoClaw versus OpenShell. - Quickstart (use the `nemoclaw-user-get-started` skill) to install NemoClaw and run your first sandboxed agent. - Agent Skills (use the `nemoclaw-user-agent-skills` skill) to load NemoClaw guidance into an AI coding assistant. - Inference Options (use the `nemoclaw-user-configure-inference` skill) to check the inference providers that NemoClaw supports and how inference routing works. diff --git a/.agents/skills/nemoclaw-user-reference/SKILL.md b/.agents/skills/nemoclaw-user-reference/SKILL.md index c5b980a93e..c1791c9e14 100644 --- a/.agents/skills/nemoclaw-user-reference/SKILL.md +++ b/.agents/skills/nemoclaw-user-reference/SKILL.md @@ -7,267 +7,11 @@ license: "Apache-2.0" -# Architecture Details - -NemoClaw combines a host CLI, a TypeScript plugin that runs with OpenClaw inside the sandbox, and a versioned YAML blueprint that defines the sandbox image, policies, and inference profiles applied through OpenShell. - -## System Overview - -NVIDIA OpenShell is a general-purpose agent runtime. It provides sandbox containers, a credential-storing gateway, inference proxying, and policy enforcement, but has no opinions about what runs inside. NemoClaw is an opinionated reference stack built on OpenShell that handles what goes in the sandbox and makes the setup accessible. - -```mermaid -graph LR - classDef nemoclaw fill:#76b900,stroke:#5a8f00,color:#fff,stroke-width:2px,font-weight:bold - classDef openshell fill:#1a1a1a,stroke:#1a1a1a,color:#fff,stroke-width:2px,font-weight:bold - classDef sandbox fill:#444,stroke:#76b900,color:#fff,stroke-width:2px,font-weight:bold - classDef agent fill:#f5f5f5,stroke:#e0e0e0,color:#1a1a1a,stroke-width:1px - classDef external fill:#f5f5f5,stroke:#e0e0e0,color:#1a1a1a,stroke-width:1px - classDef user fill:#fff,stroke:#76b900,color:#1a1a1a,stroke-width:2px,font-weight:bold - - USER(["👤 User"]):::user - - subgraph EXTERNAL["External Services"] - INFERENCE["Inference Provider
NVIDIA Endpoints · OpenAI
Anthropic · Ollama · vLLM · Model Router
"]:::external - MSGAPI["Messaging Platforms
Telegram · Discord · Slack"]:::external - INTERNET["Internet
PyPI · npm · GitHub · APIs"]:::external - end - - subgraph HOST["Host Machine"] - - subgraph NEMOCLAW["NemoClaw"] - direction TB - NCLI["CLI + Onboarding
Guided setup · provider selection
credential validation · deploy
"]:::nemoclaw - BP["Blueprint
Hardened Dockerfile
Network policies · Presets
Security configuration
"]:::nemoclaw - MIGRATE["State Management
Migration snapshots
Credential stripping
Integrity verification
"]:::nemoclaw - end - - subgraph OPENSHELL["OpenShell"] - direction TB - GW["Gateway
Credential store
Inference proxy
Policy engine
Device auth
"]:::openshell - OSCLI["openshell CLI
provider · sandbox
gateway · policy
"]:::openshell - CHMSG["Channel messaging
OpenShell-managed
Telegram · Discord · Slack
"]:::openshell - - subgraph SANDBOX["Sandbox Container 🔒"] - direction TB - AGENT["Agent
OpenClaw or any
compatible agent
"]:::agent - PLUG["NemoClaw Plugin
Extends agent with
managed configuration
"]:::sandbox - end - end - end - - USER -->|"nemoclaw onboard
nemoclaw connect"| NCLI - USER -->|"Chat messages"| MSGAPI - - NCLI -->|"Orchestrates"| OSCLI - BP -->|"Defines sandbox
shape + policies"| SANDBOX - MIGRATE -->|"Safe state
transfer"| SANDBOX - - AGENT -->|"Inference requests
no credentials"| GW - GW -->|"Proxied with
credential injected"| INFERENCE - - MSGAPI -->|"Platform APIs"| CHMSG - CHMSG -->|"Deliver to agent"| AGENT - - AGENT -.->|"Policy-gated"| INTERNET - GW -.->|"Enforced by
gateway"| INTERNET -``` - -## Deployment Topology - -The logical diagram above shows how components relate. -This section shows what actually runs where on the host. -NemoClaw's default Docker-driver topology does not place the sandbox in an embedded k3s cluster. -On Linux and Apple Silicon macOS, NemoClaw starts the OpenShell Docker-driver gateway and creates the sandbox as a Docker container. -The gateway normally runs as a host process; Linux hosts that need the gateway compatibility patch may run the same gateway binary inside a small container. -In both Docker-driver modes, the sandbox is a Docker container, not a Kubernetes pod. -Legacy non-Docker-driver installs still use the k3s-based gateway path; the diagram below shows the standard Docker-driver topology. - -```mermaid -graph TB - classDef host fill:#fff,stroke:#76b900,stroke-width:2px,color:#1a1a1a,font-weight:bold - classDef cli fill:#76b900,stroke:#5a8f00,color:#fff,stroke-width:2px,font-weight:bold - classDef docker fill:#2496ed,stroke:#1577c2,color:#fff,stroke-width:2px,font-weight:bold - classDef gateway fill:#1a1a1a,stroke:#1a1a1a,color:#fff,stroke-width:2px,font-weight:bold - classDef sandbox fill:#444,stroke:#76b900,color:#fff,stroke-width:2px - classDef external fill:#f5f5f5,stroke:#e0e0e0,color:#1a1a1a,stroke-width:1px - - subgraph HOST["Host machine · Linux / Apple Silicon macOS / DGX Spark / DGX Station"] - direction TB - CLI["nemoclaw CLI
bin/nemoclaw.js → dist/
onboard · connect · status · logs
"]:::cli - GW["OpenShell gateway
host process by default
credential store · lifecycle · L7 proxy
"]:::gateway - - subgraph DOCKER["Docker daemon"] - direction TB - SANDBOX["Sandbox container 🔒
Landlock + seccomp + netns
OpenClaw agent + NemoClaw plugin
"]:::sandbox - end - end - - INFER["Inference provider
NVIDIA Endpoints · OpenAI
Anthropic · Ollama · vLLM · Model Router
"]:::external - - CLI -->|"openshell CLI
(orchestrates)"| GW - GW -->|"creates/recreates
Docker-driver sandbox"| SANDBOX - SANDBOX -->|"inference requests
placeholder credentials"| GW - GW -->|"egress with real credentials
injected at the L7 proxy"| INFER - - class HOST host - class DOCKER docker - class GW gateway - class SANDBOX sandbox -``` - -Layering from top to bottom: - -| Layer | Runs as | Role | -|---|---|---| -| Host CLI | Host process (`nemoclaw` on Node.js) | Orchestrates OpenShell via `openshell` CLI calls. | -| OpenShell gateway | Host process by default; optional Linux compatibility container when the gateway binary needs a newer host ABI | Hosts the credential store, owns sandbox lifecycle coordination, and provides the L7 proxy. | -| Docker daemon | Host service | Runs the Docker-driver sandbox container and, on affected Linux hosts, the optional gateway compatibility container. | -| Sandbox container | Docker container | Runs the OpenClaw agent and the NemoClaw plugin under Landlock + seccomp + netns. | -| OpenShell L7 proxy | Gateway process | Intercepts agent egress and rewrites `Authorization` headers (Bearer/Bot) and URL-path segments to inject the real credential at the network boundary. | - -NemoClaw never gives the sandbox a raw provider key. -At onboard time it registers credentials with OpenShell's provider/placeholder system, and the L7 proxy substitutes the real value into outbound requests at egress. -The CLI helper `isInferenceRouteReady` (in `src/lib/onboard.ts`) is a host-side readiness check used by the resume flow to decide whether the active route already covers the chosen provider and model — it is not a runtime component. - -For the DGX Spark-specific variant of this topology (cgroup v2, aarch64, unified memory), refer to the [NVIDIA Spark playbook](https://build.nvidia.com/spark/nemoclaw). - -## NemoClaw Plugin - -The plugin is a thin TypeScript package that registers an inference provider and the `/nemoclaw` slash command. -It runs in-process with the OpenClaw gateway inside the sandbox. -It also registers runtime hooks that keep the agent aware of its environment. -Before an agent turn starts, the plugin prepends a short context block with the active sandbox name, sandbox phase, network policy summary, and filesystem policy summary. -When the policy or phase changes during a session, the plugin sends a smaller update block instead of repeating the full context. - -```text -nemoclaw/ -├── src/ -│ ├── index.ts Plugin entry: registers all commands -│ ├── cli.ts Commander.js subcommand wiring -│ ├── runtime-context.ts Sandbox and policy context injection -│ ├── commands/ -│ │ ├── launch.ts Fresh install into OpenShell -│ │ ├── connect.ts Interactive shell into sandbox -│ │ ├── status.ts Blueprint run state + sandbox health -│ │ ├── logs.ts Stream blueprint and sandbox logs -│ │ └── slash.ts /nemoclaw chat command handler -│ └── blueprint/ -│ ├── resolve.ts Version resolution, cache management -│ ├── fetch.ts Download blueprint from OCI registry -│ ├── verify.ts Digest verification, compatibility checks -│ ├── exec.ts Subprocess execution of blueprint runner -│ └── state.ts Persistent state (run IDs) -├── openclaw.plugin.json Plugin manifest -└── package.json Commands declared under openclaw.extensions -``` - -## NemoClaw Blueprint - -The blueprint is a versioned YAML package with its own release stream. -The runner resolves, verifies, and applies the blueprint through the OpenShell CLI. -The blueprint defines the sandbox shape, default policies, and inference profiles; the runner performs the OpenShell operations. - -```text -nemoclaw-blueprint/ -├── blueprint.yaml Manifest: version, profiles, compatibility -├── model-specific-setup/ Agent-scoped model/provider compatibility manifests -├── router/ Model Router config and routing engine -├── policies/ -│ └── openclaw-sandbox.yaml Default network + filesystem policy -``` - -The blueprint runtime (TypeScript) lives in the plugin source tree: - -```text -nemoclaw/src/blueprint/ -├── runner.ts CLI runner: plan / apply / status / rollback -├── ssrf.ts SSRF endpoint validation (IP + DNS checks) -├── snapshot.ts Migration snapshot / restore lifecycle -├── state.ts Persistent run state management -``` - -### Blueprint Lifecycle - -```mermaid -flowchart LR - A[resolve] --> B[verify digest] - B --> C[plan] - C --> D[apply] - D --> E[status] -``` - -1. Resolve. The plugin locates the blueprint artifact and checks the version against `min_openshell_version` and `min_openclaw_version` constraints in `blueprint.yaml`. -2. Verify. The plugin checks the artifact digest against the expected value. -3. Plan. The runner determines what OpenShell resources to create or update, such as the gateway, providers, sandbox, inference route, and policy. -4. Apply. The runner executes the plan by calling `openshell` CLI commands. -5. Status. The runner reports current state. - -## Sandbox Environment - -Normal NemoClaw onboarding builds from the -[`ghcr.io/nvidia/nemoclaw/sandbox-base`](https://github.com/NVIDIA/NemoClaw/pkgs/container/nemoclaw%2Fsandbox-base) -base image and layers the NemoClaw runtime Dockerfile on top. The direct blueprint -runner still carries a pinned OpenShell Community OpenClaw image for legacy -`openshell sandbox create --from` compatibility. Inside the sandbox: - -- OpenClaw runs with the NemoClaw plugin pre-installed. -- Inference calls are routed through OpenShell to the configured provider. -- Network egress is restricted by the baseline policy in `openclaw-sandbox.yaml`. -- Filesystem access is confined to `/sandbox` and `/tmp` for read-write access, with system paths read-only. -- The NemoClaw plugin injects sandbox and policy context into agent turns so the agent can report policy blocks accurately. -- The image exposes a Docker health check that probes the in-sandbox gateway, so container runtimes can report whether the agent service is responding. -- The image includes common runtime compatibility helpers such as Homebrew and a `python` to `python3` symlink for tools that still invoke `python`. - -## Inference Routing - -Inference requests from the agent never leave the sandbox directly. -OpenShell intercepts them and routes to the configured provider: - -```text -Agent (sandbox) ──▶ OpenShell gateway ──▶ NVIDIA Endpoint (build.nvidia.com) -``` - -When you select the Model Router provider, the OpenShell gateway routes to a host-side router process instead of a single upstream model. -The router selects from the configured pool, then calls the upstream NVIDIA endpoint with the credential held outside the sandbox. - -Some model and provider combinations need agent-specific compatibility setup. -NemoClaw keeps those declarations under `nemoclaw-blueprint/model-specific-setup//` so OpenClaw and Hermes fixes can be tested and reviewed independently. - -Refer to Inference Options (use the `nemoclaw-user-configure-inference` skill) for provider configuration details. - -## Provider Credential Storage - -Provider credentials live in the OpenShell gateway store, not on the host filesystem. -NemoClaw never writes them to host disk; the OpenShell L7 proxy injects values at egress. -See Credential Storage (use the `nemoclaw-user-configure-security` skill) for the inspection, rotation, and migration flow. - -## Host-Side State and Config - -NemoClaw keeps non-secret operator-facing state on the host rather than inside the sandbox. - -| Path | Purpose | -|---|---| -| `~/.nemoclaw/sandboxes.json` | Registered sandbox metadata, including the default sandbox selection. | -| `~/.openclaw/openclaw.json` | Host OpenClaw configuration that NemoClaw snapshots or restores during migration flows. | - -The following environment variables configure optional services and local access. - -| Variable | Purpose | -|---|---| -| `TELEGRAM_BOT_TOKEN` | Telegram bot token you provide before `nemoclaw onboard`. OpenShell stores it in a provider; the sandbox receives placeholders, not the raw secret. | -| `TELEGRAM_ALLOWED_IDS` | Comma-separated Telegram user or chat IDs for allowlists when onboarding applies channel restrictions. | -| `SLACK_BOT_TOKEN` | Slack bot token (`xoxb-...`) you provide before `nemoclaw onboard`. Stored as an OpenShell provider; never passed directly to the sandbox. | -| `SLACK_APP_TOKEN` | Slack app-level token (`xapp-...`) required for Socket Mode. Stored alongside `SLACK_BOT_TOKEN` during onboarding. | -| `SLACK_ALLOWED_USERS` | Comma-separated Slack member IDs for DM and channel `@mention` user allowlisting. | -| `SLACK_ALLOWED_CHANNELS` | Comma-separated Slack channel IDs where channel `@mention` events are enabled (e.g. `C012AB3CD,C987ZY6XW`). Baked into the sandbox image at build time. Combine with `SLACK_ALLOWED_USERS` to restrict both channel and member. | -| `CHAT_UI_URL` | URL for the optional chat UI endpoint. | -| `NEMOCLAW_DISABLE_DEVICE_AUTH` | Build-time-only toggle that disables gateway device pairing when set to `1` before the sandbox image is created. | - -For normal setup and reconfiguration, prefer `nemoclaw onboard` over editing these files by hand. -Do not treat `NEMOCLAW_DISABLE_DEVICE_AUTH` as a runtime setting for an already-created sandbox. +# NemoClaw User Reference ## References +- **Load [references/architecture.md](references/architecture.md)** when looking up architecture, plugin structure, or blueprint design. Describes the NemoClaw plugin and blueprint architecture and how they orchestrate the OpenClaw sandbox. - **[references/cli-selection-guide.md](references/cli-selection-guide.md)** — Explains when to use `nemoclaw` versus `openshell` for NemoClaw-managed sandboxes, including lifecycle, inference, policy, monitoring, file transfer, and gateway operations. - **Load [references/commands.md](references/commands.md)** when looking up a specific `nemoclaw` or `/nemoclaw` subcommand, flag, argument, or exit code. Includes the full CLI reference for slash commands and standalone NemoClaw commands. - **Load [references/network-policies.md](references/network-policies.md)** when looking up a specific default endpoint, filesystem path, or the runtime approval sequence NemoClaw applies on blocked requests. Covers the baseline network policy, filesystem rules, and operator approval flow. diff --git a/.agents/skills/nemoclaw-user-reference/references/architecture.md b/.agents/skills/nemoclaw-user-reference/references/architecture.md new file mode 100644 index 0000000000..07e193434a --- /dev/null +++ b/.agents/skills/nemoclaw-user-reference/references/architecture.md @@ -0,0 +1,260 @@ + + +# Architecture Details + +NemoClaw combines a host CLI, a TypeScript plugin that runs with OpenClaw inside the sandbox, and a versioned YAML blueprint that defines the sandbox image, policies, and inference profiles applied through OpenShell. + +## System Overview + +NVIDIA OpenShell is a general-purpose agent runtime. It provides sandbox containers, a credential-storing gateway, inference proxying, and policy enforcement, but has no opinions about what runs inside. NemoClaw is an opinionated reference stack built on OpenShell that handles what goes in the sandbox and makes the setup accessible. + +```mermaid +graph LR + classDef nemoclaw fill:#76b900,stroke:#5a8f00,color:#fff,stroke-width:2px,font-weight:bold + classDef openshell fill:#1a1a1a,stroke:#1a1a1a,color:#fff,stroke-width:2px,font-weight:bold + classDef sandbox fill:#444,stroke:#76b900,color:#fff,stroke-width:2px,font-weight:bold + classDef agent fill:#f5f5f5,stroke:#e0e0e0,color:#1a1a1a,stroke-width:1px + classDef external fill:#f5f5f5,stroke:#e0e0e0,color:#1a1a1a,stroke-width:1px + classDef user fill:#fff,stroke:#76b900,color:#1a1a1a,stroke-width:2px,font-weight:bold + + USER(["👤 User"]):::user + + subgraph EXTERNAL["External Services"] + INFERENCE["Inference Provider
NVIDIA Endpoints · OpenAI
Anthropic · Ollama · vLLM · Model Router
"]:::external + MSGAPI["Messaging Platforms
Telegram · Discord · Slack"]:::external + INTERNET["Internet
PyPI · npm · GitHub · APIs"]:::external + end + + subgraph HOST["Host Machine"] + + subgraph NEMOCLAW["NemoClaw"] + direction TB + NCLI["CLI + Onboarding
Guided setup · provider selection
credential validation · deploy
"]:::nemoclaw + BP["Blueprint
Hardened Dockerfile
Network policies · Presets
Security configuration
"]:::nemoclaw + MIGRATE["State Management
Migration snapshots
Credential stripping
Integrity verification
"]:::nemoclaw + end + + subgraph OPENSHELL["OpenShell"] + direction TB + GW["Gateway
Credential store
Inference proxy
Policy engine
Device auth
"]:::openshell + OSCLI["openshell CLI
provider · sandbox
gateway · policy
"]:::openshell + CHMSG["Channel messaging
OpenShell-managed
Telegram · Discord · Slack
"]:::openshell + + subgraph SANDBOX["Sandbox Container 🔒"] + direction TB + AGENT["Agent
OpenClaw or any
compatible agent
"]:::agent + PLUG["NemoClaw Plugin
Extends agent with
managed configuration
"]:::sandbox + end + end + end + + USER -->|"nemoclaw onboard
nemoclaw connect"| NCLI + USER -->|"Chat messages"| MSGAPI + + NCLI -->|"Orchestrates"| OSCLI + BP -->|"Defines sandbox
shape + policies"| SANDBOX + MIGRATE -->|"Safe state
transfer"| SANDBOX + + AGENT -->|"Inference requests
no credentials"| GW + GW -->|"Proxied with
credential injected"| INFERENCE + + MSGAPI -->|"Platform APIs"| CHMSG + CHMSG -->|"Deliver to agent"| AGENT + + AGENT -.->|"Policy-gated"| INTERNET + GW -.->|"Enforced by
gateway"| INTERNET +``` + +## Deployment Topology + +The logical diagram above shows how components relate. +This section shows what actually runs where on the host. +NemoClaw's default Docker-driver topology does not place the sandbox in an embedded k3s cluster. +On Linux and Apple Silicon macOS, NemoClaw starts the OpenShell Docker-driver gateway and creates the sandbox as a Docker container. +The gateway normally runs as a host process; Linux hosts that need the gateway compatibility patch may run the same gateway binary inside a small container. +In both Docker-driver modes, the sandbox is a Docker container, not a Kubernetes pod. +Legacy non-Docker-driver installs still use the k3s-based gateway path; the diagram below shows the standard Docker-driver topology. + +```mermaid +graph TB + classDef host fill:#fff,stroke:#76b900,stroke-width:2px,color:#1a1a1a,font-weight:bold + classDef cli fill:#76b900,stroke:#5a8f00,color:#fff,stroke-width:2px,font-weight:bold + classDef docker fill:#2496ed,stroke:#1577c2,color:#fff,stroke-width:2px,font-weight:bold + classDef gateway fill:#1a1a1a,stroke:#1a1a1a,color:#fff,stroke-width:2px,font-weight:bold + classDef sandbox fill:#444,stroke:#76b900,color:#fff,stroke-width:2px + classDef external fill:#f5f5f5,stroke:#e0e0e0,color:#1a1a1a,stroke-width:1px + + subgraph HOST["Host machine · Linux / Apple Silicon macOS / DGX Spark / DGX Station"] + direction TB + CLI["nemoclaw CLI
bin/nemoclaw.js → dist/
onboard · connect · status · logs
"]:::cli + GW["OpenShell gateway
host process by default
credential store · lifecycle · L7 proxy
"]:::gateway + + subgraph DOCKER["Docker daemon"] + direction TB + SANDBOX["Sandbox container 🔒
Landlock + seccomp + netns
OpenClaw agent + NemoClaw plugin
"]:::sandbox + end + end + + INFER["Inference provider
NVIDIA Endpoints · OpenAI
Anthropic · Ollama · vLLM · Model Router
"]:::external + + CLI -->|"openshell CLI
(orchestrates)"| GW + GW -->|"creates/recreates
Docker-driver sandbox"| SANDBOX + SANDBOX -->|"inference requests
placeholder credentials"| GW + GW -->|"egress with real credentials
injected at the L7 proxy"| INFER + + class HOST host + class DOCKER docker + class GW gateway + class SANDBOX sandbox +``` + +Layering from top to bottom: + +| Layer | Runs as | Role | +|---|---|---| +| Host CLI | Host process (`nemoclaw` on Node.js) | Orchestrates OpenShell via `openshell` CLI calls. | +| OpenShell gateway | Host process by default; optional Linux compatibility container when the gateway binary needs a newer host ABI | Hosts the credential store, owns sandbox lifecycle coordination, and provides the L7 proxy. | +| Docker daemon | Host service | Runs the Docker-driver sandbox container and, on affected Linux hosts, the optional gateway compatibility container. | +| Sandbox container | Docker container | Runs the OpenClaw agent and the NemoClaw plugin under Landlock + seccomp + netns. | +| OpenShell L7 proxy | Gateway process | Intercepts agent egress and rewrites `Authorization` headers (Bearer/Bot) and URL-path segments to inject the real credential at the network boundary. | + +NemoClaw never gives the sandbox a raw provider key. +At onboard time it registers credentials with OpenShell's provider/placeholder system, and the L7 proxy substitutes the real value into outbound requests at egress. +The CLI helper `isInferenceRouteReady` (in `src/lib/onboard.ts`) is a host-side readiness check used by the resume flow to decide whether the active route already covers the chosen provider and model — it is not a runtime component. + +For the DGX Spark-specific variant of this topology (cgroup v2, aarch64, unified memory), refer to the [NVIDIA Spark playbook](https://build.nvidia.com/spark/nemoclaw). + +## NemoClaw Plugin + +The plugin is a thin TypeScript package that registers an inference provider and the `/nemoclaw` slash command. +It runs in-process with the OpenClaw gateway inside the sandbox. +It also registers runtime hooks that keep the agent aware of its environment. +Before an agent turn starts, the plugin prepends a short context block with the active sandbox name, sandbox phase, network policy summary, and filesystem policy summary. +When the policy or phase changes during a session, the plugin sends a smaller update block instead of repeating the full context. + +```text +nemoclaw/ +├── src/ +│ ├── index.ts Plugin entry: registers all commands +│ ├── cli.ts Commander.js subcommand wiring +│ ├── runtime-context.ts Sandbox and policy context injection +│ ├── commands/ +│ │ ├── launch.ts Fresh install into OpenShell +│ │ ├── connect.ts Interactive shell into sandbox +│ │ ├── status.ts Blueprint run state + sandbox health +│ │ ├── logs.ts Stream blueprint and sandbox logs +│ │ └── slash.ts /nemoclaw chat command handler +│ └── blueprint/ +│ ├── resolve.ts Version resolution, cache management +│ ├── fetch.ts Download blueprint from OCI registry +│ ├── verify.ts Digest verification, compatibility checks +│ ├── exec.ts Subprocess execution of blueprint runner +│ └── state.ts Persistent state (run IDs) +├── openclaw.plugin.json Plugin manifest +└── package.json Commands declared under openclaw.extensions +``` + +## NemoClaw Blueprint + +The blueprint is a versioned YAML package with its own release stream. +The runner resolves, verifies, and applies the blueprint through the OpenShell CLI. +The blueprint defines the sandbox shape, default policies, and inference profiles; the runner performs the OpenShell operations. + +```text +nemoclaw-blueprint/ +├── blueprint.yaml Manifest: version, profiles, compatibility +├── model-specific-setup/ Agent-scoped model/provider compatibility manifests +├── router/ Model Router config and routing engine +├── policies/ +│ └── openclaw-sandbox.yaml Default network + filesystem policy +``` + +The blueprint runtime (TypeScript) lives in the plugin source tree: + +```text +nemoclaw/src/blueprint/ +├── runner.ts CLI runner: plan / apply / status / rollback +├── ssrf.ts SSRF endpoint validation (IP + DNS checks) +├── snapshot.ts Migration snapshot / restore lifecycle +├── state.ts Persistent run state management +``` + +### Blueprint Lifecycle + +```mermaid +flowchart LR + A[resolve] --> B[verify digest] + B --> C[plan] + C --> D[apply] + D --> E[status] +``` + +1. Resolve. The plugin locates the blueprint artifact and checks the version against `min_openshell_version` and `min_openclaw_version` constraints in `blueprint.yaml`. +2. Verify. The plugin checks the artifact digest against the expected value. +3. Plan. The runner determines what OpenShell resources to create or update, such as the gateway, providers, sandbox, inference route, and policy. +4. Apply. The runner executes the plan by calling `openshell` CLI commands. +5. Status. The runner reports current state. + +## Sandbox Environment + +Normal NemoClaw onboarding builds from the +[`ghcr.io/nvidia/nemoclaw/sandbox-base`](https://github.com/NVIDIA/NemoClaw/pkgs/container/nemoclaw%2Fsandbox-base) +base image and layers the NemoClaw runtime Dockerfile on top. The direct blueprint +runner still carries a pinned OpenShell Community OpenClaw image for legacy +`openshell sandbox create --from` compatibility. Inside the sandbox: + +- OpenClaw runs with the NemoClaw plugin pre-installed. +- Inference calls are routed through OpenShell to the configured provider. +- Network egress is restricted by the baseline policy in `openclaw-sandbox.yaml`. +- Filesystem access is confined to `/sandbox` and `/tmp` for read-write access, with system paths read-only. +- The NemoClaw plugin injects sandbox and policy context into agent turns so the agent can report policy blocks accurately. +- The image exposes a Docker health check that probes the in-sandbox gateway, so container runtimes can report whether the agent service is responding. +- The image includes common runtime compatibility helpers such as Homebrew and a `python` to `python3` symlink for tools that still invoke `python`. + +## Inference Routing + +Inference requests from the agent never leave the sandbox directly. +OpenShell intercepts them and routes to the configured provider: + +```text +Agent (sandbox) ──▶ OpenShell gateway ──▶ NVIDIA Endpoint (build.nvidia.com) +``` + +When you select the Model Router provider, the OpenShell gateway routes to a host-side router process instead of a single upstream model. +The router selects from the configured pool, then calls the upstream NVIDIA endpoint with the credential held outside the sandbox. + +Some model and provider combinations need agent-specific compatibility setup. +NemoClaw keeps those declarations under `nemoclaw-blueprint/model-specific-setup//` so OpenClaw and Hermes fixes can be tested and reviewed independently. + +Refer to Inference Options (use the `nemoclaw-user-configure-inference` skill) for provider configuration details. + +## Provider Credential Storage + +Provider credentials live in the OpenShell gateway store, not on the host filesystem. +NemoClaw never writes them to host disk; the OpenShell L7 proxy injects values at egress. +See Credential Storage (use the `nemoclaw-user-configure-security` skill) for the inspection, rotation, and migration flow. + +## Host-Side State and Config + +NemoClaw keeps non-secret operator-facing state on the host rather than inside the sandbox. + +| Path | Purpose | +|---|---| +| `~/.nemoclaw/sandboxes.json` | Registered sandbox metadata, including the default sandbox selection. | +| `~/.openclaw/openclaw.json` | Host OpenClaw configuration that NemoClaw snapshots or restores during migration flows. | + +The following environment variables configure optional services and local access. + +| Variable | Purpose | +|---|---| +| `TELEGRAM_BOT_TOKEN` | Telegram bot token you provide before `nemoclaw onboard`. OpenShell stores it in a provider; the sandbox receives placeholders, not the raw secret. | +| `TELEGRAM_ALLOWED_IDS` | Comma-separated Telegram user or chat IDs for allowlists when onboarding applies channel restrictions. | +| `SLACK_BOT_TOKEN` | Slack bot token (`xoxb-...`) you provide before `nemoclaw onboard`. Stored as an OpenShell provider; never passed directly to the sandbox. | +| `SLACK_APP_TOKEN` | Slack app-level token (`xapp-...`) required for Socket Mode. Stored alongside `SLACK_BOT_TOKEN` during onboarding. | +| `SLACK_ALLOWED_USERS` | Comma-separated Slack member IDs for DM and channel `@mention` user allowlisting. | +| `SLACK_ALLOWED_CHANNELS` | Comma-separated Slack channel IDs where channel `@mention` events are enabled (e.g. `C012AB3CD,C987ZY6XW`). Baked into the sandbox image at build time. Combine with `SLACK_ALLOWED_USERS` to restrict both channel and member. | +| `CHAT_UI_URL` | URL for the optional chat UI endpoint. | +| `NEMOCLAW_DISABLE_DEVICE_AUTH` | Build-time-only toggle that disables gateway device pairing when set to `1` before the sandbox image is created. | + +For normal setup and reconfiguration, prefer `nemoclaw onboard` over editing these files by hand. +Do not treat `NEMOCLAW_DISABLE_DEVICE_AUTH` as a runtime setting for an already-created sandbox. diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index eba55e56d3..f6a5673958 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -95,10 +95,11 @@ Other useful flags: ### How the Script Works The script reads YAML frontmatter from each doc page to determine its content type (`how_to`, `concept`, `reference`, `get_started`), then groups pages into skills using the `grouped` strategy by default. -Within each directory group, the page with the lowest `skill.priority` value (highest priority) becomes the full body of `SKILL.md`. +Within each directory group, the highest-priority procedure page (`how_to`, `get_started`, or `tutorial`) becomes the full body of `SKILL.md`. Sibling pages are written unchanged to `references/`. +Groups with no procedure page keep every sibling in `references/` only. -Use `--strategy individual` to emit one skill per `how_to`, `get_started`, or `tutorial` page and collect all other pages into a single reference skill. +Use `--strategy individual` to emit one skill per `how_to`, `get_started`, or `tutorial` page, collect `concept` pages into `nemoclaw-user-concept`, and collect `reference` pages (and other non-procedure types) into `nemoclaw-user-reference`. Cross-references between doc pages are rewritten as skill-to-skill pointers so agents can navigate between skills. Fern MDX components and MyST/Sphinx directives are converted to standard markdown. diff --git a/docs/about/overview.mdx b/docs/about/overview.mdx index 1b7c9a8e0f..e387c3dee1 100644 --- a/docs/about/overview.mdx +++ b/docs/about/overview.mdx @@ -8,6 +8,8 @@ description-agent: "Explains what NemoClaw covers: onboarding, lifecycle managem keywords: ["nemoclaw overview", "openclaw always-on assistants", "nvidia openshell", "nvidia nemotron"] content: type: "concept" +skill: + priority: 10 --- NVIDIA NemoClaw is an open-source reference stack that simplifies running [OpenClaw](https://openclaw.ai) always-on assistants more safely. NemoClaw provides onboarding, lifecycle management, and OpenClaw operations within OpenShell containers. diff --git a/docs/resources/agent-skills.mdx b/docs/resources/agent-skills.mdx index 105a348d84..e701d6ff43 100644 --- a/docs/resources/agent-skills.mdx +++ b/docs/resources/agent-skills.mdx @@ -7,7 +7,7 @@ description: "NemoClaw ships agent skills that let AI coding assistants guide yo description-agent: "Describes the agent skills shipped with NemoClaw and how to access them by cloning the repository. Use when users ask about AI agent support, coding assistant integration, or the .agents/skills/ directory." keywords: ["nemoclaw agent skills", "ai coding assistant", "cursor", "claude code", "copilot"] content: - type: "concept" + type: "how_to" --- NemoClaw ships agent skills that are generated directly from this documentation. Each skill is a converted version of one or more doc pages, structured so AI coding assistants can consume it as context. diff --git a/scripts/docs-to-skills.py b/scripts/docs-to-skills.py index daa2791b89..67527542fd 100755 --- a/scripts/docs-to-skills.py +++ b/scripts/docs-to-skills.py @@ -19,12 +19,14 @@ 2. Classifies each page by content type (how_to, concept, reference, get_started) using the frontmatter `content.type` field. 3. Groups pages into skills using one of two strategies: - - grouped (default): groups by parent directory; the page with the - lowest frontmatter ``skill.priority`` (highest priority) becomes the - full SKILL.md body; siblings go to ``references/``. + - grouped (default): groups by parent directory; the highest-priority + procedure page (``how_to``, ``get_started``, or ``tutorial``) becomes + the full SKILL.md body and siblings go to ``references/``. Groups + with no procedure page put every sibling in ``references/`` only. - individual: each ``how_to``, ``get_started``, or ``tutorial`` page - becomes its own skill; all other pages are collected into one - reference skill. + becomes its own skill; ``concept`` pages collect into + ``nemoclaw-user-concept`` and ``reference`` pages (plus other + non-procedure types) collect into ``nemoclaw-user-reference``. 4. Generates a skill directory per group containing: - SKILL.md with frontmatter (name, description), the lead page body, a References section linking sibling pages, and Related Skills links. @@ -149,6 +151,19 @@ def space_anchor_headings(text: str) -> str: return re.sub(r'(?m)^()\n(#{1,6}\s)', r"\1\n\n\2", text) +def collapse_consecutive_blank_lines(text: str) -> str: + """Collapse runs of blank lines to a single blank line (markdownlint MD012).""" + return re.sub(r"\n{3,}", "\n\n", text) + + +def append_markdown_section(lines: list[str], heading: str) -> None: + """Append a section heading, avoiding duplicate blank lines before it.""" + if lines and lines[-1] != "": + lines.append("") + lines.append(heading) + lines.append("") + + # --------------------------------------------------------------------------- # Frontmatter / doc parsing # --------------------------------------------------------------------------- @@ -1124,6 +1139,7 @@ def _safe_truncation_point(lines: list[str], target: int) -> int: CATEGORY_NOUNS = { "about": "overview", + "concept": "concept", "reference": "reference", "get-started": "get-started", "root": "overview", @@ -1493,11 +1509,31 @@ def partition_skill_pages(pages: list[DocPage]) -> tuple[DocPage, list[DocPage]] """Split a skill group into the lead page and reference siblings. The lead page has the lowest ``skill.priority`` value (highest priority). + Used by the ``individual`` strategy. """ ordered = sorted(pages, key=lambda p: (p.skill_priority, str(p.path))) return ordered[0], ordered[1:] +def partition_grouped_skill_pages( + pages: list[DocPage], +) -> tuple[DocPage | None, list[DocPage]]: + """Split a grouped skill into an optional inline lead and reference siblings. + + When the group contains a procedure page (``how_to``, ``get_started``, or + ``tutorial``), the one with the lowest ``skill.priority`` becomes the + SKILL.md body and siblings go to ``references/``. Otherwise every page is + reference-only progressive disclosure. + """ + ordered = sorted(pages, key=lambda p: (p.skill_priority, str(p.path))) + candidates = [p for p in pages if p.content_type in PROCEDURE_CONTENT_TYPES] + if not candidates: + return None, ordered + lead = min(candidates, key=lambda p: (p.skill_priority, str(p.path))) + refs = [p for p in ordered if p is not lead] + return lead, refs + + def _append_page_sections_to_skill( page: DocPage, lines: list[str], @@ -1540,6 +1576,7 @@ def generate_skill( doc_to_skill: dict[str, str] | None = None, html_baseurl: str | None = None, doc_platform: str = "myst-md", + strategy: str = "grouped", dry_run: bool = False, ) -> dict: """Generate a complete skill directory from a group of doc pages.""" @@ -1569,7 +1606,15 @@ def _clean( image_acc.extend(copies) return result - primary_page, reference_pages = partition_skill_pages(pages) + if strategy == "grouped": + primary_page, reference_pages = partition_grouped_skill_pages(pages) + else: + primary_page, reference_pages = partition_skill_pages(pages) + + ordered_pages = sorted(pages, key=lambda p: (p.skill_priority, str(p.path))) + description_pages = ( + [primary_page, *reference_pages] if primary_page is not None else ordered_pages + ) def _page_rel(page: DocPage) -> str | None: if docs_dir is None: @@ -1588,11 +1633,12 @@ def _page_rel(page: DocPage) -> str | None: ref_name = page.path.stem + ".md" skill_md_local_links[rel] = f"references/{ref_name}" reference_local_links[rel] = ref_name - primary_rel = _page_rel(primary_page) - if primary_rel is not None: - reference_local_links[primary_rel] = "../SKILL.md" + if primary_page is not None: + primary_rel = _page_rel(primary_page) + if primary_rel is not None: + reference_local_links[primary_rel] = "../SKILL.md" - description = build_skill_description(name, pages) + description = build_skill_description(name, description_pages) lines: list[str] = [] lines.append("---") @@ -1604,77 +1650,83 @@ def _page_rel(page: DocPage) -> str | None: lines.append(markdown_spdx_header().rstrip("\n")) lines.append("") - skill_title = primary_page.title or _brand_case(name.replace("-", " ").title()) + skill_title = ( + primary_page.title + if primary_page is not None and primary_page.title + else _brand_case(name.replace("-", " ").title()) + ) lines.append(f"# {skill_title}") - lines.append("") - - gotchas = _extract_gotchas([primary_page], doc_platform=doc_platform) - if gotchas: - lines.append("## Gotchas") - lines.append("") - for gotcha in gotchas: - lines.append(gotcha) + if primary_page is not None: lines.append("") - prereq_items: list[str] = [] - seen_prereqs: set[str] = set() - for heading, content in primary_page.sections: - if heading.lower() not in ("prerequisites", "before you begin"): - continue - cleaned = _clean(content, primary_page, skill_md_images, skill_md_local_links) - for item_line in cleaned.split("\n"): - stripped = item_line.strip() - if stripped.startswith("- "): - if prereq_items and not prereq_items[-1].startswith("- "): - prereq_items.append("") - norm = stripped.lower().strip("- .") - if norm not in seen_prereqs: - seen_prereqs.add(norm) - prereq_items.append(stripped) - elif stripped and not prereq_items: - prereq_items.append(stripped) + if primary_page is not None: + gotchas = _extract_gotchas([primary_page], doc_platform=doc_platform) + if gotchas: + lines.append("## Gotchas") + lines.append("") + for gotcha in gotchas: + lines.append(gotcha) + lines.append("") - if prereq_items: - lines.append("## Prerequisites") - lines.append("") - for item in prereq_items: - lines.append(item) - lines.append("") + prereq_items: list[str] = [] + seen_prereqs: set[str] = set() + for heading, content in primary_page.sections: + if heading.lower() not in ("prerequisites", "before you begin"): + continue + cleaned = _clean(content, primary_page, skill_md_images, skill_md_local_links) + for item_line in cleaned.split("\n"): + stripped = item_line.strip() + if stripped.startswith("- "): + if prereq_items and not prereq_items[-1].startswith("- "): + prereq_items.append("") + norm = stripped.lower().strip("- .") + if norm not in seen_prereqs: + seen_prereqs.add(norm) + prereq_items.append(stripped) + elif stripped and not prereq_items: + prereq_items.append(stripped) - collected_related: list[str] = [] - _append_page_sections_to_skill( - primary_page, - lines, - clean_fn=_clean, - skill_md_images=skill_md_images, - skill_md_local_links=skill_md_local_links, - collected_related=collected_related, - ) + if prereq_items: + lines.append("## Prerequisites") + lines.append("") + for item in prereq_items: + lines.append(item) + lines.append("") - raw_md = "\n".join(lines) - raw_md, body_related = extract_related_skills(raw_md) - lines = raw_md.rstrip("\n").split("\n") + collected_related: list[str] = [] + _append_page_sections_to_skill( + primary_page, + lines, + clean_fn=_clean, + skill_md_images=skill_md_images, + skill_md_local_links=skill_md_local_links, + collected_related=collected_related, + ) - all_related_text = "\n".join( - f"## Related Topics\n\n{block}" for block in collected_related - ) - _, section_related = extract_related_skills(all_related_text) + raw_md = "\n".join(lines) + raw_md, body_related = extract_related_skills(raw_md) + lines = raw_md.rstrip("\n").split("\n") - seen_skills: set[str] = set() - merged_entries: list[str] = [] - for entry in section_related + body_related: - skill_match = re.search(r"`([a-z0-9-]+)`", entry) - key = skill_match.group(1) if skill_match else entry - if key == name: - continue - if key not in seen_skills: - seen_skills.add(key) - merged_entries.append(entry) + all_related_text = "\n".join( + f"## Related Topics\n\n{block}" for block in collected_related + ) + _, section_related = extract_related_skills(all_related_text) + + seen_skills: set[str] = set() + merged_entries: list[str] = [] + for entry in section_related + body_related: + skill_match = re.search(r"`([a-z0-9-]+)`", entry) + key = skill_match.group(1) if skill_match else entry + if key == name: + continue + if key not in seen_skills: + seen_skills.add(key) + merged_entries.append(entry) + else: + merged_entries = [] if reference_pages: - lines.append("") - lines.append("## References") - lines.append("") + append_markdown_section(lines, "## References") for ref_page in reference_pages: ref_name = ref_page.path.stem + ".md" file_link = f"[references/{ref_name}](references/{ref_name})" @@ -1690,14 +1742,14 @@ def _page_rel(page: DocPage) -> str | None: lines.append(bullet) if merged_entries: - lines.append("") - lines.append("## Related Skills") - lines.append("") + append_markdown_section(lines, "## Related Skills") for entry in merged_entries: lines.append(entry) lines.append("") - skill_md = normalize_heading_levels("\n".join(lines)) + skill_md = collapse_consecutive_blank_lines( + normalize_heading_levels("\n".join(lines)) + ) ref_files: dict[str, str] = {} for ref_page in reference_pages: @@ -1793,14 +1845,19 @@ def group_by_directory(pages: list[DocPage]) -> dict[str, list[DocPage]]: def group_individual(pages: list[DocPage]) -> dict[str, list[DocPage]]: - """Give each procedure page its own skill; collect the rest into reference.""" + """Give each procedure page its own skill; bucket concept and reference pages.""" groups: dict[str, list[DocPage]] = {} + concept_pages: list[DocPage] = [] reference_pages: list[DocPage] = [] for page in pages: if page.content_type in PROCEDURE_CONTENT_TYPES: groups[page.path.stem] = [page] + elif page.content_type == "concept": + concept_pages.append(page) else: reference_pages.append(page) + if concept_pages: + groups["concept"] = concept_pages if reference_pages: groups["reference"] = reference_pages return groups @@ -1882,8 +1939,9 @@ def main(): epilog=textwrap.dedent("""\ Strategies: grouped Group docs by parent directory (default) - individual One skill per how_to/get_started/tutorial page; one - reference skill for all other pages + individual One skill per how_to/get_started/tutorial page; + concept pages -> nemoclaw-user-concept; + reference pages -> nemoclaw-user-reference Examples: %(prog)s docs/ .agents/skills/ --prefix nemoclaw-user --doc-platform fern-mdx @@ -2045,6 +2103,7 @@ def main(): doc_to_skill=doc_to_skill, html_baseurl=html_baseurl, doc_platform=args.doc_platform, + strategy=args.strategy, dry_run=args.dry_run, ) summaries.append(summary) diff --git a/test/docs-to-skills.test.py b/test/docs-to-skills.test.py index b5e2a7daba..39887f9b45 100644 --- a/test/docs-to-skills.test.py +++ b/test/docs-to-skills.test.py @@ -46,15 +46,46 @@ def test_partition_skill_pages_uses_lowest_priority_value(self): self.assertEqual(lead.path.stem, "high") self.assertEqual([page.path.stem for page in refs], ["low"]) - def test_group_individual_splits_procedure_and_reference_pages(self): + def test_partition_grouped_skill_pages_uses_highest_priority_procedure(self): + concept = make_page("overview", content_type="concept", priority=10) + primary = make_page("lifecycle", content_type="how_to", priority=10) + secondary = make_page("backup", content_type="how_to", priority=20) + lead, refs = self.mod.partition_grouped_skill_pages( + [secondary, concept, primary] + ) + self.assertEqual(lead.path.stem, "lifecycle") + self.assertEqual( + {page.path.stem for page in refs}, + {"backup", "overview"}, + ) + + def test_partition_grouped_skill_pages_reference_only_without_procedure(self): + concept = make_page("overview", content_type="concept", priority=10) + reference = make_page("commands", content_type="reference", priority=20) + lead, refs = self.mod.partition_grouped_skill_pages([reference, concept]) + self.assertIsNone(lead) + self.assertEqual([page.path.stem for page in refs], ["overview", "commands"]) + + def test_partition_grouped_skill_pages_treats_get_started_as_procedure(self): + quickstart = make_page("quickstart", content_type="get_started", priority=10) + prereq = make_page("prerequisites", content_type="reference", priority=5) + lead, refs = self.mod.partition_grouped_skill_pages([prereq, quickstart]) + self.assertEqual(lead.path.stem, "quickstart") + self.assertEqual([page.path.stem for page in refs], ["prerequisites"]) + + def test_group_individual_splits_procedure_concept_and_reference_pages(self): procedure = make_page("quickstart", content_type="get_started") concept = make_page("overview", content_type="concept") reference = make_page("commands", content_type="reference") - groups = self.mod.group_individual([procedure, concept, reference]) + troubleshooting = make_page("tool-calling", content_type="troubleshooting") + groups = self.mod.group_individual( + [procedure, concept, reference, troubleshooting] + ) self.assertEqual(groups["quickstart"], [procedure]) + self.assertEqual(groups["concept"], [concept]) self.assertEqual( {page.path.stem for page in groups["reference"]}, - {"overview", "commands"}, + {"commands", "tool-calling"}, ) def test_group_by_directory_keeps_siblings_together(self): @@ -65,6 +96,18 @@ def test_group_by_directory_keeps_siblings_together(self): groups = self.mod.group_by_directory([page_a, page_b]) self.assertEqual(len(groups["get-started"]), 2) + def test_collapse_consecutive_blank_lines(self): + text = "# Title\n\n\n## References\n" + self.assertEqual( + self.mod.collapse_consecutive_blank_lines(text), + "# Title\n\n## References\n", + ) + + def test_append_markdown_section_avoids_duplicate_blank_lines(self): + lines = ["# Title"] + self.mod.append_markdown_section(lines, "## References") + self.assertEqual(lines, ["# Title", "", "## References", ""]) + if __name__ == "__main__": unittest.main() From 26f78cd151d96ccb3ccfcb53e4f0bce3fb7b2770 Mon Sep 17 00:00:00 2001 From: Miyoung Choi Date: Fri, 29 May 2026 15:25:01 -0700 Subject: [PATCH 3/3] chore: rm unnecessary test --- test/docs-to-skills.test.py | 113 ------------------------------------ 1 file changed, 113 deletions(-) delete mode 100644 test/docs-to-skills.test.py diff --git a/test/docs-to-skills.test.py b/test/docs-to-skills.test.py deleted file mode 100644 index 39887f9b45..0000000000 --- a/test/docs-to-skills.test.py +++ /dev/null @@ -1,113 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -"""Unit tests for scripts/docs-to-skills.py helpers.""" - -from __future__ import annotations - -import importlib.util -import sys -import unittest -from pathlib import Path - -REPO_ROOT = Path(__file__).resolve().parents[1] -SCRIPT_PATH = REPO_ROOT / "scripts" / "docs-to-skills.py" - - -def load_docs_to_skills(): - module_name = "docs_to_skills_test_module" - spec = importlib.util.spec_from_file_location(module_name, SCRIPT_PATH) - if spec is None or spec.loader is None: - raise RuntimeError(f"Unable to load {SCRIPT_PATH}") - module = importlib.util.module_from_spec(spec) - sys.modules[module_name] = module - spec.loader.exec_module(module) - return module - - -def make_page(stem: str, *, content_type: str = "how_to", priority: int = 100) -> object: - mod = load_docs_to_skills() - page = mod.DocPage(path=Path(f"docs/example/{stem}.mdx"), raw="") - page.content_type = content_type - page.skill_priority = priority - page.category = "example" - return page - - -class DocsToSkillsHelpersTest(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.mod = load_docs_to_skills() - - def test_partition_skill_pages_uses_lowest_priority_value(self): - high = make_page("high", priority=10) - low = make_page("low", priority=50) - lead, refs = self.mod.partition_skill_pages([low, high]) - self.assertEqual(lead.path.stem, "high") - self.assertEqual([page.path.stem for page in refs], ["low"]) - - def test_partition_grouped_skill_pages_uses_highest_priority_procedure(self): - concept = make_page("overview", content_type="concept", priority=10) - primary = make_page("lifecycle", content_type="how_to", priority=10) - secondary = make_page("backup", content_type="how_to", priority=20) - lead, refs = self.mod.partition_grouped_skill_pages( - [secondary, concept, primary] - ) - self.assertEqual(lead.path.stem, "lifecycle") - self.assertEqual( - {page.path.stem for page in refs}, - {"backup", "overview"}, - ) - - def test_partition_grouped_skill_pages_reference_only_without_procedure(self): - concept = make_page("overview", content_type="concept", priority=10) - reference = make_page("commands", content_type="reference", priority=20) - lead, refs = self.mod.partition_grouped_skill_pages([reference, concept]) - self.assertIsNone(lead) - self.assertEqual([page.path.stem for page in refs], ["overview", "commands"]) - - def test_partition_grouped_skill_pages_treats_get_started_as_procedure(self): - quickstart = make_page("quickstart", content_type="get_started", priority=10) - prereq = make_page("prerequisites", content_type="reference", priority=5) - lead, refs = self.mod.partition_grouped_skill_pages([prereq, quickstart]) - self.assertEqual(lead.path.stem, "quickstart") - self.assertEqual([page.path.stem for page in refs], ["prerequisites"]) - - def test_group_individual_splits_procedure_concept_and_reference_pages(self): - procedure = make_page("quickstart", content_type="get_started") - concept = make_page("overview", content_type="concept") - reference = make_page("commands", content_type="reference") - troubleshooting = make_page("tool-calling", content_type="troubleshooting") - groups = self.mod.group_individual( - [procedure, concept, reference, troubleshooting] - ) - self.assertEqual(groups["quickstart"], [procedure]) - self.assertEqual(groups["concept"], [concept]) - self.assertEqual( - {page.path.stem for page in groups["reference"]}, - {"commands", "tool-calling"}, - ) - - def test_group_by_directory_keeps_siblings_together(self): - page_a = make_page("a") - page_b = make_page("b") - page_a.category = "get-started" - page_b.category = "get-started" - groups = self.mod.group_by_directory([page_a, page_b]) - self.assertEqual(len(groups["get-started"]), 2) - - def test_collapse_consecutive_blank_lines(self): - text = "# Title\n\n\n## References\n" - self.assertEqual( - self.mod.collapse_consecutive_blank_lines(text), - "# Title\n\n## References\n", - ) - - def test_append_markdown_section_avoids_duplicate_blank_lines(self): - lines = ["# Title"] - self.mod.append_markdown_section(lines, "## References") - self.assertEqual(lines, ["# Title", "", "## References", ""]) - - -if __name__ == "__main__": - unittest.main()