Skip to content

fix(sandbox): rewrite #2109 proxy fix as http.request wrapper - #1

Merged
lcsmontiel merged 17 commits into
mainfrom
fix/2109-http-proxy-wrapper
Apr 23, 2026
Merged

lcsmontiel merged 17 commits into
mainfrom
fix/2109-http-proxy-wrapper

Conversation

@lcsmontiel

Copy link
Copy Markdown
Owner

Summary

PR NVIDIA#2110's axios-proxy-fix.js Module._load preload never fired in production:

  1. nemoclaw-blueprint/scripts/ is excluded from the optimized sandbox build context (see src/lib/sandbox-build-context.ts), so axios-proxy-fix.js never landed in the image.
  2. Adding scripts/ to the build context cache-busts the COPY nemoclaw-blueprint/ layer and hangs npm ci in the k3s Docker-in-Docker build — the delivery gap can't be closed by expanding the context.
  3. Even if the file had reached the image, intercepting require('axios') via Module._load can't patch follow-redirects + proxy-from-env bundled as ESM in OpenClaw's dist/http-Bh-HtMAg.js — there are no require() calls to intercept. The Bot Connector reply path uses the bundled code.

This change replaces the preload with an http.request() wrapper — the lowest common denominator every HTTP client bottoms out at. When a library configures an HTTP FORWARD-mode request (hostname == proxy, path == https://...), the wrapper rewrites it as https.request() against the real target, letting NODE_USE_ENV_PROXY handle the CONNECT tunnel correctly. Works for axios, follow-redirects, proxy-from-env, and any bundled ESM that makes no require() calls.

Delivery

Reproduction (from original issue)

Inside a NemoClaw sandbox with NODE_USE_ENV_PROXY=1, before the fix:

```bash

FAIL — follow-redirects configures FORWARD, NODE_USE_ENV_PROXY double-processes

node -e "require('axios').get('https://clawhub.ai', {timeout:10000}).then(r => console.log('PASS', r.status)).catch(e => console.log('FAIL', e.code))"
```

After the fix, the same call returns `PASS 200`.

Test plan

Fixes NVIDIA#2109. Supersedes NVIDIA#2110.

cjagwani and others added 17 commits April 22, 2026 14:04
…NVIDIA#2178) (NVIDIA#2298)

## Summary

Fixes NVIDIA#2178 (NV QA UAT: \`node\` still reports v20 after installer
claims v22.22.2 was installed).

When \`scripts/install.sh\` upgrades Node via nvm, \`nvm use 22\` only
takes effect inside the installer's subshell — the user's parent shell
still resolves \`node\` to the pre-install version until they reload.
The existing generic \`source <profile>\` hint at the bottom of the
installer is easy to miss.

Two layers:

### 1. Loud warning adjacent to install line

```
[INFO]  Node.js installed via nvm: v22.22.2 (default alias)
[WARN]  Your current shell may still resolve `node` to an older version until you reload it.
        To activate v22.22.2 in this shell:
          exec "$SHELL" -l        # (or) nvm use 22
```

### 2. Opt-in auto-activation prompt (new)

At the end of \`print_done\`, when the upgrade path actually ran AND the
user is on a real TTY AND \`--non-interactive\` is not set:

```
  Reload your shell now to activate Node v22.22.2? [Y/n]
```

On accept → \`exec \"\$SHELL\" -l\`, replacing the installer process so
\`node --version\` prints v22 in the very next prompt with zero extra
user action. On decline → noop (warning + manual command still on
screen).

## Gates preserving scriptability

- \`NON_INTERACTIVE=1\` / \`--non-interactive\` → skip prompt (keeps
CI-style flows quiet)
- stdin not a TTY (e.g. \`curl | bash\`) → skip prompt (keeps chained
invocations like \`curl | bash && nemoclaw onboard\` working)
- Upgrade path didn't run (Node was already >= 22) → skip prompt
(nothing to reload for)

In all skipped cases the loud warning + manual command remain visible.

## Test plan

- [x] \`npx vitest run --project cli test/install-preflight.test.ts\` —
two new tests pass (hint presence, helper gating + exec path)
- [x] \`bash -n scripts/install.sh\` syntax OK; \`install.sh --help\`
unaffected

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

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

* **Bug Fixes**
* Installer now reports the exact Node.js version with nvm-specific
messaging, warns that the current shell may still resolve an older Node,
and prints an explicit pasteable command to activate the new version in
the current session.

* **Tests**
* Added tests that verify the installer’s upgrade messaging and presence
of the activation instructions.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary
This PR tightens typing around agent manifest parsing in
`src/lib/agent-defs.ts` by replacing the old catch-all record type with
recursive manifest value guards. That keeps the YAML loader and field
readers typed more precisely, reduces a hotspot in the manifest loader,
and adds coverage for invalid top-level manifest payloads.

## Changes
- replace `UnknownRecord` in `src/lib/agent-defs.ts` with recursive
`ManifestValue` and `ManifestRecord` types
- validate parsed YAML manifests with `isManifestRecord()` while still
accepting scalar values emitted by `js-yaml`, including timestamp `Date`
objects
- add a regression test in `src/lib/agent-defs.test.ts` that rejects
non-object manifest payloads

## Type of Change
- [x] Code change (feature, bug fix, or refactor)
- [ ] Code change with doc updates
- [ ] Doc only (prose changes, no code sample modifications)
- [ ] Doc only (includes code sample changes)

## Verification
- [x] `npx prek run --all-files` passes
- [x] `npm test` passes
- [x] Tests added or updated for new or changed behavior
- [x] No secrets, API keys, or credentials committed
- [ ] Docs updated for user-facing behavior changes
- [ ] `make docs` builds without warnings (doc changes only)
- [ ] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

## AI Disclosure
- [x] AI-assisted — tool: OpenAI Codex

---
Signed-off-by: Carlos Villela <cvillela@nvidia.com>

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

## Summary by CodeRabbit

* **Tests**
* Added validation test to ensure agent manifest configuration is
properly structured as an object.

* **Bug Fixes**
* Strengthened agent manifest parsing validation to reject malformed
configuration files and enforce stricter type constraints on
configuration values.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: Carlos Villela <cvillela@nvidia.com>
…VIDIA#2243)

## Summary

Add `test/e2e/test-diagnostics.sh` with 5 end-to-end test cases covering
CLI version output, debug snapshots, credential sanitization in debug
tarballs, sandbox inference config visibility, and credential list
safety. Integrates as `diagnostics-e2e` job in `nightly-e2e.yaml`.

## Related Issue

Closes NVIDIA#2242

## Changes

- **Version output (TC-DIAG-04):** Runs `nemoclaw --version`, verifies
output matches semver pattern and exits with code 0. No sandbox needed.
- **Quick debug snapshot (TC-DIAG-02):** Runs `nemoclaw debug --quick
--output <path>`, verifies a non-empty archive is produced within 30
seconds. No sandbox needed.
- **Full debug tarball + credential sanitization (TC-DIAG-01):** Runs
`nemoclaw debug --output <path>`, extracts the tarball, greps all files
for the real API key value and `nvapi-` patterns. Verifies no
credentials found in any collected file.
- **Sandbox inference config (TC-DIAG-05):** Reads `openclaw.json`
inside the sandbox via SSH, verifies the model field is present. Runs
`nemoclaw status` from the host and verifies the Model field appears in
output.
- **Credentials list safety (TC-DIAG-03):** Runs `nemoclaw credentials
list`, verifies key names are shown but the real API key value is not
exposed. Handles the CI case where the credential store is empty (API
key passed via env var).
- **Nightly integration:** Added `diagnostics-e2e` job to
`nightly-e2e.yaml` with 45-minute timeout, artifact upload on failure,
and wired into `notify-on-failure`.

## Type of Change

- Code change (feature, bug fix, or refactor)

## Verification

- `npx prek run --all-files` passes
- Tests added or updated for new or changed behavior
- No secrets, API keys, or credentials committed
- Verified on CI: all PASS on ubuntu-latest

## AI Disclosure

- AI-assisted — tool: Cursor

---

Signed-off-by: Truong Nguyen <tgnguyen@nvidia.com>

Made with [Cursor](https://cursor.com)

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

* **Tests**
* Added an end-to-end diagnostics test suite covering version checks,
debug/export operations, credential handling (preventing secret
leakage), sandbox onboarding/status, per-test PASS/FAIL/SKIP reporting,
timestamped logs, and overall timeout support.

* **Chores**
* Nightly CI now runs diagnostics, uploads diagnostic logs on failure,
and extends failure notifications so diagnostics failures trigger the
existing alert workflow.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: Truong Nguyen <tgnguyen@nvidia.com>
## Summary

Fix TC-NET-03 always skipping on nightly by switching from a
base-policy-whitelisted endpoint to one only reachable via preset.

## Related Issue

Fixes NVIDIA#2274

## Changes

- **TC-NET-03:** Switched from `api.telegram.org` (whitelisted in base
sandbox policy, always reachable) to `slack.com` (only reachable after
applying slack preset). The blocked → add preset → reachable flow is now
actually tested.
- **TC-NET-04:** Switched from `slack.com` to `api.atlassian.com` (jira
preset) because TC-NET-03 now applies the slack preset earlier in the
suite. Each test uses a unique endpoint to avoid interference.
- **Nightly workflow:** Added `NEMOCLAW_POLICY_TIER: restricted` to the
`network-policy-e2e` job env to ensure no presets are applied during
initial setup.
- **install_nemoclaw():** Added `NEMOCLAW_POLICY_TIER=restricted` to the
install.sh call so the initial onboard also uses restricted tier.

## Type of Change

- Code change (feature, bug fix, or refactor)

## Verification

- `npx prek run --all-files` passes
- Verified on CI: 10/10 PASS, 0 SKIP on ubuntu-latest

## AI Disclosure

- AI-assisted — tool: Cursor

---

Signed-off-by: Truong Nguyen <tgnguyen@nvidia.com>

Made with [Cursor](https://cursor.com)

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

* **Tests**
* E2E network-policy tests updated to target different service
integrations (Slack and Atlassian) and to align their checks and
messages with those integrations.
* **Chores**
* Installation and test environments standardized to use the
"restricted" policy tier.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: Truong Nguyen <tgnguyen@nvidia.com>
…IA#2279)

install.sh previously delegated all OpenShell CLI install/upgrade to
`nemoclaw onboard`. When onboard is skipped (host preflight blocks, user
aborts, interrupted session) the version gate never runs, so a curl|bash
upgrade could leave openshell stale even though the new NemoClaw release
declares a higher min_openshell_version.

Run install-openshell.sh at the tail of install_nemoclaw instead. The
script is idempotent — it exits 0 if the installed version is already
within [MIN_VERSION, MAX_VERSION], so calling it every install is free
on the happy path. The onboard-side check at onboard.ts:2664-2706 stays
in place as a safety net for direct `nemoclaw onboard` invocations
outside the installer.

Fixes NVIDIA#2272.

<!-- markdownlint-disable MD041 -->
## Summary
<!-- 1-3 sentences: what this PR does and why. -->

## Related Issue
<!-- Fixes #NNN or Closes #NNN. Remove this section if none. -->

## Changes
<!-- Bullet list of key changes. -->

## Type of Change

- [X] Code change (feature, bug fix, or refactor)
- [ ] Code change with doc updates
- [ ] Doc only (prose changes, no code sample modifications)
- [ ] Doc only (includes code sample changes)

## Verification
<!-- Check each item you ran and confirmed. Leave unchecked items you
skipped. -->
- [X] `npx prek run --all-files` passes
- [X] `npm test` passes
- [ ] Tests added or updated for new or changed behavior
- [X] No secrets, API keys, or credentials committed
- [ ] Docs updated for user-facing behavior changes
- [ ] `make docs` builds without warnings (doc changes only)
- [ ] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

## AI Disclosure
<!-- If an AI agent authored or co-authored this PR, check the box and
name the tool. Remove this section for fully human-authored PRs. -->
- [X] AI-assisted — tool: Claude Code<!-- e.g., Claude Code, Cursor,
GitHub Copilot -->

---
<!-- DCO sign-off required by CI. Run: git config user.name && git
config user.email -->
Signed-off-by: Tinson Lai <tinsonl@nvidia.com>


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

* **Chores**
* Installer now runs the OpenShell CLI installation/upgrade during
initial setup so it’s available immediately instead of waiting for a
later onboarding step.
* OpenShell installation/upgrade is attempted on every install to ensure
the CLI is present and kept up to date.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
Co-authored-by: Claude <noreply@anthropic.com>
…uild (NVIDIA#2302)

## Summary

- `sandboxRebuild()` called `onboard({ resume: true })` without passing
the session's stored `fromDockerfile`, causing the conflict check to
reject the resume (`requestedFrom=null` vs
`recordedFrom="/path/to/Dockerfile"`)
- This made `nemoclaw <name> rebuild` and `nemoclaw upgrade-sandboxes`
fail unconditionally for any sandbox created with `--from`
- Fix reads the stored `fromDockerfile` from session metadata and passes
it through to `onboard()`

Fixes NVIDIA#2301

## Test plan

- [x] New test: rebuild does not hit fromDockerfile conflict when
session has a stored `--from` path
- [x] Existing NVIDIA#2201 regression tests still pass (agent syncing
unaffected)
- [x] All 3 rebuild tests pass

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

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

## Summary by CodeRabbit

* **Bug Fixes**
* Fixed rebuild operations to properly preserve and use the stored
Dockerfile source configuration, preventing errors when resuming
sessions.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
NVIDIA#2296)

## Summary

- Add `ws-proxy-fix.ts` preload script that patches `https.request()` to
detect WebSocket upgrade requests and inject a CONNECT tunnel agent,
fixing Discord gateway connections through the OpenShell L7 proxy
- Wire the preload into `nemoclaw-start.sh` (entrypoint + proxy-env.sh
persistence for connect sessions)
- Add blueprint TypeScript compilation infrastructure (`tsconfig.json`,
`build:blueprint` script)

Closes NVIDIA#1570

## Details

Node.js 22's `EnvHttpProxyAgent` (activated by `NODE_USE_ENV_PROXY=1`)
sends forward proxy requests instead of CONNECT tunnels for HTTPS
WebSocket upgrades. The OpenShell L7 proxy correctly rejects these with
HTTP 400, breaking the Discord gateway connection.

The preload intercepts `https.request()` calls that contain an `Upgrade:
websocket` header and replaces the default agent with one that
establishes a proper CONNECT tunnel through the proxy, then upgrades to
TLS. Non-WebSocket HTTPS requests pass through unchanged.

Belt-and-suspenders: if the caller (OpenClaw) already provides a custom
agent, the preload steps aside — no double-tunnelling. Works regardless
of any upstream OpenClaw fix.

## Test plan

- [x] `npx vitest run test/service-env.test.ts` — 39 tests pass (5 new)
- [x] `npm run build:blueprint` — clean TS compilation
- [x] `npm run typecheck:cli` — clean type-check
- [x] `npx prek run --all-files` — all relevant hooks pass (test-cli
failures are pre-existing environmental)
- [ ] E2E: deploy sandbox with Discord channel, verify gateway connects

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

* **New Features**
* Adds a preload that enables HTTPS-proxy CONNECT tunneling for Discord
gateway WebSocket upgrades when a proxy is configured
  * Start script now auto-applies the preload to launched sessions

* **Bug Fixes**
* Improved handling of WebSocket-upgrade requests over HTTPS proxies to
prevent connection failures and port duplication

* **Chores**
  * Blueprint compilation added to the CLI build

* **Tests**
* Added unit and e2e tests covering proxy injection and gateway
handshake flows
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
…lnerability (NVIDIA#2277) (NVIDIA#2297)

## Summary

Extract common entrypoint functions from `scripts/nemoclaw-start.sh`
(OpenClaw) and `agents/hermes/start.sh` (Hermes) into a shared shell
library (`scripts/lib/sandbox-init.sh`). This fixes an active Hermes
vulnerability (same class as NVIDIA#2181), prevents the two entrypoints from
drifting further apart, and establishes reusable primitives for future
agent types.

## Related Issue

Fixes NVIDIA#2277

## Changes

- **`scripts/lib/sandbox-init.sh`** (new): Shared library with security
primitives — `emit_sandbox_sourced_file()`,
`validate_tmp_permissions()`, `drop_capabilities()`,
`verify_config_integrity()`, `lock_rc_files()`, `cleanup_on_signal()`,
`validate_config_symlinks()`, `harden_config_symlinks()`,
`configure_messaging_channels()`
- **`agents/hermes/start.sh`**: Sources shared library. **Security
fix**: `.bashrc`/`.profile` now locked to 444 via `lock_rc_files()` (was
wide open). Proxy config now uses `emit_sandbox_sourced_file` (root:root
444) instead of inline append to writable `.bashrc`.
`validate_tmp_permissions()` gate added before service launch.
- **`scripts/nemoclaw-start.sh`**: Sources shared library. Replaced
inline capsh block, `verify_config_integrity()`, symlink validation,
messaging channels, cleanup, and proxy-env writing with shared versions.
Proxy-env.sh now written via `emit_sandbox_sourced_file` (444 instead of
644).
- **`test/sandbox-init.test.ts`** (new): 28 tests covering all shared
library functions independently
- **`test/nemoclaw-start.test.ts`**: Updated tests for shared
`cleanup_on_signal` pattern (replaces inline `cleanup()`)
- **`test/service-env.test.ts`**: Updated sed extraction anchors for
`emit_sandbox_sourced_file` pattern; added 444 permission assertion

### Hermes vulnerability fix summary

| Control | Before | After |
|---|---|---|
| `.bashrc`/`.profile` locked? | **No — wide open** | ✅ chmod 444 via
`lock_rc_files` |
| Proxy config | **Inline in writable .bashrc** | ✅ Standalone 444 file
via `emit_sandbox_sourced_file` |
| `/tmp` permission validation | **Missing** | ✅
`validate_tmp_permissions` before service launch |
| Future fixes auto-applied? | **No — manual port** | ✅ Both source same
library |

## Type of Change
- [x] Code change (feature, bug fix, or refactor)
- [ ] Code change with doc updates
- [ ] Doc only (prose changes, no code sample modifications)
- [ ] Doc only (includes code sample changes)

## Verification
- [x] `npx prek run --all-files` passes (shellcheck, shfmt, eslint,
prettier, gitleaks all pass; test runner has pre-existing
Docker-dependent failures on main)
- [x] `npm test` passes (sandbox-init 28/28, service-env 34/34,
nemoclaw-start 68/68; pre-existing install-preflight failures unrelated
to this PR)
- [x] Tests added or updated for new or changed behavior
- [x] No secrets, API keys, or credentials committed
- [ ] Docs updated for user-facing behavior changes
- [ ] `make docs` builds without warnings (doc changes only)
- [ ] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

## AI Disclosure
- [x] AI-assisted — tool: Claude Code (pi agent)

---
Signed-off-by: Julie Yaunches <jyaunches@nvidia.com>


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

* **Chores**
* Centralized and hardened sandbox startup logic with stricter /tmp and
config permission checks, symlink protections, capability-lowering,
improved signal/shutdown handling, and safer proxy env persistence.
* **Tests**
* Added comprehensive tests covering sandbox init routines, permission
enforcement, integrity checks, symlink hardening, capability behavior,
and signal/cleanup semantics.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Julie Yaunches <jyaunches@nvidia.com>
Co-authored-by: Carlos Villela <cvillela@nvidia.com>
Add "save it now — will not be printed again" to the tokenized dashboard
URL line in both onboard print paths (OpenClaw in onboard.ts, agent UI
in agent-onboard.ts). Treat the URL as a one-shot credential (GitHub-PAT
style) rather than exposing it through `nemoclaw status`, which would
widen incidental leakage via CI logs and issue-paste output.

Fixes NVIDIA#2167

<!-- markdownlint-disable MD041 -->
## Summary
<!-- 1-3 sentences: what this PR does and why. -->

## Related Issue
<!-- Fixes #NNN or Closes #NNN. Remove this section if none. -->

## Changes
<!-- Bullet list of key changes. -->

## Type of Change

- [x] Code change (feature, bug fix, or refactor)
- [ ] Code change with doc updates
- [ ] Doc only (prose changes, no code sample modifications)
- [ ] Doc only (includes code sample changes)

## Verification
<!-- Check each item you ran and confirmed. Leave unchecked items you
skipped. -->
- [x] `npx prek run --all-files` passes
- [x] `npm test` passes
- [x] Tests added or updated for new or changed behavior
- [x] No secrets, API keys, or credentials committed
- [ ] Docs updated for user-facing behavior changes
- [ ] `make docs` builds without warnings (doc changes only)
- [ ] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

## AI Disclosure
<!-- If an AI agent authored or co-authored this PR, check the box and
name the tool. Remove this section for fully human-authored PRs. -->
- [x] AI-assisted — tool: <!-- e.g., Claude Code, Cursor, GitHub Copilot
-->

---
<!-- DCO sign-off required by CI. Run: git config user.name && git
config user.email -->
Signed-off-by: Your Name <your-email@example.com>


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

## Summary by CodeRabbit

## Release Notes

* **Documentation**
* Improved guidance in onboarding console messages regarding tokenized
URLs. When users encounter the authentication token during the setup
process, they are now explicitly instructed to save it immediately and
clearly informed that the token will not be printed again in the future.
This applies to both agent and gateway UI setup flows.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: Paritosh Dixit <paritoshd@nvidia.com>
…DIA#380)

## Summary

- Always show an "Install Ollama" option during onboard on Linux (not
just macOS), so users have a local inference fallback when
`build.nvidia.com` is overloaded or down
- Use the official `curl -fsSL https://ollama.com/install.sh | sh`
installer on Linux (Homebrew on macOS)
- Add `install-ollama` as a valid `NEMOCLAW_PROVIDER` value for
non-interactive/CI mode

## Problem

When Ollama is not installed and the NVIDIA API server is unavailable,
`nemoclaw onboard` presents only the cloud option on Linux. Users can't
get an API key and onboarding fails with no fallback path (issue NVIDIA#301).

## Fix

The `install-ollama` option was already implemented for macOS but gated
behind `process.platform === "darwin"`. This PR extends it to Linux with
the appropriate installer command. The change is minimal — no
restructuring of the onboard flow.

Fixes NVIDIA#301

## Test plan

- [x] New test: verifies `Install Ollama (Linux)` option appears when
Ollama is not installed on Linux
- [x] New test: verifies the curl installer (not Homebrew) is invoked on
Linux
- [x] Existing `onboard-selection.test.js` tests still pass
- [x] All onboard/credentials/inference-config tests pass
- [x] `npm test` shows no new failures (pre-existing failures in
`runtime-shell.test.js` and `install-preflight.test.js` are unrelated)

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

## Release Notes

* **New Features**
* Added automatic Ollama installation option with platform-specific
methods: `brew install` on macOS and curl-based installation on Linux
* Enhanced provider selection with improved fallback behavior during
non-interactive setup
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: Josue Balandrano Coronel <josuebc@pm.me>

---------

Signed-off-by: Josue Gomez <josue@guatulab.com>
Signed-off-by: Julie Yaunches <jyaunches@nvidia.com>
Co-authored-by: futhgar <futhgar@users.noreply.github.com>
Co-authored-by: Test User <test@example.com>
Co-authored-by: Carlos Villela <cvillela@nvidia.com>
Co-authored-by: Aaron Erickson 🦞 <aerickson@nvidia.com>
…IA#2257)

<!-- markdownlint-disable MD041 -->
## Summary
Extends `test/e2e/test-token-rotation.sh` to cover Discord rotation
alongside Telegram (with provider-isolation checks), and makes the test
resilient to environmental install failures so corporate-VPN runs SKIP
cleanly instead of exiting 1 mid-Phase 0.

## Related Issue
Fixes NVIDIA#2247
Closes NVIDIA#2255

## Changes
**Test script (`test/e2e/test-token-rotation.sh`)**
- Phase 0 now seeds both `TELEGRAM_BOT_TOKEN` and `DISCORD_BOT_TOKEN`;
Phase 1 verifies both providers and both credential hashes are stored.
- Add Phase 4 (Discord rotation) and Phase 5 (re-onboard with same
tokens) mirroring the existing Telegram phases.
- Phase 2 (Telegram) and Phase 4 (Discord) each get a Negative
provider-isolation check — the rotation message must NOT name the
provider whose token didn't change.
- Add `SKIP` counter and `skip()` helper. When `install.sh` fails with
`(Telegram|Discord) network reachability failure` in the install log
(typical of VPN/proxy blocking `api.telegram.org`), record a SKIP, mark
Phases 1–5 as skipped, and still print the Summary instead of exiting 1.
- Per-phase onboard failures no longer hard-exit — Summary always runs.
- Add Discord prereq guards (`DISCORD_BOT_TOKEN_A`/`_B` set, A ≠ B);
SKIP cleanly if either is missing.
**CI (`.github/workflows/nightly-e2e.yaml`)**
- Wire `DISCORD_BOT_TOKEN_A` / `DISCORD_BOT_TOKEN_B` into the
`token-rotation-e2e` job so the new prereq guards do not skip the test
under nightly cron.
- Refresh the job comment to mention the combined Telegram + Discord
coverage.

## Type of Change
- [x] Code change (feature, bug fix, or refactor)

## Verification
- [ ] `npx prek run --all-files` passes
- [ ] `npm test` passes
- [ ] Tests added or updated for new or changed behavior
- [ ] No secrets, API keys, or credentials committed

---
<!-- DCO sign-off required by CI. Run: git config user.name && git
config user.email -->
Signed-off-by: Hung Le <hple@nvidia.com>


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

* **Documentation**
* Clarified test description to state per-provider (Telegram + Discord)
fake tokens and cross-talk assertions.

* **Tests**
* Extended end-to-end token-rotation tests to include Discord alongside
Telegram and added corresponding environment variables.
* Added prereq gating, skip counting and summary reporting for missing
tokens, install/network failures, or identical tokens.
* Strengthened post-install verification for both provider bridges and
credential registration.
* Implemented provider-isolated rotation phases and reuse assertions for
sandbox onboarding.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Bumps the pinned OpenShell version range from `0.0.29` → `0.0.32` so
fresh NemoClaw installs pick up sandbox hardening and TLS improvements
from the last three OpenShell releases.

## Notable upstream changes

**0.0.30**
([NVIDIA/OpenShell@v0.0.29...v0.0.30](NVIDIA/OpenShell@v0.0.29...v0.0.30))
- Network policy deny rules
([OpenShell#822](NVIDIA/OpenShell#822))
- Preserve ownership on existing `read_write` paths
([OpenShell#827](NVIDIA/OpenShell#827))
- Disable child core dumps
([OpenShell#821](NVIDIA/OpenShell#821))
- Escape control characters in SSE error formatting
([OpenShell#842](NVIDIA/OpenShell#842))
- Fix silent truncation of large streaming inference responses
([OpenShell#834](NVIDIA/OpenShell#834))

**0.0.31**
([NVIDIA/OpenShell@v0.0.30...v0.0.31](NVIDIA/OpenShell@v0.0.30...v0.0.31))
- Inference routed-request header allowlist
([OpenShell#826](NVIDIA/OpenShell#826))

**0.0.32**
([NVIDIA/OpenShell@v0.0.31...v0.0.32](NVIDIA/OpenShell@v0.0.31...v0.0.32))
- **Load system CA certificates for upstream TLS connections**
([OpenShell#862](NVIDIA/OpenShell#862))
- Publish standalone `openshell-gateway` binaries
([OpenShell#853](NVIDIA/OpenShell#853))

## Changes
- `nemoclaw-blueprint/blueprint.yaml`: `min_openshell_version` and
`max_openshell_version` → `0.0.32`
- `scripts/install-openshell.sh`: `MIN_VERSION` and `MAX_VERSION` →
`0.0.32` (`PIN_VERSION` follows `MAX`)
- `scripts/brev-launchable-ci-cpu.sh`: default `OPENSHELL_VERSION` →
`v0.0.32`
- `src/lib/onboard.ts`: blueprint-fallback min version → `0.0.32`
- `test/onboard.test.ts`,
`test/install-openshell-version-check.test.ts`: fixtures updated; "above
MAX" test case moved from `0.0.30` to `0.0.33`

Historical `m-dev` comments referencing `0.0.29` left in place — they
describe a self-report quirk the sidecar fallback still handles.

## Why not 0.0.33+?
`0.0.34` introduced incremental sandbox policy updates and L7
request-target canonicalization — changes with larger surface area
against how NemoClaw delivers policy via gRPC. Worth a follow-up PR
rather than bundling here. `0.0.35` released hours before this PR was
cut — too fresh.

## Type of Change
- [x] Code change for a new feature, bug fix, or refactor.

## Testing
- [x] `npx vitest run test/install-openshell-version-check.test.ts` — 9
passed
- [x] pre-commit hooks (prek) clean: shellcheck, commitlint, gitleaks,
YAML validator, CLI test suite
- [ ] Nightly E2E on this branch — will be kicked off after PR opens

## Notes
- No user-facing CLI behavior changes — just the pinned version range.
- Two pre-existing failures in `test/onboard.test.ts` reproduce on clean
`main` and are unrelated to this bump.

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

* **Chores**
* Updated OpenShell version constraints and default pinned version to
v0.0.32 across configuration, install, and onboarding flows.

* **Tests**
* Updated test fixtures and expectations to match the new OpenShell
version (v0.0.32).
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary
Add a maintainer skill for normalizing bracketed `NemoClaw` tags in
issue and PR titles. The skill provides a dry-run workflow, a reusable
TypeScript helper that matches tags case-insensitively anywhere in the
title, and verification guidance so maintainers can apply the cleanup
safely.

## Changes
- Add `.agents/skills/nemoclaw-maintainer-normalize-title-tags/SKILL.md`
with a dry-run-first workflow for previewing, applying, and verifying
title tag cleanup
- Add
`.agents/skills/nemoclaw-maintainer-normalize-title-tags/scripts/normalize-title-tags.ts`
to find and remove bracketed `nemoclaw` tags case-insensitively from
GitHub issue and PR titles
- Update `.agents/skills/nemoclaw-skills-guide/SKILL.md` to include the
new maintainer skill in the catalog

## Type of Change
- [x] Code change (feature, bug fix, or refactor)
- [ ] Code change with doc updates
- [ ] Doc only (prose changes, no code sample modifications)
- [ ] Doc only (includes code sample changes)

## Verification
- [x] `npx prek run --all-files` passes
- [ ] `npm test` passes
- [ ] Tests added or updated for new or changed behavior
- [x] No secrets, API keys, or credentials committed
- [ ] Docs updated for user-facing behavior changes
- [ ] `make docs` builds without warnings (doc changes only)
- [ ] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

## AI Disclosure
- [x] AI-assisted — tool: OpenAI Codex

---
Signed-off-by: Carlos Villela <cvillela@nvidia.com>

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

## Summary by CodeRabbit

* **New Features**
* Added a new maintainer skill to preview and bulk remove bracketed
title tags containing "NemoClaw" from GitHub issues and pull requests,
with dry-run preview and optional apply execution modes.

* **Documentation**
* Added comprehensive documentation for the new title-tag normalization
skill with step-by-step usage instructions and behavioral guidelines.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: Carlos Villela <cvillela@nvidia.com>
Co-authored-by: Aaron Erickson 🦞 <aerickson@nvidia.com>
## Summary
Catch up the user-facing docs for features and command renames merged
since v0.0.22, and bump the doc-site version strings to 0.0.23.

## Related Issue
<!-- Fixes #NNN or Closes #NNN. Remove this section if none. -->

## Changes
- `docs/reference/commands.md` — Documented `snapshot create --name`,
the new version-aware `snapshot list` table, and version/name/timestamp
selectors for `snapshot restore` (from NVIDIA#2184). Renamed `nemoclaw
start`/`stop` sections to `nemoclaw tunnel start`/`stop` with a
deprecation note on the legacy aliases, and added new `channels
start`/`channels stop` per-channel subsections (from NVIDIA#2103).
- `docs/workspace/backup-restore.md` — Added `--name` and `v<N>` / name
/ timestamp selector examples for the snapshot commands.
- `docs/deployment/set-up-telegram-bridge.md`,
`docs/deployment/deploy-to-remote-gpu.md`,
`docs/reference/troubleshooting.md` — Switched references from `nemoclaw
start` to `nemoclaw tunnel start`; noted `channels stop/start` as the
non-destructive way to pause a bridge.
- `docs/project.json`, `docs/versions1.json` — Bumped preferred doc
version to 0.0.23 and moved 0.0.22 into the version history list.
- `.agents/skills/nemoclaw-user-*` — Regenerated via
`scripts/docs-to-skills.py` so downstream skills match the doc sources.

## Type of Change
- [ ] Code change (feature, bug fix, or refactor)
- [ ] Code change with doc updates
- [ ] Doc only (prose changes, no code sample modifications)
- [x] Doc only (includes code sample changes)

## Verification
- [x] `npx prek run --all-files` passes
- [ ] `npm test` passes
- [ ] Tests added or updated for new or changed behavior
- [x] No secrets, API keys, or credentials committed
- [x] Docs updated for user-facing behavior changes
- [x] `make docs` builds without warnings (doc changes only)
- [x] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

Note on `npm test`: the CLI suite hit a pre-existing environmental
timeout in `test/cli.test.ts:60` (`bare unknown name surfaces
sandbox-not-found`) reaching a non-running local gateway. The plugin and
skills YAML suites pass. This PR touches only Markdown + JSON under
`docs/` and autogenerated `.agents/skills/` output, so the failing test
is unrelated.

## AI Disclosure
- [x] AI-assisted — tool: Claude Code

---
Signed-off-by: Miyoung Choi <miyoungc@nvidia.com>

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

## Summary by CodeRabbit

* **New Features**
* Added `channels stop/start` commands to pause and resume individual
messaging bridges without destroying sandboxes
* Introduced `--name` flag for creating labeled snapshots with improved
restore selection (by version, name, or timestamp)
  * Added `--dangerously-skip-permissions` flag to `nemoclaw onboard`
* Renamed host auxiliary commands to `nemoclaw tunnel start/stop`
(legacy aliases retained for backward compatibility)

* **Documentation**
* Expanded quickstart guide with recovery, reconfiguration, and
credential reset workflows
* Updated all documentation to reflect new command structure and bridge
lifecycle operations

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: Miyoung Choi <miyoungc@nvidia.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary

When a Gemini onboard validates with an expired API key, the provider
returns HTTP 400 with `"API key expired. Please renew the API key."`.
The current classifier checks `httpStatus === 400` first and returns `{
kind: "model", retry: "model" }`, so the expired-key case never reaches
the credential-message regex. In the Gemini validation flow (no
`allowModelRetry`), `kind: "model"` falls through to `unknown`, and the
onboard wizard loops back to provider selection without offering to
re-enter the key.

## Related Issue

Closes NVIDIA#1942

## Changes

- `src/lib/validation.ts` — move the credential-bearing message check
**before** the `httpStatus === 400 → model` default. Extend the
credential regex to include `api key expired` and `api[_ ]key[_
]invalid` so Gemini's `API_KEY_INVALID` status classification also lands
as `credential`.
- Inline comment documents why the order matters and cites NVIDIA#1942.
- HTTP 400 without a credential-bearing message still classifies as
`model` — regression guard test added.

## Testing

- [x] `src/lib/validation.test.ts` — 41 tests pass (+3 new: expired key,
API_KEY_INVALID, regression guard)
- [x] `src/lib/validation-recovery.test.ts` — 5 tests pass
- [x] Build + typecheck clean

Executed:
- Targeted `npx vitest run src/lib/validation.test.ts
src/lib/validation-recovery.test.ts` in the `nemoclaw-test` Docker
environment

## Prior art

Prior fix attempt [NVIDIA#1944](NVIDIA#1944)
was closed by its author on 2026-04-18 to reduce PR volume — the issue
remained open. This PR replays the same core reorder with a slightly
broader credential regex (covering both `API key expired` and
`API_KEY_INVALID` forms Gemini uses) and preserves the existing test
style.

## Checklist

- [x] Follows [Conventional
Commits](https://www.conventionalcommits.org/)
- [x] Commits are signed (SSH)
- [x] DCO Signed-off-by trailer present

Signed-off-by: latenighthackathon
<latenighthackathon@users.noreply.github.com>

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

* **Bug Fixes**
* Error classification now prioritizes credential-related messages so
API key/credential failures (expired, invalid, unauthorized) are
reported as credential issues rather than being misclassified as model
errors, improving clarity and retry guidance.

* **Tests**
* Added tests covering credential-related 400 responses and a regression
case to ensure unrelated model errors remain classified correctly.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: latenighthackathon <latenighthackathon@users.noreply.github.com>
Co-authored-by: latenighthackathon <latenighthackathon@users.noreply.github.com>
…1913)

## Problem

The `setup-jetson.sh` script used `sed` to patch
`/etc/docker/daemon.json` on JP6 devices. The sed command stripped the
trailing comma from `"default-runtime": "nvidia",`:

```
s/"default-runtime": "nvidia",/"default-runtime": "nvidia"/
```

This produced invalid JSON when `"runtimes"` was the next key:
```json
{
    "default-runtime": "nvidia"   ← missing comma here
    "runtimes": {
        "nvidia": { ... }
    }
}
```

Docker rejects malformed JSON and fails to start, causing `Job for
docker.service failed` errors.

## Fix

Replace the sed one-liner with a Python3 snippet that parses the JSON
properly, removes the `iptables` and `bridge` keys cleanly, and writes
back valid JSON. Python3 is available on all supported Jetson/Ubuntu
setups.

Fixes NVIDIA#1875

Signed-off-by: Benedikt Schackenberg
<6381261+BenediktSchackenberg@users.noreply.github.com>

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

* **Chores**
* Safer Jetson setup: Docker daemon configuration handling during
installation is now more robust and less likely to corrupt existing
files.
* Improved validation and error handling: installers attempt to repair
common formatting issues but will stop instead of overwriting unfixable
malformed files.
* Preserves original file permissions and uses atomic writes to ensure
safer updates.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Benedikt Schackenberg <6381261+BenediktSchackenberg@users.noreply.github.com>
Co-authored-by: Carlos Villela <cvillela@nvidia.com>
PR NVIDIA#2110's axios-only Module._load preload never fired at runtime:

  1. nemoclaw-blueprint/scripts/ is excluded from the optimized sandbox
     build context (src/lib/sandbox-build-context.ts), so
     axios-proxy-fix.js was not baked into the sandbox image.
  2. Adding scripts/ to the build context cache-busts the
     `COPY nemoclaw-blueprint/` Dockerfile layer and hangs npm ci in
     the k3s Docker-in-Docker build, so the delivery gap cannot be
     closed by expanding the context.
  3. Even if the file had reached the image, intercepting
     require('axios') via Module._load cannot patch follow-redirects +
     proxy-from-env bundled as ESM in OpenClaw's dist/http-Bh-HtMAg.js
     — there are no require() calls to intercept. The Bot Connector
     reply path uses the bundled code.

Replace with an http.request() wrapper — the lowest common denominator
every HTTP library bottoms out at. Detect FORWARD-mode requests
(hostname = proxy IP, path = full https:// URL) and rewrite them to
https.request() against the real target, letting NODE_USE_ENV_PROXY
handle the CONNECT tunnel correctly. Works for any HTTP client,
including bundled ESM that makes no require() calls.

Delivery:
  - nemoclaw-blueprint/scripts/http-proxy-fix.js — canonical source for
    review and tests.
  - scripts/nemoclaw-start.sh embeds the same JS inline via a heredoc,
    writes it to /tmp/nemoclaw-http-proxy-fix.js through
    emit_sandbox_sourced_file (root:root 444, symlink-safe), and loads
    it via NODE_OPTIONS=--require. No changes to sandbox-build-context.
  - test/http-proxy-fix-sync.test.ts enforces byte-for-byte equality
    between the heredoc and the canonical file, so future edits cannot
    silently diverge.
  - validate_tmp_permissions is invoked with the new path on both the
    root and non-root boot paths (the fix JS is a trust-boundary file
    — tampering would inject arbitrary code into every Node process
    via NODE_OPTIONS).

Because the content ships inside nemoclaw-start.sh rather than as a
separately-deployed file, the fix fires on the very first sandbox boot
with no post-onboard deploy + restart dance required.

Closes the NVIDIA#2109 regression class; removes the superseded
axios-proxy-fix.js and updates the existing regression tests in
service-env.test.ts to the new variable name (_PROXY_FIX_SCRIPT).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@lcsmontiel
lcsmontiel marked this pull request as ready for review April 23, 2026 02:59
@lcsmontiel
lcsmontiel merged commit f162482 into main Apr 23, 2026
18 of 19 checks passed
lcsmontiel pushed a commit that referenced this pull request May 19, 2026
…3456) (NVIDIA#3520)

> **Draft for visibility.** Issue-autopilot Stages 4-5 of NVIDIA#3456. Will
mark ready once batch self-review + CI complete.

## Summary

Closes the two remaining output threads in NVIDIA#3456 after the core
dead-loop fix already landed on `main` (via NVIDIA#3459, NVIDIA#3434, NVIDIA#3483). Full
sub-bug mapping in the [NVIDIA#3456 status
comment](NVIDIA#3456 (comment)).

- **Sub-bug #3** — `nemoclaw <name> destroy --yes` recovery hint
replaced with a registry-aware helper.
- **Sub-bug NVIDIA#4** — `Destroyed gateway 'nemoclaw' skipped`
self-contradictory wording replaced with `Gateway 'nemoclaw' already
removed or unreachable`.

## Acceptance criteria mapping

| Sub-bug | Resolution | Evidence |
|---|---|---|
| #1 dead loop | Already fixed on main (NVIDIA#3459) | out of scope |
| #2 firewall diagnostic | Already fixed on main (NVIDIA#3459) | out of scope
|
| **#3** literal `<name>` placeholder | **This PR** |
`src/lib/onboard/gpu-recovery.ts` + `onboard.ts:10387-10405` |
| **NVIDIA#4** misleading "skipped" wording | **This PR** |
`src/lib/actions/uninstall/run-plan.ts:210-228, 407-414` |
| NVIDIA#5 uninstall residuals | Already fixed on main (NVIDIA#3483) | out of scope
|

## Behavior matrix

`gpuPassthroughRecoveryLines(names)`:

| Input | Suggestion |
|---|---|
| `null` / `[]` | `nemoclaw uninstall && nemoclaw onboard --gpu` |
| one sandbox | `nemoclaw <name> destroy --yes --cleanup-gateway &&
nemoclaw onboard --gpu` |
| many sandboxes | each `destroy --yes`, only the last gets
`--cleanup-gateway` |

## Test plan

```
npm run typecheck:cli
npx vitest run src/lib/onboard/gpu-recovery.test.ts src/lib/actions/uninstall/run-plan.test.ts
```

22 tests pass (6 new + 16 existing).

## Notes for reviewers

- This is the work [NVIDIA#3464
attempted](NVIDIA#3464); that PR was
closed without merging after CodeRabbit asked for the `<name>`
placeholder to be forbidden in tests via negative assertion. This PR
adopts that refinement.
- `runOptional` extension is backwards-compatible — existing callers
without `onSkip` get the original wording.

Closes NVIDIA#3456 once merged.

---------

Signed-off-by: Charan Jagwani <charjags100@gmail.com>
Co-authored-by: Charan Jagwani <charjags100@gmail.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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

axios requests fail with ERR_BAD_RESPONSE inside NemoClaw sandbox — double proxy conflict with NODE_USE_ENV_PROXY