Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
ce7677d
fix: resolve 3 failing tests in installer and uninstall scripts
cluster2600 Mar 23, 2026
b164b7e
fix: redact secrets from CLI error output and stop exposing API keys …
cluster2600 Mar 23, 2026
147dc92
fix: add local-inference policy preset for Ollama/vLLM host gateway (…
cluster2600 Mar 23, 2026
d10930f
fix: add missing binaries section to all policy presets (#676)
cluster2600 Mar 23, 2026
87364ce
fix: add safe config I/O with EACCES error handling (#692, #606, #719)
cluster2600 Mar 23, 2026
d701540
fix: verify integrity of downloaded scripts before execution (#583)
cluster2600 Mar 23, 2026
238d372
fix: make gateway and dashboard ports configurable to avoid conflicts…
cluster2600 Mar 23, 2026
c090d84
fix: unify credential passing pattern across integrations (#616)
cluster2600 Mar 23, 2026
ec3d74e
fix: patch CoreDNS on all Docker-based runtimes, not just Colima (#626)
cluster2600 Mar 23, 2026
020ac90
fix: npm link --ignore-scripts for git checkout install, sudo lsof hi…
cluster2600 Mar 23, 2026
a95ad40
fix: detect and remove placeholder npm package that clobbers real ins…
cluster2600 Mar 23, 2026
470e0b9
fix: sha-pin all GitHub Actions to prevent supply chain attacks (#578)
cluster2600 Mar 23, 2026
7539cb6
fix: sync user-selected model into sandbox openclaw.json (#628)
cluster2600 Mar 23, 2026
a9fb616
fix: early Ollama validation — no-models guard + Linux bind address h…
cluster2600 Mar 23, 2026
0e3ff0a
feat: add --verbose/--debug flag for CLI observability (#666)
cluster2600 Mar 23, 2026
692555e
fix: configurable OpenClaw version, expanded trustedProxies, extra al…
cluster2600 Mar 23, 2026
a79a813
fix: resolve DNS on systemd-resolved hosts by reading upstream from r…
cluster2600 Mar 23, 2026
0ca3022
fix: configurable NIM port via NEMOCLAW_NIM_PORT, fix hardcoded port …
cluster2600 Mar 23, 2026
1554f8a
fix: repair restrictive sandbox home directory permissions (#622)
cluster2600 Mar 23, 2026
5221457
test: add comprehensive Python test suite for blueprint runner, snaps…
cluster2600 Mar 23, 2026
999860c
test: expand branch coverage for policies.js and inference-config.js
cluster2600 Mar 23, 2026
5a53281
test: export onboard.js internal helpers and add 74 branch coverage t…
cluster2600 Mar 23, 2026
d7be658
test: expand branch coverage across CLI, config-io, platform, resolve…
cluster2600 Mar 23, 2026
1792890
fix: expose full model catalog so openshell inference set can switch …
cluster2600 Mar 23, 2026
0106095
fix: broken docs links and confusing setup-spark messaging (#747, #738)
cluster2600 Mar 24, 2026
eb1eb2e
feat: add self-update command for automatic CLI updates (#642)
cluster2600 Mar 24, 2026
33a599c
fix: add build toolchain for native Node.js addons on aarch64 (#724)
cluster2600 Mar 24, 2026
4f180c6
fix: prevent build-arg injection in Dockerfile Python config generator
cluster2600 Mar 24, 2026
3b050e9
test: add comprehensive detectGpu() branch coverage via dependency in…
cluster2600 Mar 24, 2026
8ee47c7
feat: add nemoclaw reconnect command for gateway/sandbox connectivity…
cluster2600 Mar 24, 2026
806e73b
fix: enable model-aware reasoning and maxTokens in openclaw.json (#736)
cluster2600 Mar 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Commands

The `nemoclaw` CLI is the primary interface for managing NemoClaw sandboxes. It is installed when you run `npm install -g nemoclaw`.
The `nemoclaw` CLI is the primary interface for managing NemoClaw sandboxes. It is installed when you run `npm install -g git+https://github.com/NVIDIA/NemoClaw.git`.

### `/nemoclaw` Slash Command

Expand Down
6 changes: 3 additions & 3 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
## Checklist

### General
- [ ] I have read and followed the [contributing guide](CONTRIBUTING.md).
- [ ] I have read and followed the [style guide](docs/CONTRIBUTING.md). (for doc-only changes)
- [ ] I have read and followed the [contributing guide](../CONTRIBUTING.md).
- [ ] I have read and followed the [style guide](../docs/CONTRIBUTING.md). (for doc-only changes)

### Code Changes
<!-- Skip if this is a doc-only PR. -->
Expand All @@ -35,6 +35,6 @@

### Doc Changes
<!-- Skip if this PR has no doc changes. -->
- [ ] Follows the [style guide](docs/CONTRIBUTING.md). Try running the `update-docs` agent skill to draft changes while complying with the style guide. For example, prompt your agent with "`/update-docs` catch up the docs for the new changes I made in this PR."
- [ ] Follows the [style guide](../docs/CONTRIBUTING.md). Try running the `update-docs` agent skill to draft changes while complying with the style guide. For example, prompt your agent with "`/update-docs` catch up the docs for the new changes I made in this PR."
- [ ] New pages include SPDX license header and frontmatter, if creating a new page.
- [ ] Cross-references and links verified.
4 changes: 2 additions & 2 deletions .github/workflows/commit-lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ jobs:
timeout-minutes: 5
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0

- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: "22"
cache: npm
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/docs-preview-pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,17 @@ jobs:
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Set up Python
if: github.event.action != 'closed'
uses: actions/setup-python@v5
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.11"

- name: Install uv
if: github.event.action != 'closed'
uses: astral-sh/setup-uv@v4
uses: astral-sh/setup-uv@38f3f104447c67c051c4a08e39b64a148898af3a # v4

- name: Install doc dependencies
if: github.event.action != 'closed'
Expand All @@ -56,7 +56,7 @@ jobs:
touch docs/_build/html/.nojekyll

- name: Deploy preview
uses: rossjrw/pr-preview-action@v1
uses: rossjrw/pr-preview-action@ffa7509e91a3ec8dfc2e5536c4d5c1acdf7a6de9 # v1
with:
source-dir: ./docs/_build/html/
preview-branch: gh-pages
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/docs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ jobs:
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.11"

- name: Install uv
uses: astral-sh/setup-uv@v4
uses: astral-sh/setup-uv@38f3f104447c67c051c4a08e39b64a148898af3a # v4

- name: Install doc dependencies
run: uv sync --group docs
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/nightly-e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
timeout-minutes: 45
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Run full E2E test
env:
Expand All @@ -42,7 +42,7 @@ jobs:

- name: Upload install log on failure
if: failure()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: install-log
path: /tmp/nemoclaw-e2e-install.log
Expand Down
14 changes: 7 additions & 7 deletions .github/workflows/pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,21 @@ jobs:
timeout-minutes: 5
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: "22"
cache: npm

- name: Setup Python
uses: actions/setup-python@v5
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.11"

- name: Install uv
uses: astral-sh/setup-uv@v4
uses: astral-sh/setup-uv@38f3f104447c67c051c4a08e39b64a148898af3a # v4

- name: Install ruff
run: uv tool install ruff
Expand All @@ -51,10 +51,10 @@ jobs:
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: "22"
cache: npm
Expand Down Expand Up @@ -83,7 +83,7 @@ jobs:
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Build sandbox test image
run: docker build -f test/Dockerfile.sandbox -t nemoclaw-sandbox-test .
Expand Down
12 changes: 12 additions & 0 deletions .shellcheckrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# ShellCheck configuration for NemoClaw shell scripts.

# SC2059: Don't use variables in printf format string — intentional for ANSI
# color-code formatting (C_GREEN, C_BOLD, etc.) throughout the installer.
disable=SC2059

# SC1091: Not following sourced file — we source nvm.sh dynamically and
# shellcheck cannot resolve the path at analysis time.
disable=SC1091
64 changes: 55 additions & 9 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,25 @@ FROM node:22-slim

ENV DEBIAN_FRONTEND=noninteractive

# build-essential + python3-dev are required for native Node.js addons
# (@whiskeysockets/baileys/libsignal-node, @matrix-org/matrix-sdk-crypto-nodejs,
# protobufjs, koffi) that lack prebuilt binaries on aarch64.
# Ref: https://github.com/NVIDIA/NemoClaw/issues/724
RUN apt-get update && apt-get install -y --no-install-recommends \
python3 python3-pip python3-venv \
python3 python3-pip python3-venv python3-dev \
curl git ca-certificates \
iproute2 \
build-essential \
&& rm -rf /var/lib/apt/lists/*

# Create sandbox user (matches OpenShell convention)
# Explicit chmod 755 prevents OpenShell from inheriting a restrictive home
# directory mode (e.g. 0711) that would block agents from listing files.
# Ref: https://github.com/NVIDIA/NemoClaw/issues/622
RUN groupadd -r sandbox && useradd -r -g sandbox -d /sandbox -s /bin/bash sandbox \
&& mkdir -p /sandbox/.nemoclaw \
&& chown -R sandbox:sandbox /sandbox
&& chown -R sandbox:sandbox /sandbox \
&& chmod 755 /sandbox

# Split .openclaw into immutable config dir + writable state dir.
# The policy makes /sandbox/.openclaw read-only via Landlock, so the agent
Expand Down Expand Up @@ -51,8 +60,12 @@ RUN mkdir -p /sandbox/.openclaw-data/agents/main/agent \
&& ln -s /sandbox/.openclaw-data/update-check.json /sandbox/.openclaw/update-check.json \
&& chown -R sandbox:sandbox /sandbox/.openclaw /sandbox/.openclaw-data

# Install OpenClaw CLI
RUN npm install -g openclaw@2026.3.11
# Install OpenClaw CLI — version is a build arg so users can upgrade
# without editing the Dockerfile. Override at build time:
# docker build --build-arg OPENCLAW_VERSION=2026.3.22 ...
# or via nemoclaw onboard with NEMOCLAW_OPENCLAW_VERSION env var.
ARG OPENCLAW_VERSION=2026.3.11
RUN npm install -g openclaw@${OPENCLAW_VERSION}

# Install PyYAML for blueprint runner
RUN pip3 install --break-system-packages pyyaml
Expand All @@ -67,6 +80,11 @@ COPY nemoclaw-blueprint/ /opt/nemoclaw-blueprint/
WORKDIR /opt/nemoclaw
RUN npm install --omit=dev

# Remove build toolchain — no longer needed after native addons are compiled.
# Saves ~200 MB in the final image. Ref: #724
RUN apt-get purge -y --auto-remove build-essential python3-dev \
&& rm -rf /var/lib/apt/lists/*

# Set up blueprint for local resolution
RUN mkdir -p /sandbox/.nemoclaw/blueprints/0.1.0 \
&& cp -r /opt/nemoclaw-blueprint/* /sandbox/.nemoclaw/blueprints/0.1.0/
Expand All @@ -79,10 +97,23 @@ RUN chmod +x /usr/local/bin/nemoclaw-start
# nemoclaw onboard passes these at image build time.
ARG NEMOCLAW_MODEL=nvidia/nemotron-3-super-120b-a12b
ARG CHAT_UI_URL=http://127.0.0.1:18789
# Extra allowed CORS origins (comma-separated). Useful for headless
# deployments accessed via SSH tunnel or LAN with a different origin.
# Example: --build-arg NEMOCLAW_EXTRA_ORIGINS=http://192.168.1.50:3333,http://10.0.0.5:18789
ARG NEMOCLAW_EXTRA_ORIGINS=
# Unique per build to ensure each image gets a fresh auth token.
# Pass --build-arg NEMOCLAW_BUILD_ID=$(date +%s) to bust the cache.
ARG NEMOCLAW_BUILD_ID=default

# Promote ARGs to ENV so the Python config generator reads them via
# os.environ instead of shell interpolation inside string literals.
# This prevents build-arg injection — a crafted value containing
# quotes or semicolons cannot break out of the Python script.
# Ref: security hardening for #739 build-arg handling
ENV NEMOCLAW_MODEL=${NEMOCLAW_MODEL} \
CHAT_UI_URL=${CHAT_UI_URL} \
NEMOCLAW_EXTRA_ORIGINS=${NEMOCLAW_EXTRA_ORIGINS}

WORKDIR /sandbox
USER sandbox

Expand All @@ -91,29 +122,44 @@ USER sandbox
# No runtime writes to openclaw.json are needed or possible.
# Build args (NEMOCLAW_MODEL, CHAT_UI_URL) customize per deployment.
# Auth token is generated per build so each image has a unique token.
#
# SECURITY: Values are read from os.environ, NOT from shell interpolation.
# Prior versions used '${VAR}' inside Python string literals, which was
# vulnerable to injection if a build arg contained single quotes.
RUN python3 -c "\
import json, os, secrets; \
from urllib.parse import urlparse; \
model = '${NEMOCLAW_MODEL}'; \
chat_ui_url = '${CHAT_UI_URL}'; \
model = os.environ.get('NEMOCLAW_MODEL', 'nvidia/nemotron-3-super-120b-a12b'); \
chat_ui_url = os.environ.get('CHAT_UI_URL', 'http://127.0.0.1:18789'); \
extra_origins_raw = os.environ.get('NEMOCLAW_EXTRA_ORIGINS', ''); \
parsed = urlparse(chat_ui_url); \
chat_origin = f'{parsed.scheme}://{parsed.netloc}' if parsed.scheme and parsed.netloc else 'http://127.0.0.1:18789'; \
origins = ['http://127.0.0.1:18789']; \
origins = list(dict.fromkeys(origins + [chat_origin])); \
if extra_origins_raw: \
for o in extra_origins_raw.split(','): \
o = o.strip(); \
if o and o not in origins: origins.append(o); \
reasoning_models = {'nvidia/nemotron-3-super-120b-a12b', 'nvidia/llama-3.1-nemotron-ultra-253b-v1', 'nvidia/llama-3.3-nemotron-super-49b-v1.5'}; \
model_max_tokens = {'nvidia/nemotron-3-super-120b-a12b': 8192, 'nvidia/llama-3.1-nemotron-ultra-253b-v1': 8192}; \
is_reasoning = model in reasoning_models; \
max_tokens = model_max_tokens.get(model, 4096); \
def model_entry(mid, mname): \
return {'id': mid, 'name': mname, 'reasoning': is_reasoning, 'input': ['text'], 'cost': {'input': 0, 'output': 0, 'cacheRead': 0, 'cacheWrite': 0}, 'contextWindow': 131072, 'maxTokens': max_tokens}; \
config = { \
'agents': {'defaults': {'model': {'primary': f'inference/{model}'}}}, \
'models': {'mode': 'merge', 'providers': { \
'nvidia': { \
'baseUrl': 'https://inference.local/v1', \
'apiKey': 'openshell-managed', \
'api': 'openai-completions', \
'models': [{'id': model.split('/')[-1], 'name': model, 'reasoning': False, 'input': ['text'], 'cost': {'input': 0, 'output': 0, 'cacheRead': 0, 'cacheWrite': 0}, 'contextWindow': 131072, 'maxTokens': 4096}] \
'models': [model_entry(model.split('/')[-1], model)] \
}, \
'inference': { \
'baseUrl': 'https://inference.local/v1', \
'apiKey': 'unused', \
'api': 'openai-completions', \
'models': [{'id': model, 'name': model, 'reasoning': False, 'input': ['text'], 'cost': {'input': 0, 'output': 0, 'cacheRead': 0, 'cacheWrite': 0}, 'contextWindow': 131072, 'maxTokens': 4096}] \
'models': [model_entry(model, model)] \
} \
}}, \
'channels': {'defaults': {'configWrites': False}}, \
Expand All @@ -124,7 +170,7 @@ config = { \
'dangerouslyDisableDeviceAuth': True, \
'allowedOrigins': origins, \
}, \
'trustedProxies': ['127.0.0.1', '::1'], \
'trustedProxies': ['127.0.0.1', '::1', '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'], \
'auth': {'token': secrets.token_hex(32)} \
} \
}; \
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ Run these on the host to set up, connect to, and manage sandboxes.
| `openshell term` | Launch the OpenShell TUI for monitoring and approvals. |
| `nemoclaw start` / `stop` / `status` | Manage auxiliary services (Telegram bridge, tunnel). |

See the full [CLI reference](https://docs.nvidia.com/nemoclaw/latest/reference/commands.md) for all commands, flags, and options.
See the full [CLI reference](https://docs.nvidia.com/nemoclaw/latest/reference/commands.html) for all commands, flags, and options.

---

Expand Down
Loading
Loading