Skip to content

feat(sandbox): runtime model override via env vars#1633

Merged
brandonpelfrey merged 21 commits intoNVIDIA:mainfrom
prekshivyas:fix/runtime-model-override
Apr 9, 2026
Merged

feat(sandbox): runtime model override via env vars#1633
brandonpelfrey merged 21 commits intoNVIDIA:mainfrom
prekshivyas:fix/runtime-model-override

Conversation

@prekshivyas
Copy link
Copy Markdown
Contributor

@prekshivyas prekshivyas commented Apr 8, 2026

Summary

  • Allow model and provider changes without rebuilding the sandbox image
  • The entrypoint patches openclaw.json at startup when NEMOCLAW_MODEL_OVERRIDE is set, then recomputes the config hash so integrity checks still pass
  • Same pattern as NEMOCLAW_LOCAL_INFERENCE_TIMEOUT (PR fix(inference): increase timeout for local providers to 180s #1620)

New env vars

Env var Purpose When needed
NEMOCLAW_MODEL_OVERRIDE Override agents.defaults.model.primary and provider model name Any model switch
NEMOCLAW_INFERENCE_API_OVERRIDE Override inference API type (openai-completions or anthropic-messages) Cross-provider switches only

Usage example (NVIDIA → Anthropic)

# On host: configure gateway route
openshell inference set --provider anthropic-prod --model claude-sonnet-4-6 --no-verify

# Set env vars for the sandbox (via openshell or Docker)
export NEMOCLAW_MODEL_OVERRIDE="anthropic/claude-sonnet-4-6"
export NEMOCLAW_INFERENCE_API_OVERRIDE="anthropic-messages"

# Restart sandbox — no image rebuild needed

Security

  • Env vars come from the host (Docker/OpenShell), not from inside the sandbox
  • Config integrity is verified first (detects build-time tampering), then override is applied
  • Config hash is recomputed after patching
  • Landlock locks the file after this function runs
  • Agent cannot set these env vars

Related Issue

Closes #759

Test plan

  • npm test passes (39 tests in nemoclaw-start.test.js)
  • Set NEMOCLAW_MODEL_OVERRIDE → sandbox starts with overridden model
  • Unset env var → sandbox starts with original baked model (no regression)
  • Set invalid model → sandbox starts but inference fails (expected)
  • Config hash passes integrity check on restart after override

Signed-off-by: Prekshi Vyas prekshiv@nvidia.com

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Env vars to override the active model and (optionally) the inference API at container startup; when used in privileged startup, the runtime config is updated and its integrity hash recomputed so startup verification aligns.
  • Runtime safeguards

    • Input validation, API allowlist, symlink protections, applies only in privileged mode, and no-op behavior when unset.
  • Tests

    • New unit and end-to-end tests covering override behavior, timing, hash recomputation, validation, and noop cases.
  • Documentation

    • Guidance added for cross-provider switching and the runtime override workflow.

Allow model and provider changes without rebuilding the sandbox image.
The entrypoint patches openclaw.json at startup when env vars are set,
then recomputes the config hash so integrity checks still pass.

New env vars:
- NEMOCLAW_MODEL_OVERRIDE: patches agents.defaults.model.primary
  and the provider model name. Must match the model configured on
  the gateway via openshell inference set.
- NEMOCLAW_INFERENCE_API_OVERRIDE: patches the inference API type
  (e.g., "anthropic-messages" or "openai-completions"). Only needed
  for cross-provider switches.

Security: env vars come from the host (Docker/OpenShell), not from
inside the sandbox. Landlock locks the file after patching. Same
trust model as NEMOCLAW_LOCAL_INFERENCE_TIMEOUT.

Closes NVIDIA#759

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 8, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a runtime model override: a new apply_model_override() in the container startup script that, when allowed, rewrites /sandbox/.openclaw/openclaw.json (and recomputes .config-hash) based on NEMOCLAW_MODEL_OVERRIDE and optional NEMOCLAW_INFERENCE_API_OVERRIDE; tests and docs updated accordingly.

Changes

Cohort / File(s) Summary
Startup Script
scripts/nemoclaw-start.sh
Adds apply_model_override() which validates NEMOCLAW_MODEL_OVERRIDE, refuses symlinked config/hash paths, only runs as root, updates agents.defaults.model.primary, sets each provider model id/name to the override, optionally replaces provider api (allowlist enforced), writes openclaw.json, and recomputes /sandbox/.openclaw/.config-hash. Hooked into both startup branches after verify_config_integrity.
Unit Tests
test/nemoclaw-start.test.js
Refines parsing/diagnostic extraction and adds a runtime model override (#759) test suite asserting presence/placement of apply_model_override(), references to env vars, invocation order in root/non-root paths, sha256 hash recomputation, no-op when unset, symlink refusal message, allowlist enforcement, and control-character rejection.
E2E Tests
test/e2e-gateway-isolation.sh
Adds two e2e assertions: one invokes apply_model_override with NEMOCLAW_MODEL_OVERRIDE="test/override-model" and verifies agents.defaults.model.primary and providers' id/name are rewritten; the other asserts no change when the override is unset. Tests signal pass/fail via sentinel strings.
Docs / Skill
.agents/skills/nemoclaw-user-configure-inference/SKILL.md, docs/inference/switch-inference-providers.md
Adds “Cross-Provider Switching” instructions: update host gateway route (openshell inference set ... --no-verify), export NEMOCLAW_MODEL_OVERRIDE (and NEMOCLAW_INFERENCE_API_OVERRIDE if changing provider families), then run nemoclaw onboard --resume --recreate-sandbox so the entrypoint applies the override; clarifies same-provider vs cross-provider behavior and sandbox recreate requirement.

Sequence Diagram(s)

sequenceDiagram
  participant Entrypoint as Entrypoint Script
  participant Verifier as verify_config_integrity
  participant Override as apply_model_override()
  participant FS as Sandbox FS (/sandbox/.openclaw)
  participant Hasher as sha256sum

  Entrypoint->>Verifier: call verify_config_integrity
  Verifier-->>Entrypoint: integrity OK
  Entrypoint->>Override: call apply_model_override()
  Override->>FS: check ownership & symlink status
  alt running as root and NEMOCLAW_MODEL_OVERRIDE set
    Override->>FS: read openclaw.json
    Override->>FS: modify agents.defaults.model.primary
    Override->>FS: set providers' models[].id and .name
    alt NEMOCLAW_INFERENCE_API_OVERRIDE set (allowed)
      Override->>FS: replace providers' api fields
    end
    Override->>FS: write openclaw.json
    Override->>Hasher: compute sha256sum openclaw.json
    Hasher-->>FS: write .config-hash
  else no-op (non-root or unset)
    Override-->>Entrypoint: no changes
  end
  Override-->>Entrypoint: return
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I nibble config lines at dawn,
I hop and swap the model pawn,
A guarded rewrite, hash remade,
Sandbox wakes with changes laid,
The little rabbit hums, well-played.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(sandbox): runtime model override via env vars' accurately summarizes the main change: adding runtime overrides via environment variables to switch sandbox models without rebuilding.
Linked Issues check ✅ Passed The PR fulfills issue #759 by providing a host-driven mechanism to override sandbox model configuration at runtime via NEMOCLAW_MODEL_OVERRIDE and NEMOCLAW_INFERENCE_API_OVERRIDE env vars, with integrity checks and secure implementation preventing tampering.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing the runtime model override feature: the core function in nemoclaw-start.sh, comprehensive tests, documentation updates, and e2e validation—nothing unrelated to the stated objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/nemoclaw-start.sh`:
- Line 623: The script calls apply_model_override which attempts to open
/sandbox/.openclaw/openclaw.json with "w" even when running as non-root while
install_configure_guard()/configure_messaging_channels have made
/sandbox/.openclaw Landlock read-only; this causes NEMOCLAW_MODEL_OVERRIDE to
fail under set -e. Fix by updating apply_model_override to first detect uid (id
-u) and if non-root either (A) require/gate the write path to root (skip or sudo
the write) or (B) write the override into a per-user writable config mirror
(e.g., create a writable copy directory for the non-root fallback and write into
that mirror) and ensure subsequent reads prefer the mirror; also add a pre-check
in apply_model_override that verifies the target file is writable and falls back
cleanly rather than causing an immediate set -e exit.
- Around line 187-223: The script writes config_file and hash_file before
symlink validation; update apply_model_override() (or the section using
variables config_file and hash_file) to reject or handle symlinks: before
opening/writing config_file and before writing hash_file, check if each path is
a symlink (e.g., test -L or use readlink) and abort with an error if so, or
write to a temp file in the same directory and atomically mv into place only
after verifying the target is not a symlink; ensure the same symlink guard is
applied to both config_file and hash_file so they cannot be pre-created as
symlinks and followed/overwritten.

In `@test/nemoclaw-start.test.js`:
- Around line 207-213: The test currently uses a regex that starts at the first
verify_config_integrity and matches helper definitions; instead extract the
non-root shell block 'if [ "$(id -u)" -ne 0 ]; then ... fi' from src first and
then assert that within that extracted block the sequence
verify_config_integrity → apply_model_override → export_gateway_token occurs;
update the matcher in test/nemoclaw-start.test.js to locate the non-root
if-block and run the ordering regex against that block (referencing
verify_config_integrity, apply_model_override, and export_gateway_token to
identify the calls).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: cd47e5eb-bd44-45cb-81d8-0271a4f2e336

📥 Commits

Reviewing files that changed from the base of the PR and between 1aa8427 and 125fadf.

📒 Files selected for processing (2)
  • scripts/nemoclaw-start.sh
  • test/nemoclaw-start.test.js

Comment thread scripts/nemoclaw-start.sh Outdated
Comment thread scripts/nemoclaw-start.sh
Comment thread test/nemoclaw-start.test.ts
Copy link
Copy Markdown
Contributor

@cv cv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security Review — WARNING

The shell/Python mechanics are solid — proper quoting, single-quoted heredoc, argv passing, json.load/dump. No injection vectors. Call ordering is correct.

Requested changes

1. Input validation (MEDIUM):

  • NEMOCLAW_INFERENCE_API_OVERRIDE should be checked against an allowlist (e.g., anthropic-messages, openai-completions). An arbitrary value here could cause unexpected provider routing behavior.
  • NEMOCLAW_MODEL_OVERRIDE should reject control characters, newlines, and enforce a reasonable length limit. This converts the documentation-only contract ("must match the gateway config") into a code-enforced one.

2. Document as security-sensitive (MEDIUM):
NEMOCLAW_MODEL_OVERRIDE controls where the agent sends all prompts. Anyone who can set this env var controls inference routing. Please:

  • Document this explicitly in the PR description / docs
  • Consider logging at [SECURITY] level (not just [config]) when an override is active

Minor (non-blocking)

  • Use printf '%s\n' "$value" instead of echo "$value" in log lines to prevent terminal escape sequence injection (low risk since values come from host, but more defensive)

- Patch model.id in addition to model.name — id is what OpenClaw
  sends in API requests, name is the display reference
- Add e2e tests 26-27 to e2e-gateway-isolation.sh verifying the
  override patches primary/id/name, and is a no-op when unset
- Document cross-provider switching in switch-inference-providers.md
  with env var usage and recreate-sandbox workflow
- Update Notes section to clarify same-provider vs cross-provider

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/inference/switch-inference-providers.md`:
- Around line 96-100: Clarify that same-provider model switches (e.g., between
two NVIDIA models) only require updating the gateway route and do not need
NEMOCLAW_INFERENCE_API_OVERRIDE, but do require setting NEMOCLAW_MODEL_OVERRIDE;
make the revert path explicit: removing NEMOCLAW_INFERENCE_API_OVERRIDE and/or
NEMOCLAW_MODEL_OVERRIDE in the environment will not take effect until the
sandbox is restarted/recreated because overrides are applied at startup, so
document that users must recreate the sandbox to revert to the original model;
update the conflicting lines to reflect this consistent behavior and mention
both env vars by name (NEMOCLAW_INFERENCE_API_OVERRIDE, NEMOCLAW_MODEL_OVERRIDE)
and the sandbox recreation step.

In `@test/e2e-gateway-isolation.sh`:
- Around line 391-399: The current loop overwrites model_id/model_name on each
iteration so only the last model is validated; update the logic that iterates
providers -> pval.get("models") to validate every model entry (e.g., perform the
equality check against primary inside the inner loop or accumulate a boolean
that requires all models to match) using the variables pval, models, model_id,
model_name and primary, and only print "OVERRIDE_OK" when every model's id and
name equal "test/override-model" (otherwise print the failure with the first
mismatched model's details).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 541f98df-72e2-4124-ba37-07dbe88c93bd

📥 Commits

Reviewing files that changed from the base of the PR and between 125fadf and 34965dc.

📒 Files selected for processing (4)
  • .agents/skills/nemoclaw-user-configure-inference/SKILL.md
  • docs/inference/switch-inference-providers.md
  • scripts/nemoclaw-start.sh
  • test/e2e-gateway-isolation.sh
🚧 Files skipped from review as they are similar to previous changes (1)
  • scripts/nemoclaw-start.sh

Comment thread docs/inference/switch-inference-providers.md Outdated
Comment thread test/e2e-gateway-isolation.sh Outdated
prekshivyas and others added 3 commits April 8, 2026 15:02
Address CodeRabbit and security review (Carlos):
- Gate override to root mode only (sandbox user can't write 444 files)
- Add symlink guard on config and hash files before writing
- Validate model override: reject control chars, enforce 256 char limit
- Allowlist inference API types (openai-completions, anthropic-messages)
- Use [SECURITY] log level for override messages (printf, not echo)
- Fix test regex to target non-root block specifically
- Add tests for symlink guard, root-only gate, input validation
- Fix e2e test to validate all model entries, not just last
- Fix docs: revert requires sandbox recreate, remove contradictions
- Fix 2 pre-existing test failures (non-root block extraction used
  ^fi$ which matched nested fi, now uses # ── Root path boundary)

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
test/nemoclaw-start.test.js (1)

223-225: Make root-path ordering assertion less brittle to harmless comments.

Current matcher requires adjacent lines; a comment between calls would fail despite correct order. Prefer the same flexible sequencing style used in the non-root assertion.

Suggested refactor
-    const rootBlock = src.match(
-      /# ── Root path[\s\S]*?verify_config_integrity\n\s*apply_model_override\n\s*export_gateway_token/,
-    );
+    const rootBlock = src.match(
+      /# ── Root path[\s\S]*?verify_config_integrity[\s\S]*?apply_model_override[\s\S]*?export_gateway_token/,
+    );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/nemoclaw-start.test.js` around lines 223 - 225, The current rootBlock
regex in the test (src.match) is brittle because it requires
verify_config_integrity, apply_model_override, and export_gateway_token to be on
adjacent lines; update the pattern to allow arbitrary content (including
comments and blank lines) between those calls using the same flexible sequencing
approach used in the non-root assertion so that the order is enforced but not
adjacency (i.e., replace the /\n\s*/ separators with a non-greedy [\s\S]*?
between each symbol: verify_config_integrity, apply_model_override,
export_gateway_token in the rootBlock match).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/nemoclaw-start.test.js`:
- Around line 50-53: The brace-group stripping regex used when building
braceStripped fails to match redirects that include spaces after the '>' (it
currently expects no space); update the regex used on block (the one within the
assignment to braceStripped) to allow optional whitespace between '>' and the
quoted filename (e.g. change the pattern so it matches
/\{[\s\S]*?\}\s*>\s*"[^"]*"/g), ensuring brace-group redirects like > "file" are
stripped correctly.

---

Nitpick comments:
In `@test/nemoclaw-start.test.js`:
- Around line 223-225: The current rootBlock regex in the test (src.match) is
brittle because it requires verify_config_integrity, apply_model_override, and
export_gateway_token to be on adjacent lines; update the pattern to allow
arbitrary content (including comments and blank lines) between those calls using
the same flexible sequencing approach used in the non-root assertion so that the
order is enforced but not adjacency (i.e., replace the /\n\s*/ separators with a
non-greedy [\s\S]*? between each symbol: verify_config_integrity,
apply_model_override, export_gateway_token in the rootBlock match).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 22fff212-fbd9-4cf4-94e3-9ecb063de068

📥 Commits

Reviewing files that changed from the base of the PR and between f38c44a and aefac93.

📒 Files selected for processing (5)
  • .agents/skills/nemoclaw-user-configure-inference/SKILL.md
  • docs/inference/switch-inference-providers.md
  • scripts/nemoclaw-start.sh
  • test/e2e-gateway-isolation.sh
  • test/nemoclaw-start.test.js
✅ Files skipped from review due to trivial changes (1)
  • .agents/skills/nemoclaw-user-configure-inference/SKILL.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • test/e2e-gateway-isolation.sh
  • scripts/nemoclaw-start.sh
  • docs/inference/switch-inference-providers.md

Comment thread test/nemoclaw-start.test.ts
@wscurran wscurran added NemoClaw CLI Use this label to identify issues with the NemoClaw command-line interface (CLI). enhancement: feature Use this label to identify requests for new capabilities in NemoClaw. labels Apr 8, 2026
prekshivyas and others added 4 commits April 8, 2026 15:28
CodeRabbit review: the regex stripping brace-group redirects missed
the common form with whitespace after >, e.g. } > "file".

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ride (NVIDIA#719)

Adds apply_cors_override() following the same pattern as the model
override: host-set env var, applied after integrity check and before
chattr +i, config hash recomputed.

Users deploying on custom domains/ports can now add their browser
origin without rebuilding the sandbox:

  export NEMOCLAW_CORS_ORIGIN="https://my-server.example.com:8443"

Security guards: symlink check, control character rejection, length
limit, http/https URL validation. Only applies in root mode.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…overrides

Extend apply_model_override() to also accept:
- NEMOCLAW_CONTEXT_WINDOW: override model context window size
- NEMOCLAW_MAX_TOKENS: override model max output tokens
- NEMOCLAW_REASONING: enable/disable reasoning mode (true/false)

These can be set independently of NEMOCLAW_MODEL_OVERRIDE — useful
for tuning model parameters without switching models.

Validation: context window and max tokens must be positive integers,
reasoning must be "true" or "false".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Make the root-path call-order regex use [\s\S]*? between function calls
instead of \n\s*, so comments or blank lines between calls don't break
the test. Matches the style used in the non-root assertion.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
14 tests covering all runtime env var overrides:
- No-op baseline (config hash valid without overrides)
- Individual overrides: model, context window, max tokens, reasoning, CORS
- Combined overrides (all 5 at once)
- Validation rejections: control chars, non-integer, invalid reasoning,
  non-http CORS, invalid API type (6 cases)
- Config unchanged after rejected override

Each test runs as an independent short-lived container — safe for
parallel CI execution.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@prekshivyas prekshivyas requested a review from cv April 9, 2026 03:16
@cv cv removed their request for review April 9, 2026 19:55
@brandonpelfrey
Copy link
Copy Markdown
Collaborator

Full Onboard + Inference Test — PR #1633

Date: 2026-04-09 22:35–22:51 UTC
Environment: DinD container (nemoclaw-reviewer-onboard:latest + lsof + perl-utils)
PR SHA: 8d58d4a08ff3275cd3d2428cefc0cc2c0b0e7d62

Phase 1: Install from PR source ✅

$ bash install.sh --non-interactive --yes-i-accept-third-party-software
  • Node.js v22.22.2 detected
  • NemoClaw installed from source (npm ci, tsc -p tsconfig.src.json)
  • OpenClaw plugin built (npm run build)
  • nemoclaw CLI linked to /root/.local/bin/nemoclaw
  • Duration: ~25s

Phase 2: Onboard ✅

  • Preflight: Docker ✓, openshell 0.0.26 ✓, ports 8080+18789 free ✓, no GPU (cloud mode) ✓, 15990 MB RAM ✓
  • Gateway: OpenShell gateway started, healthy after ~60s
  • Inference: NVIDIA Endpoints, model nvidia/nemotron-3-super-120b-a12b, API openai-completions
  • Provider: nvidia-prod created
  • Sandbox pr-test created (Landlock + seccomp + netns)
  • DNS proxy configured (4/4 checks passed)
  • OpenClaw gateway launched inside sandbox
  • Total onboard duration: 310s (~5 min)

Phase 3: Sandbox verification ✅

$ nemoclaw pr-test status
  Sandbox: pr-test
    Model:    nvidia/nemotron-3-super-120b-a12b
    Provider: nvidia-prod
    GPU:      no
    Phase:    Ready

Live config from sandbox:

Primary model: inference/nvidia/nemotron-3-super-120b-a12b
Provider: inference (api: openai-completions)
  nvidia/nemotron-3-super-120b-a12b: ctx=131072, max=4096, reasoning=False
CORS allowedOrigins: ['http://127.0.0.1:18789']
Config hash: 18d011b3... ✓ (matches .config-hash file)

Phase 4: Live inference ✅

Test 1:

$ openclaw agent --local -m 'Reply with exactly one word: WORKING' --session-id test-inference-1
→ WORKING

Test 2:

$ openclaw agent --local -m 'What is 2+2? Reply with just the number.' --session-id test-inference-2
→ 4

Both inference calls went through NVIDIA Endpoints → OpenShell gateway proxy → sandbox → model → response.

Phase 5: Runtime entrypoint tests (in DinD container, not sandbox) ✅ 10/10

Ran test-entrypoint.sh against PR's nemoclaw-start.sh in a separate Debian container:

# Test Result Evidence
1 No-op when env unset PASS Config SHA unchanged after sourcing function with no env vars
2 Valid model override PASS Set NEMOCLAW_MODEL_OVERRIDE=nvidia/new-model → config JSON shows nvidia/new-model as primary
3 Hash recomputation PASS sha256sum -c .config-hash → OK after override
4 Symlink rejection PASS Replaced config with symlink → output: "Refusing model override — config or hash path is a symlink"
5 Invalid API rejection PASS Set NEMOCLAW_INFERENCE_API_OVERRIDE=evil-api → output: error mentions "evil-api"
6 Control char rejection PASS Set model to model\x01evil → output: "control characters"
7 Length overflow rejection PASS Set model to 300 chars → output: "256 characters"
8 Non-root rejection PASS Ran as nobody → config unchanged
9 CORS override PASS Set NEMOCLAW_CORS_ORIGIN=https://my-server.example.com:8443 → origin added to allowedOrigins
10 Invalid CORS origin PASS Set NEMOCLAW_CORS_ORIGIN=ftp://evil.com → rejected (must start with http(s)://)

Phase 6: Model override in live sandbox ⚠️ (not testable via sandbox exec)

openshell sandbox exec runs as the sandbox user (non-root). The apply_model_override() function correctly refuses to run as non-root — this is the designed security behavior. To test the override in a live sandbox, the sandbox must be destroyed and recreated with NEMOCLAW_MODEL_OVERRIDE set as a container env var, which triggers the override at entrypoint time (as root).

This is correct behavior — the override is a startup-time operation, not a runtime hot-swap. The entrypoint security tests (Phase 5) comprehensively verify the override logic works correctly.

Issues Found During Testing

  1. BusyBox lsof incompatibility — Alpine's BusyBox lsof doesn't support -i :PORT -sTCP:LISTEN flags, causing false positives in NemoClaw's port preflight check. Fix: install real lsof package. (Not a PR feat(sandbox): runtime model override via env vars #1633 issue; pre-existing in the installer.)

  2. BusyBox shasum missing — Alpine doesn't ship shasum (needed by install-openshell.sh). Fix: apk add perl-utils. (Not a PR feat(sandbox): runtime model override via env vars #1633 issue; pre-existing.)

Summary

Area Status
Install from PR source ✅ Clean build, no errors
Non-interactive onboard ✅ Full onboard in 310s
Sandbox creation ✅ Ready, Landlock+seccomp+netns
Live inference (NVIDIA API) ✅ Two successful completions
Entrypoint security tests ✅ 10/10 pass
Config hash integrity ✅ Verified
Model override logic ✅ Verified via isolated tests

Verdict: PR #1633's runtime override feature works correctly. The security validation is thorough (input sanitization, symlink protection, root-only enforcement, allowlisted values). Inference through NVIDIA Endpoints is functional.

@brandonpelfrey brandonpelfrey merged commit 3f281f9 into NVIDIA:main Apr 9, 2026
13 checks passed
hodgesz added a commit to hodgesz/NemoClaw that referenced this pull request Apr 10, 2026
…ig injection

Upstream PR NVIDIA#1633 added env var support for runtime model overrides,
making the openclaw.json injection hack unnecessary for model switching.

- auto-start now passes --skip-gemini to apply-custom-policies.sh
- apply-custom-policies.sh skips Steps 1+2 (Gemini inject + hash) when
  --skip-gemini is set; fetch-guard patch, device pairing, and skills
  still run (NemoClaw NVIDIA#1252 is still open)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@prekshivyas prekshivyas self-assigned this Apr 10, 2026
ericksoa pushed a commit to cheese-head/NemoClaw that referenced this pull request Apr 14, 2026
## Summary

- Allow model and provider changes without rebuilding the sandbox image
- The entrypoint patches `openclaw.json` at startup when
`NEMOCLAW_MODEL_OVERRIDE` is set, then recomputes the config hash so
integrity checks still pass
- Same pattern as `NEMOCLAW_LOCAL_INFERENCE_TIMEOUT` (PR NVIDIA#1620)

### New env vars

| Env var | Purpose | When needed |
|---------|---------|-------------|
| `NEMOCLAW_MODEL_OVERRIDE` | Override `agents.defaults.model.primary`
and provider model name | Any model switch |
| `NEMOCLAW_INFERENCE_API_OVERRIDE` | Override inference API type
(`openai-completions` or `anthropic-messages`) | Cross-provider switches
only |

### Usage example (NVIDIA → Anthropic)

```bash
# On host: configure gateway route
openshell inference set --provider anthropic-prod --model claude-sonnet-4-6 --no-verify

# Set env vars for the sandbox (via openshell or Docker)
export NEMOCLAW_MODEL_OVERRIDE="anthropic/claude-sonnet-4-6"
export NEMOCLAW_INFERENCE_API_OVERRIDE="anthropic-messages"

# Restart sandbox — no image rebuild needed
```

### Security

- Env vars come from the host (Docker/OpenShell), not from inside the
sandbox
- Config integrity is verified first (detects build-time tampering),
then override is applied
- Config hash is recomputed after patching
- Landlock locks the file after this function runs
- Agent cannot set these env vars

## Related Issue

Closes NVIDIA#759

## Test plan

- [ ] `npm test` passes (39 tests in nemoclaw-start.test.js)
- [ ] Set `NEMOCLAW_MODEL_OVERRIDE` → sandbox starts with overridden
model
- [ ] Unset env var → sandbox starts with original baked model (no
regression)
- [ ] Set invalid model → sandbox starts but inference fails (expected)
- [ ] Config hash passes integrity check on restart after override

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Env vars to override the active model and (optionally) the inference
API at container startup; when used in privileged startup, the runtime
config is updated and its integrity hash recomputed so startup
verification aligns.

* **Runtime safeguards**
* Input validation, API allowlist, symlink protections, applies only in
privileged mode, and no-op behavior when unset.

* **Tests**
* New unit and end-to-end tests covering override behavior, timing, hash
recomputation, validation, and noop cases.

* **Documentation**
* Guidance added for cross-provider switching and the runtime override
workflow.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Carlos Villela <cvillela@nvidia.com>
gemini2026 pushed a commit to gemini2026/NemoClaw that referenced this pull request Apr 14, 2026
## Summary

- Allow model and provider changes without rebuilding the sandbox image
- The entrypoint patches `openclaw.json` at startup when
`NEMOCLAW_MODEL_OVERRIDE` is set, then recomputes the config hash so
integrity checks still pass
- Same pattern as `NEMOCLAW_LOCAL_INFERENCE_TIMEOUT` (PR NVIDIA#1620)

### New env vars

| Env var | Purpose | When needed |
|---------|---------|-------------|
| `NEMOCLAW_MODEL_OVERRIDE` | Override `agents.defaults.model.primary`
and provider model name | Any model switch |
| `NEMOCLAW_INFERENCE_API_OVERRIDE` | Override inference API type
(`openai-completions` or `anthropic-messages`) | Cross-provider switches
only |

### Usage example (NVIDIA → Anthropic)

```bash
# On host: configure gateway route
openshell inference set --provider anthropic-prod --model claude-sonnet-4-6 --no-verify

# Set env vars for the sandbox (via openshell or Docker)
export NEMOCLAW_MODEL_OVERRIDE="anthropic/claude-sonnet-4-6"
export NEMOCLAW_INFERENCE_API_OVERRIDE="anthropic-messages"

# Restart sandbox — no image rebuild needed
```

### Security

- Env vars come from the host (Docker/OpenShell), not from inside the
sandbox
- Config integrity is verified first (detects build-time tampering),
then override is applied
- Config hash is recomputed after patching
- Landlock locks the file after this function runs
- Agent cannot set these env vars

## Related Issue

Closes NVIDIA#759

## Test plan

- [ ] `npm test` passes (39 tests in nemoclaw-start.test.js)
- [ ] Set `NEMOCLAW_MODEL_OVERRIDE` → sandbox starts with overridden
model
- [ ] Unset env var → sandbox starts with original baked model (no
regression)
- [ ] Set invalid model → sandbox starts but inference fails (expected)
- [ ] Config hash passes integrity check on restart after override

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Env vars to override the active model and (optionally) the inference
API at container startup; when used in privileged startup, the runtime
config is updated and its integrity hash recomputed so startup
verification aligns.

* **Runtime safeguards**
* Input validation, API allowlist, symlink protections, applies only in
privileged mode, and no-op behavior when unset.

* **Tests**
* New unit and end-to-end tests covering override behavior, timing, hash
recomputation, validation, and noop cases.

* **Documentation**
* Guidance added for cross-provider switching and the runtime override
workflow.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Carlos Villela <cvillela@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement: feature Use this label to identify requests for new capabilities in NemoClaw. NemoClaw CLI Use this label to identify issues with the NemoClaw command-line interface (CLI).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Sandbox config file (openclaw.json) is root-owned and unwritable — no supported path to change primary model

4 participants