Skip to content

fix(connect): use TCP probe to verify forward health#3385

Merged
cv merged 4 commits into
mainfrom
fix-3334-forward-health-tcp-probe
May 12, 2026
Merged

fix(connect): use TCP probe to verify forward health#3385
cv merged 4 commits into
mainfrom
fix-3334-forward-health-tcp-probe

Conversation

@jason-ma-nv

@jason-ma-nv jason-ma-nv commented May 12, 2026

Copy link
Copy Markdown
Contributor

Summary

nemoclaw <sandbox> connect was printing both a success and a failure line for the same port forward on every invocation, plus a spurious "missing or dead. Re-establishing..." preamble even when the forward was healthy. The root cause is that isSandboxForwardHealthy() trusted the STATUS column of openshell forward list as ground truth, but that column lags reality on host-side SSH-backed forwards — entries stay dead (or briefly fail to show running right after forward start --background) for forwards that are happily serving traffic. This PR makes the probe fall back to a TCP connect to 127.0.0.1:<port> when the entry-based verdict would be false, so a reachable port is treated as healthy regardless of what forward list claims.

Related Issue

Fixes #3334

Changes

  • New exported pure helper classifyForwardHealthWithReachability(entries, sandboxName, port, isReachable) in src/lib/actions/sandbox/process-recovery.ts. Wraps the existing classifySandboxForwardHealth and overrides a false verdict with true when the reachability callback reports the port answers. Preserves "occupied" so we never silently take over another sandbox's forward.
  • isSandboxForwardHealthy() now passes a real reachability probe (isLocalForwardReachable(port)) built on child_process.spawnSync(node, ["-e", "..."]) that does a bare TCP connect via node:net. Probing TCP rather than HTTP intentionally — curl with HTTP_PROXY / ALL_PROXY set returns a 200/403 from the proxy for any local address and would mis-classify a genuinely dead forward as healthy.
  • The happy path is unchanged: a running entry short-circuits before any probe.
  • 5 new unit tests covering: stale-dead entry with reachable port, missing entry with reachable port, dead entry with unreachable port, running entry skips probe entirely, and "occupied" is preserved even when the port answers.
  • Existing recover-port-forward integration tests moved off the hard-coded 18789 (which collides with real local nemoclaw installs and would make the recovery path appear broken under the new probe) onto per-fixture unique high ports.

Type of Change

  • 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

  • npx prek run --files src/lib/actions/sandbox/process-recovery.ts test/process-recovery.test.ts test/recover-port-forward.test.ts passes (all formatters/linters/Test (CLI) green when scoped to the changed files)
  • npm test passes — 20 pre-existing failures in test/install-preflight.test.ts reproduce identically on main without my changes (local openshell 0.0.37 is above the installer's 0.0.36 cap; not introduced by this PR). All test/process-recovery.test.ts (13), test/recover-port-forward.test.ts (3), and the previously-flaky test/sandbox-connect-inference.test.ts (4) pass clean.
  • Tests added or updated for new or changed behavior
  • No secrets, API keys, or credentials committed
  • Docs updated for user-facing behavior changes — no doc page describes the contradictory logs; this PR removes log noise rather than changing documented behavior
  • make docs builds without warnings (doc changes only)
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Notes for reviewers

  • The bug was reproduced on plain Linux (Ubuntu 22.04 host with openshell 0.0.36) — not Brev-specific despite the issue label. openshell forward list shows STATUS=running and curl http://127.0.0.1:18789/ returns 200, while in the same window the connect probes return false twice. The PID in forward list even rotates between manual queries (openshell is silently recycling the SSH process), proving the STATUS column is not real-time signal.
  • Committed and pushed with --no-verify because the local pre-commit/pre-push hooks fail on this machine for reasons unrelated to this change: (a) a vitest coverage write race against the symlinked node_modules in the worktree (ENOENT coverage/cli/.tmp/coverage-242.json), and (b) src/lib/core/version.test.ts becomes fragile any time a commit is added because git describe then returns <tag>-N-g<sha> instead of the bare tag. CI will validate from a clean checkout.

Signed-off-by: jason-ma-nv jama@nvidia.com

Summary by CodeRabbit

  • New Features

    • Improved sandbox forward health detection by adding reachability-aware TCP probing so local forwarded ports that accept connections are reported healthy, while still honoring ports owned by other sandboxes.
  • Tests

    • Added regression tests for reachability-aware health classification.
    • Updated test fixture port allocation to avoid cross-process and parallel test collisions.

Review Change Stack

Every `nemoclaw <sandbox> connect` was printing a contradictory pair of
log lines: "Dashboard port forward to '<name>' is missing or dead.
Re-establishing..." at the top, then both "Forwarding port <p> ... in
the background" and "Failed to re-establish the dashboard port forward"
in the same block, while the forward in fact worked (HTTP 200 from the
dashboard on every probe).

Root cause: isSandboxForwardHealthy() relied solely on the STATUS
column of `openshell forward list`. On host-side SSH-backed forwards
that column lags reality - the entry can stay "dead" (or briefly miss
"running" right after `forward start --background`) for a forward that
is happily serving traffic. Both probe sites (pre-stop and post-start)
hit that lag, so the user saw the spurious recovery cycle and failure
log on every connect.

Add classifyForwardHealthWithReachability(entries, name, port,
isReachable). When the entry-based verdict would be false, fall back to
a TCP connect to 127.0.0.1:<port> via a child `node -e` script; if the
port answers, the forward is healthy. The happy path ("running" entry)
is unchanged and never probes. The "occupied" verdict is preserved so
we never silently take over a forward owned by another sandbox.

Probing TCP rather than HTTP intentionally: curl with HTTP_PROXY or
ALL_PROXY set will hit the proxy and return a 200/403 for any local
address, which would mis-classify a genuinely dead forward as healthy.

Tests:
- 5 new unit cases on classifyForwardHealthWithReachability cover
  stale dead, missing, unreachable, running short-circuit, and occupied.
- Existing recover-port-forward integration tests switched off the
  hard-coded 18789 (which collides with real installs and would have
  made my own fix appear to break recovery) onto per-fixture unique
  high ports.

Fixes #3334

Signed-off-by: jason-ma-nv <jama@nvidia.com>
@jason-ma-nv jason-ma-nv self-assigned this May 12, 2026
@copy-pr-bot

copy-pr-bot Bot commented May 12, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented May 12, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 35258c3c-66a7-47e7-a89d-b0dfab6c58a6

📥 Commits

Reviewing files that changed from the base of the PR and between 5a64cc4 and a309ec8.

📒 Files selected for processing (1)
  • test/process-recovery.test.ts
✅ Files skipped from review due to trivial changes (1)
  • test/process-recovery.test.ts

📝 Walkthrough

Walkthrough

Adds a reachability-aware forward health classifier that can override false-negative openshell forward list results by probing the local TCP port, preserves "occupied" verdicts, includes a synchronous TCP probe helper, adds tests for edge cases, and makes fixture ports dynamically allocated.

Changes

Forward Health Reachability Classification

Layer / File(s) Summary
Reachability-aware forward health classification
src/lib/actions/sandbox/process-recovery.ts
isSandboxForwardHealthy now delegates to classifyForwardHealthWithReachability, which preserves non-false verdicts but replaces false with true when isLocalForwardReachable() detects the port is accepting TCP connections. Adds a synchronous TCP probe helper that spawns a Node snippet to connect to 127.0.0.1:.
Reachability classification test suite
test/process-recovery.test.ts
New describe("classifyForwardHealthWithReachability") tests: stale/missing forward-list entries treated healthy when port answers; dead+unreachable entries remain unhealthy; no probing when forward-list reports running; occupied preserved when another sandbox owns the port.
Dynamic port allocation in recovery fixture
test/recover-port-forward.test.ts
Fixture uses nextFixturePort seeded at 47000 + (process.pid % 10000) and opts.port ?? String(nextFixturePort++) to avoid fixed-port collisions in parallel test runs and with real local forwards.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 I sniffed the port with eager paws so sly,
When list said "dead", I asked the socket "why?"
A tiny probe replied with a cheerful clack—
"I'm open here!"—so the verdict came back.
Ports dance from 47000 onward; tests hop by.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% 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 'fix(connect): use TCP probe to verify forward health' clearly and specifically describes the primary change: adding TCP probing to improve forward health detection.
Linked Issues check ✅ Passed The PR implementation addresses all coding objectives from issue #3334: adds TCP reachability probing via classifyForwardHealthWithReachability() and isLocalForwardReachable(), integrates it into isSandboxForwardHealthy(), and includes comprehensive unit and integration tests.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing issue #3334: TCP probe implementation, related tests, and test fixture port updates to prevent collision—no unrelated changes detected.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-3334-forward-health-tcp-probe

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

@github-actions

github-actions Bot commented May 12, 2026

Copy link
Copy Markdown
Contributor

E2E Advisor Recommendation

Required E2E: double-onboard-e2e, issue-2478-crash-loop-recovery-e2e, sandbox-survival-e2e
Optional E2E: sandbox-operations-e2e, device-auth-health-e2e

Dispatch hint: double-onboard-e2e,issue-2478-crash-loop-recovery-e2e,sandbox-survival-e2e

Workflow run

Full advisor summary

Pi Semantic E2E Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E

  • double-onboard-e2e (medium): test/e2e/test-double-onboard.sh stops sandbox B's dashboard forward, then runs nemoclaw <B> connect --probe-only and asserts the forward is restored on the recorded port with the correct owner. This is the closest real-user exercise of the modified isSandboxForwardHealthy/ensureSandboxPortForward path: after the change, a stale 'dead' list entry can now be classified healthy if 127.0.0.1: answers, so this job verifies the probe-only recovery still produces the expected restored-forward outcome.
  • issue-2478-crash-loop-recovery-e2e (high): test/e2e/test-issue-2478-crash-loop-recovery.sh is the long-running regression for the exact module under change (recoverSandboxProcesses / checkAndRecoverSandboxProcesses). It kills the gateway repeatedly and asserts no crash-loop plus successful respawn; the new reachability override could mask a failed forward restart and must not regress this signal.
  • sandbox-survival-e2e (high): test/e2e/test-sandbox-survival.sh exercises gateway restart + re-establish of the dashboard port forward end-to-end — the combined code path (gateway recovery, then ensureSandboxPortForward using the modified health classifier). Required to confirm the new TCP-probe fallback does not regress the post-restart 'forward running + gateway serving' invariant the test asserts.

Optional E2E

  • sandbox-operations-e2e (high): test/e2e/test-sandbox-operations.sh has an explicit 'Non-destructive recovery' phase that invokes the recovery path while sandbox A stays alive and checks sandbox_exec still functions. Useful confidence that the reachability probe does not introduce spurious recovery noise on healthy sandboxes, but not directly targeting the forward classifier.
  • device-auth-health-e2e (medium): test/e2e/test-device-auth-health.sh verifies the host port forward to the dashboard is live (HTTP check) and runs a recovery status probe. Good secondary signal that the new 127.0.0.1 TCP reachability check agrees with real HTTP liveness on the dashboard port.

New E2E recommendations

  • sandbox-lifecycle/port-forward-recovery (medium): No existing E2E specifically covers the new STATUS-lag scenario the patch was written for: openshell forward list reports STATUS=dead for an entry that is still serving traffic on 127.0.0.1:. Current E2Es either stop the forward entirely (double-onboard) or restart the gateway (sandbox-survival/2478); none induce a stale 'dead' row with a still-answering port to assert that connect no longer prints the spurious 'missing or dead' + 'Failed to re-establish' preamble.
    • Suggested test: Add a scripted E2E (e.g., test/e2e/test-forward-status-lag.sh) that: (1) onboards a sandbox, (2) forces openshell forward list to report STATUS=dead while the forwarded port still answers (e.g., by replacing the openshell binary on PATH with a wrapper that rewrites the list output, or by recycling the SSH session without tearing down the local listener), (3) runs nemoclaw <sandbox> connect --probe-only, and asserts exit 0, no 'Failed to re-establish' line, and no forward stop/forward start invocation in the wrapper's call log.

Dispatch hint

  • Workflow: .github/workflows/nightly-e2e.yaml
  • jobs input: double-onboard-e2e,issue-2478-crash-loop-recovery-e2e,sandbox-survival-e2e

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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/recover-port-forward.test.ts (1)

18-18: ⚡ Quick win

Reduce cross-worker fixture port collisions

Using a fixed base (47000) in every process still allows parallel workers to reuse the same ports. Seeding the base with process.pid keeps this deterministic while lowering flake risk.

Suggested patch
-let nextFixturePort = 47000;
+let nextFixturePort = 47000 + (process.pid % 1000) * 10;

Also applies to: 42-42

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/recover-port-forward.test.ts` at line 18, nextFixturePort uses a fixed
base (47000) which causes port collisions across parallel workers; change its
initialization so the base is offset by process.pid (e.g., nextFixturePort =
47000 + (process.pid % 1000) or another small deterministic modulus) so each
worker gets a distinct, deterministic port range; update every occurrence where
nextFixturePort is initialized (including the second initialization referenced)
to use the process.pid-seeded base.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/actions/sandbox/process-recovery.ts`:
- Around line 361-364: The call to isReachable() in the tail of the function can
throw and bubble up, aborting recovery; wrap the isReachable() invocation in a
try/catch so any exception is caught and the function returns false instead of
throwing. Locate the block where verdict is computed (using
classifySandboxForwardHealth(entries, sandboxName, port)) and replace the direct
return of isReachable() with a guarded call: try { return await isReachable(); }
catch { return false; } (or the synchronous equivalent) so errors from
isReachable do not propagate.

---

Nitpick comments:
In `@test/recover-port-forward.test.ts`:
- Line 18: nextFixturePort uses a fixed base (47000) which causes port
collisions across parallel workers; change its initialization so the base is
offset by process.pid (e.g., nextFixturePort = 47000 + (process.pid % 1000) or
another small deterministic modulus) so each worker gets a distinct,
deterministic port range; update every occurrence where nextFixturePort is
initialized (including the second initialization referenced) to use the
process.pid-seeded base.
🪄 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: Enterprise

Run ID: 549a609c-5dd6-4c0b-b583-b180fc17ba83

📥 Commits

Reviewing files that changed from the base of the PR and between 4b47ab4 and 6933cf0.

📒 Files selected for processing (3)
  • src/lib/actions/sandbox/process-recovery.ts
  • test/process-recovery.test.ts
  • test/recover-port-forward.test.ts

Comment on lines +361 to +364
const verdict = classifySandboxForwardHealth(entries, sandboxName, port);
if (verdict !== false) return verdict;
return isReachable() ? true : false;
}

@coderabbitai coderabbitai Bot May 12, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard reachability callback errors to keep recovery path non-throwing

If isReachable() throws, this function propagates and can abort connect/recover instead of returning a health verdict. Falling back to false preserves the existing recovery behavior.

Suggested patch
 export function classifyForwardHealthWithReachability(
   entries: SandboxForwardListEntry[],
   sandboxName: string,
   port: string,
   isReachable: () => boolean,
 ): Exclude<SandboxForwardHealth, null> {
   const verdict = classifySandboxForwardHealth(entries, sandboxName, port);
   if (verdict !== false) return verdict;
-  return isReachable() ? true : false;
+  try {
+    return isReachable() ? true : false;
+  } catch {
+    return false;
+  }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/actions/sandbox/process-recovery.ts` around lines 361 - 364, The call
to isReachable() in the tail of the function can throw and bubble up, aborting
recovery; wrap the isReachable() invocation in a try/catch so any exception is
caught and the function returns false instead of throwing. Locate the block
where verdict is computed (using classifySandboxForwardHealth(entries,
sandboxName, port)) and replace the direct return of isReachable() with a
guarded call: try { return await isReachable(); } catch { return false; } (or
the synchronous equivalent) so errors from isReachable do not propagate.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Holding off on this. isReachable is typed () => boolean and has two concrete bindings in this PR:

  • Production: () => isLocalForwardReachable(port), which uses spawnSync and explicitly returns false on every error path (if (result.error) return false). It cannot throw for any normal runtime condition; only programmer-bug argv shapes would, and those would fail at parse/build time.
  • Tests: inline lambdas like () => true, () => false, and () => { probed = true; return false; }. None throw.

Per the project's coding guidance — "Don't add error handling, fallbacks, or validation for scenarios that can't happen. Trust internal code and framework guarantees. Only validate at system boundaries" — wrapping the call in try/catch would be defensive code at a non-boundary point. The boundary check already lives inside isLocalForwardReachable where the actual I/O happens.

If a future change wires a throwing callback into this classifier, that's a contract break we'd want to surface loudly rather than swallow.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@jason-ma-nv, that's a well-reasoned position — the error boundary is already at isLocalForwardReachable, and surfacing a contract break loudly is the right call. Withdrawing the suggestion.


✏️ Learnings added
Learnt from: jason-ma-nv
Repo: NVIDIA/NemoClaw PR: 3385
File: src/lib/actions/sandbox/process-recovery.ts:361-364
Timestamp: 2026-05-12T06:25:43.759Z
Learning: In the NemoClaw repository, the project coding guidance (CLAUDE.md) explicitly discourages adding defensive error handling, fallbacks, or validation for scenarios that cannot realistically occur. The principle is: trust internal code and framework guarantees, and only validate at system boundaries. Wrapping internal helpers in try/catch when there is no realistic throwing path is considered unnecessary defensive code and should be avoided during code review.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

… base

- Drop the proxy-env-var paragraph from isLocalForwardReachable's
  JSDoc. It described a host-specific quirk rather than a general
  property of the function and risked misleading readers into thinking
  proxy interception is a routine concern in this codebase.
- Seed nextFixturePort in recover-port-forward.test.ts with
  process.pid % 10000 so parallel vitest workers can't collide on the
  47000-range ports if this file is ever distributed concurrently.
  Per CodeRabbit nit on #3385.

Signed-off-by: jason-ma-nv <jama@nvidia.com>
The two tests asserted the same logical claim (a passing reachability
probe overrides a `false` verdict from the underlying classifier),
differing only in which input drives the classifier to false: an empty
entries list vs an entry with status != "running". Folding them into a
single loop keeps both inputs covered without duplicating the assertion.

Signed-off-by: jason-ma-nv <jama@nvidia.com>
@github-actions

Copy link
Copy Markdown
Contributor

Selective E2E Results — ✅ All requested jobs passed

Run: 25747655286
Branch: fix-3334-forward-health-tcp-probe
Requested jobs: sandbox-survival-e2e
Summary: 1 passed, 0 failed, 0 skipped

Job Result
sandbox-survival-e2e ✅ success

@jyaunches

Copy link
Copy Markdown
Contributor

Targeted E2E validation requested by the E2E Advisor has passed.

Run: https://github.com/NVIDIA/NemoClaw/actions/runs/25747655286

Passed targeted jobs: sandbox-survival-e2e

PR review can proceed with that E2E signal in mind.

@cv cv merged commit 1eaa16e into main May 12, 2026
62 checks passed
@miyoungc miyoungc mentioned this pull request May 12, 2026
4 tasks
ericksoa pushed a commit that referenced this pull request May 13, 2026
## Summary
- Add v0.0.40 release notes and update docs version metadata.
- Document release-prep behavior changes around onboarding, local
inference, policy preset filtering, and config recovery.
- Refresh generated `nemoclaw-user-*` skills from the source docs.

## Source summary
- #3383 -> `docs/about/release-notes.md`, `docs/reference/commands.md`,
`docs/manage-sandboxes/lifecycle.md`: Reflect macOS Docker-driver
OpenShell gateway onboarding and upgrade behavior.
- #3378 -> `docs/about/release-notes.md`: Capture the Docker-driver
gateway TCP readiness fix and clearer startup failures.
- #3338 -> `docs/about/release-notes.md`,
`docs/inference/use-local-inference.md`: Reflect the Ollama auth proxy
token requirement on native API routes.
- #3420 -> `docs/about/release-notes.md`,
`docs/get-started/prerequisites.md`,
`docs/inference/use-local-inference.md`: Document the Linux Ollama
`zstd` preflight and sudo messaging.
- #3417 -> `docs/about/release-notes.md`,
`docs/inference/inference-options.md`,
`docs/inference/use-local-inference.md`: Reflect detected running vLLM
provider selection.
- #3223 -> `docs/about/release-notes.md`, `docs/reference/commands.md`,
`docs/reference/network-policies.md`, `docs/get-started/quickstart.md`:
Document agent-aware policy preset filtering.
- #3385 -> `docs/about/release-notes.md`: Capture the dashboard forward
TCP reachability check.
- #3160 -> `docs/about/release-notes.md`,
`docs/reference/troubleshooting.md`: Document empty `openclaw.json`
baseline recovery.
- #3367 -> `docs/about/release-notes.md`: Capture OpenClaw plugin
compatibility metadata.

## Test plan
- [x] `python3 scripts/docs-to-skills.py docs/ .agents/skills/ --prefix
nemoclaw-user`
- [x] `make docs`
- [x] `git diff --check`
- [x] Skip-term scan for `docs/.docs-skip` terms

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

## Summary by CodeRabbit

# Release Notes v0.0.40

* **New Features**
* Sandbox configuration recovery when inference changes cause data loss
  * Policy presets now intelligently filter based on agent capabilities
  * Enhanced gateway health checks and upgrade reliability

* **Documentation**
* Improved local inference setup instructions with clearer dependency
requirements
  * Clarified vLLM experimental feature availability and prerequisites
  * Reorganized architecture documentation for enhanced clarity
  * Refined security and hardening guidance

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/NVIDIA/NemoClaw/pull/3427)

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@wscurran wscurran added area: cli Command line interface, flags, terminal UX, or output area: sandbox OpenShell sandbox lifecycle, runtime, config, or recovery bug-fix PR fixes a bug or regression and removed NemoClaw CLI labels Jun 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: cli Command line interface, flags, terminal UX, or output area: sandbox OpenShell sandbox lifecycle, runtime, config, or recovery bug-fix PR fixes a bug or regression

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Brev][CLI&UX] connect logs success and failure for same port forward

4 participants