Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 9 additions & 8 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
FROM node:22-slim

ENV DEBIAN_FRONTEND=noninteractive
ENV HOME=/sandbox

RUN apt-get update && apt-get install -y --no-install-recommends \
python3 python3-pip python3-venv \
Expand Down Expand Up @@ -35,12 +36,12 @@ RUN npm install --omit=dev
RUN mkdir -p /sandbox/.nemoclaw/blueprints/0.1.0 \
&& cp -r /opt/nemoclaw-blueprint/* /sandbox/.nemoclaw/blueprints/0.1.0/

# Copy startup script
# Copy startup scripts and force read+execute bits for the sandbox user.
COPY scripts/nemoclaw-start.sh /usr/local/bin/nemoclaw-start
RUN chmod +x /usr/local/bin/nemoclaw-start
COPY scripts/nemoclaw-shell.sh /usr/local/bin/nemoclaw-shell
RUN chmod 755 /usr/local/bin/nemoclaw-start /usr/local/bin/nemoclaw-shell

WORKDIR /sandbox
USER sandbox

# Pre-create OpenClaw directories
RUN mkdir -p /sandbox/.openclaw/agents/main/agent \
Expand All @@ -60,13 +61,13 @@ config = { \
'models': [{'id': 'nemotron-3-super-120b-a12b', 'name': 'NVIDIA Nemotron 3 Super 120B', 'reasoning': False, 'input': ['text'], 'cost': {'input': 0, 'output': 0, 'cacheRead': 0, 'cacheWrite': 0}, 'contextWindow': 131072, 'maxTokens': 4096}] \
}}} \
}; \
path = os.path.expanduser('~/.openclaw/openclaw.json'); \
path = '/sandbox/.openclaw/openclaw.json'; \
json.dump(config, open(path, 'w'), indent=2); \
os.chmod(path, 0o600)"
RUN chown -R sandbox:sandbox /sandbox/.openclaw /sandbox/.nemoclaw

# Install NemoClaw plugin into OpenClaw
RUN openclaw doctor --fix > /dev/null 2>&1 || true \
&& openclaw plugins install /opt/nemoclaw > /dev/null 2>&1 || true
RUN runuser -u sandbox -- env HOME=/sandbox openclaw doctor --fix > /dev/null 2>&1 || true \
&& runuser -u sandbox -- env HOME=/sandbox openclaw plugins install /opt/nemoclaw > /dev/null 2>&1 || true

ENTRYPOINT ["/bin/bash"]
CMD []
USER sandbox
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ First run prompts for your NVIDIA API Key (get one from [build.nvidia.com](https
- Docker
- [OpenShell CLI](https://github.com/NVIDIA/OpenShell/releases) (binary download)

The repo-owned sandbox image stays simple and non-root at rest.
Headless bootstrap runs `openclaw setup`, then `openclaw gateway install --json`
inside the sandbox. If the sandbox cannot host user-systemd, NemoClaw falls
back to a direct background `openclaw gateway run` process so the gateway auth
token and runtime still exist before first connect.

### Ubuntu 24.04 (fresh install)

> **Minimum 8GB RAM** — the sandbox image is ~1.5 GiB and will OOM-kill on 4GB VMs.
Expand Down Expand Up @@ -179,7 +185,7 @@ Without tmux, run these in two terminals:
openshell term

# Terminal 2 — agent
openshell sandbox connect nemoclaw
openshell sandbox connect nemoclaw -- nemoclaw-shell
export NVIDIA_API_KEY=nvapi-...
nemoclaw-start
openclaw agent --agent main --local --session-id live
Expand Down Expand Up @@ -222,7 +228,8 @@ nemoclaw-blueprint/ Versioned blueprint artifact (separate relea
|--------|---------|
| `scripts/setup.sh` | Host-side setup — gateway, providers, inference route, sandbox |
| `scripts/brev-setup.sh` | Brev bootstrap — installs prerequisites, then runs `setup.sh` |
| `scripts/nemoclaw-start.sh` | Sandbox entrypoint — configures OpenClaw, installs plugin |
| `scripts/nemoclaw-shell.sh` | Thin shell wrapper — runs commands inside the sandbox as the current user |
| `scripts/nemoclaw-start.sh` | Sandbox startup helper — configures OpenClaw, installs plugin |
| `scripts/walkthrough.sh` | Split-screen walkthrough — agent + TUI approval flow |
| `scripts/fix-coredns.sh` | CoreDNS patch for Colima environments |

Expand Down
11 changes: 5 additions & 6 deletions bin/lib/onboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -360,12 +360,11 @@ async function setupInference(sandboxName, model, provider) {
async function setupOpenclaw(sandboxName) {
step(6, 7, "Setting up OpenClaw inside sandbox");

// sandbox create with a command runs it inside the sandbox then exits.
// Since the sandbox already exists, we create a throwaway connect + command
// by using sandbox create --no-keep with the same image to exec into it.
// Simpler: just use sandbox connect which opens a shell — but it doesn't
// support passing commands. So we run the setup on next connect instead.
console.log(" ✓ OpenClaw gateway launched inside sandbox");
// `openshell sandbox create ... -- <cmd>` only helps during allocation.
// Once the sandbox already exists, use the post-create bootstrap helper to
// initialize OpenClaw and seed the Gateway before first connect.
run(`bash "${path.join(SCRIPTS, "bootstrap-sandbox-openclaw.sh")}" "${sandboxName}"`);
console.log(" ✓ OpenClaw configured");
}

// ── Step 7: Policy presets ───────────────────────────────────────
Expand Down
4 changes: 2 additions & 2 deletions bin/nemoclaw.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ async function deploy(instanceName) {
console.log("");
console.log(" Connecting to sandbox...");
console.log("");
run(`ssh -t -o StrictHostKeyChecking=no -o LogLevel=ERROR ${name} 'cd /home/ubuntu/nemoclaw && set -a && . .env && set +a && openshell sandbox connect nemoclaw'`);
run(`ssh -t -o StrictHostKeyChecking=no -o LogLevel=ERROR ${name} 'cd /home/ubuntu/nemoclaw && set -a && . .env && set +a && openshell sandbox connect nemoclaw -- nemoclaw-shell'`);
}

async function start() {
Expand Down Expand Up @@ -229,7 +229,7 @@ function egg(instanceName) {
function sandboxConnect(sandboxName) {
// Ensure port forward is alive before connecting
run(`openshell forward start --background 18789 ${sandboxName} 2>/dev/null || true`, { ignoreError: true });
run(`openshell sandbox connect ${sandboxName}`);
run(`openshell sandbox connect ${sandboxName} -- nemoclaw-shell`);
}

function sandboxStatus(sandboxName) {
Expand Down
20 changes: 16 additions & 4 deletions docs/reference/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,14 @@ Both interfaces are installed when you run `npm install -g nemoclaw`.

### `openclaw nemoclaw launch`

Bootstrap OpenClaw inside an OpenShell sandbox.
If NemoClaw detects an existing host installation, `launch` stops and points you to `openclaw nemoclaw migrate` unless you pass `--force`.
Bootstrap a fresh OpenClaw installation inside an OpenShell sandbox.
After provisioning the sandbox, NemoClaw runs `openclaw setup` and
`openclaw gateway install --json` inside it so the config, workspace, sessions,
and gateway auth token all exist before first use.
If NemoClaw detects an existing host installation, `launch` stops and points you
to `openclaw nemoclaw migrate` unless you pass `--force`.
If the sandbox does not support user-systemd, NemoClaw falls back to a direct
background gateway process so headless bootstrap still completes without root.

```console
$ openclaw nemoclaw launch [--force] [--profile <profile>]
Expand All @@ -46,7 +52,11 @@ $ openclaw nemoclaw launch [--force] [--profile <profile>]
### `openclaw nemoclaw migrate`

Migrate an existing host OpenClaw installation into an OpenShell sandbox.
The command snapshots the resolved OpenClaw state, captures external agent roots referenced by config, preserves symlinks in tar archives, and verifies the migrated paths inside the sandbox.
The command snapshots the resolved OpenClaw state, captures external agent roots
referenced by config, preserves symlinks in tar archives, rewrites migrated
config paths, verifies the migrated paths inside the sandbox, and then re-runs
the same headless bootstrap so migrated installs also have a ready gateway
token and Gateway runtime after cutover.

```console
$ openclaw nemoclaw migrate [--dry-run] [--profile <profile>] [--skip-backup]
Expand All @@ -63,7 +73,9 @@ $ openclaw nemoclaw migrate [--dry-run] [--profile <profile>] [--skip-backup]

### `nemoclaw <name> connect`

Open an interactive shell inside the OpenClaw sandbox.
Open an interactive shell inside the OpenClaw sandbox as the `sandbox` user.
This routes through `nemoclaw-shell`, which is a thin shell wrapper used by the
host-side bootstrap helpers.

```console
$ nemoclaw my-assistant connect
Expand Down
4 changes: 2 additions & 2 deletions nemoclaw/dist/commands/connect.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion nemoclaw/dist/commands/connect.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion nemoclaw/dist/commands/launch.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions nemoclaw/dist/commands/launch.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion nemoclaw/dist/commands/launch.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading