Skip to content

Fix doctor and backup checks for local gateway runtimes - #4583

Closed
Christoffer91 wants to merge 2 commits into
NVIDIA:mainfrom
Christoffer91:codex/nemoclaw-doctor-backup-fixes
Closed

Fix doctor and backup checks for local gateway runtimes#4583
Christoffer91 wants to merge 2 commits into
NVIDIA:mainfrom
Christoffer91:codex/nemoclaw-doctor-backup-fixes

Conversation

@Christoffer91

@Christoffer91 Christoffer91 commented May 31, 2026

Copy link
Copy Markdown

Summary

  • allow nemoclaw doctor to accept a running local openshell-gateway process when the legacy openshell-cluster-nemoclaw Docker container is not present
  • increase the tar -tvf hard-link validation buffer for large sandbox state backups
  • add a regression test for large hard-link validation archives that exceed Node default spawn output buffering

Why

On a Linux Docker-driver install using a local OpenShell gateway process, nemoclaw xterm doctor reported a gateway Docker-container failure even though OpenShell status, sandbox exec, inference, and local services were healthy. The check was hardcoded around openshell-cluster-nemoclaw.

Large OpenClaw sandbox state also caused nemoclaw backup-all to fail during hard-link validation because tar -tvf - produced more stdout than Node’s default spawnSync buffer. The tar archive itself was valid and backup succeeded after increasing the buffer.

Validation

  • npm run build:cli
  • npm test -- test/security-sandbox-tar-traversal.test.ts
  • local runtime verification: nemoclaw xterm doctor --json returned status=ok failed=0 warnings=0 after applying the equivalent patch

Summary by CodeRabbit

  • Bug Fixes

    • More resilient gateway detection: added a local-process fallback to reduce false failure reports when standard checks fail.
    • Prevented failures when processing very large tar archives by increasing the output buffer for archive listing operations.
  • Tests

    • Added a regression test ensuring large tar archives are handled without buffer-related failures.

@copy-pr-bot

copy-pr-bot Bot commented May 31, 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 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

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: 4304a885-5847-4112-b44b-0b8ffc64e527

📥 Commits

Reviewing files that changed from the base of the PR and between 8567021 and ee96a76.

📒 Files selected for processing (1)
  • src/lib/actions/sandbox/doctor.ts
💤 Files with no reviewable changes (1)
  • src/lib/actions/sandbox/doctor.ts

📝 Walkthrough

Walkthrough

Adds a host-level gateway detection fallback when docker inspect fails, and raises spawnSync maxBuffer to 256MB for tar listing operations; includes a regression test exercising large (20,000-entry) tar archives.

Changes

Sandbox Operation Resilience

Layer / File(s) Summary
Docker Gateway Fallback Resilience
src/lib/actions/sandbox/doctor.ts
When docker inspect fails, the gateway doctor now attempts host-level recovery by checking if the openshell-gateway process is running and the gateway port is listening via pgrep and ss, returning early with an ok status if both checks pass.
Tar Buffer Limits for Large Archives
src/lib/state/sandbox.ts, test/security-sandbox-tar-traversal.test.ts
validateTarEntries() and rejectHardLinks() now use a 256MB maxBuffer for tar listing operations, and a regression test ensures large tar archives (20,000 entries) are processed without buffer overflow.

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly Related PRs

Suggested Labels

NemoClaw CLI, Sandbox, Docker

Suggested Reviewers

  • prekshivyas
  • cv

Poem

🐰 In shells and tarballs I hop and peep,
I listen where gateways wake from sleep.
If Docker hides, I scan the host ground,
And give tar more room so entries abound.
Hooray — small fixes, big bounds, bounce and leap!

🚥 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 accurately describes the main change: fixing doctor and backup checks to support local gateway runtimes, which aligns with all three key modifications (doctor check enhancement, buffer increase for tar validation, and regression test).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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.

@wscurran

wscurran commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

✨ Thanks for submitting this detailed PR about fixing doctor and backup checks for local gateway runtimes. This proposes a way to improve the NemoClaw CLI's compatibility with local OpenShell gateway processes and enhance the backup validation for large sandbox state archives.

1 similar comment
@wscurran

wscurran commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

✨ Thanks for submitting this detailed PR about fixing doctor and backup checks for local gateway runtimes. This proposes a way to improve the NemoClaw CLI's compatibility with local OpenShell gateway processes and enhance the backup validation for large sandbox state archives.

@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
@Christoffer91
Christoffer91 force-pushed the codex/nemoclaw-doctor-backup-fixes branch from 20b906e to 8567021 Compare June 3, 2026 10:46
@Christoffer91
Christoffer91 marked this pull request as ready for review June 3, 2026 10:46
Copilot AI review requested due to automatic review settings June 3, 2026 10:46

Copilot AI 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.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR improves sandbox tar validation robustness for large archives and enhances the sandbox “doctor” gateway diagnostics when Docker inspection fails.

Changes:

  • Increase spawnSync output buffer for tar listing commands to handle very large verbose output.
  • Add a regression test ensuring rejectHardLinks can process large archives without hitting Node’s default spawn buffer limit.
  • In doctor, detect a locally running openshell-gateway (process + listening port) when Docker container inspection fails.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
test/security-sandbox-tar-traversal.test.ts Adds a regression test for large-archive verbose listing behavior.
src/lib/state/sandbox.ts Raises spawnSync maxBuffer for tar listing output in validation and hard-link rejection.
src/lib/actions/sandbox/doctor.ts Adds fallback checks for a local gateway process/port when docker inspect fails.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/lib/state/sandbox.ts
encoding: "utf-8",
stdio: ["pipe", "pipe", "pipe"],
timeout: 60000,
maxBuffer: 256 * 1024 * 1024,
Comment thread src/lib/state/sandbox.ts
encoding: "utf-8",
stdio: ["pipe", "pipe", "pipe"],
timeout: 60000,
maxBuffer: 256 * 1024 * 1024,
Comment on lines +586 to +596
it("accepts a large archive whose verbose listing exceeds Node's default spawn buffer", async () => {
const { rejectHardLinks } = await loadSandboxState();
const entries = Array.from({ length: 20_000 }, (_, index) => ({
path: `workspace/file-${index.toString().padStart(5, "0")}.txt`,
content: "x",
}));

const violations = rejectHardLinks(buildTar(entries));

expect(violations).toEqual([]);
});
Comment on lines +204 to +216
const processCheck = captureHostCommand("pgrep", ["-af", "openshell-gateway"], 5000);
const portCheck = captureHostCommand("ss", ["-ltn", `( sport = :${GATEWAY_PORT} )`], 5000);
const processRunning = processCheck.status === 0 && processCheck.stdout.trim().length > 0;
const portListening = portCheck.status === 0 && portCheck.stdout.includes(`:${GATEWAY_PORT}`);
if (processRunning && portListening) {
checks.push({
group: "Gateway",
label: "Local gateway process",
status: "ok",
detail: `openshell-gateway is running and listening on port ${GATEWAY_PORT}`,
});
return checks;
}

@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

🤖 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/doctor.ts`:
- Line 204: The fallback in dockerInspectGateway is using
captureHostCommand("pgrep", ["-af", "openshell-gateway"], 5000) which can
produce false positives; replace the literal "openshell-gateway" argv with the
anchored HOST_GATEWAY_PGREP_PATTERN imported from
src/lib/onboard/host-gateway-process.ts and call captureHostCommand("pgrep",
["-f", HOST_GATEWAY_PGREP_PATTERN], 5000) so pgrep runs with the proper anchored
pattern; keep the existing timeout and ensure any variable name references
(e.g., processCheck) are preserved.
🪄 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: a3b47909-ef2f-4473-8a16-d45ac84dba47

📥 Commits

Reviewing files that changed from the base of the PR and between 325ed77 and 8567021.

📒 Files selected for processing (3)
  • src/lib/actions/sandbox/doctor.ts
  • src/lib/state/sandbox.ts
  • test/security-sandbox-tar-traversal.test.ts

5000,
);
if (inspect.status !== 0) {
const processCheck = captureHostCommand("pgrep", ["-af", "openshell-gateway"], 5000);

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Simulate a false positive scenario where a user is editing the gateway binary
# but the gateway itself is not running.

# Check if the bare substring pattern would match unrelated processes
echo "Testing bare substring pattern (current code):"
ps aux | grep -i openshell-gateway | grep -v grep || echo "No matches"

echo ""
echo "The anchored pattern from HOST_GATEWAY_PGREP_PATTERN should be used instead:"
cat src/lib/onboard/host-gateway-process.ts | grep -A2 "HOST_GATEWAY_PGREP_PATTERN"

Repository: NVIDIA/NemoClaw

Length of output: 618


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Inspect doctor.ts around the reported lines
echo "=== doctor.ts around lines 180-240 ==="
nl -ba src/lib/actions/sandbox/doctor.ts | sed -n '180,240p'

# 2) Inspect host-gateway-process.ts for the anchored pattern definition
echo
echo "=== host-gateway-process.ts HOST_GATEWAY_PGREP_PATTERN definition ==="
rg -n "HOST_GATEWAY_PGREP_PATTERN" src/lib/onboard/host-gateway-process.ts
nl -ba src/lib/onboard/host-gateway-process.ts | sed -n '1,120p'

# 3) Check what ss usage looks like elsewhere in the repo (to judge syntax expectations)
echo
echo "=== repo-wide 'ss -ltn' occurrences (trimmed) ==="
rg -n "ss -ltn" -S src | head -n 50

Repository: NVIDIA/NemoClaw

Length of output: 159


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Show doctor.ts around the target area (no nl available)
echo "=== src/lib/actions/sandbox/doctor.ts lines 180-240 (approx) ==="
sed -n '180,240p' src/lib/actions/sandbox/doctor.ts

# 2) Locate and show HOST_GATEWAY_PGREP_PATTERN definition
echo
echo "=== src/lib/onboard/host-gateway-process.ts: HOST_GATEWAY_PGREP_PATTERN ==="
rg -n "HOST_GATEWAY_PGREP_PATTERN" src/lib/onboard/host-gateway-process.ts
sed -n '1,120p' src/lib/onboard/host-gateway-process.ts

# 3) Find how doctor.ts is currently searching for the gateway process
echo
echo "=== doctor.ts: pgrep occurrences ==="
rg -n "pgrep" src/lib/actions/sandbox/doctor.ts

# 4) Find how 'ss -ltn' is used in the repo (syntax comparison)
echo
echo "=== repo: ss -ltn occurrences (first 80 matches total) ==="
rg -n "ss -ltn" src | head -n 80

Repository: NVIDIA/NemoClaw

Length of output: 7162


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== src/lib/actions/sandbox/doctor.ts lines 140-230 ==="
sed -n '140,230p' src/lib/actions/sandbox/doctor.ts

echo
echo "=== Search for other ss invocations / sport filters ==="
rg -n "ss\\b|sport\\s*=\\s*:\\$\\{?GATEWAY_PORT\\}?|sport\\s*=\\s*:" src | head -n 80

Repository: NVIDIA/NemoClaw

Length of output: 10194


🌐 Web query:

ss -ltn filter syntax "( sport = :PORT )" "sport = :PORT" examples

💡 Result:

The ss command uses a filter expression syntax that supports comparing source (sport) and destination (dport) ports against specific values [1][2]. Because these characters (like parentheses, greater-than/less-than signs, or pipes) are often interpreted by the shell, they must typically be quoted or escaped when used in a command line [3][4]. Filter Syntax The general syntax for port filtering is: {dport|sport} [OP] [FAMILY:]:PORT - [OP]: The operator, such as "=", "<", "<=", "!=", ">=", or ">" [1][2]. If no operator is provided, "=" is assumed [1][2]. - [FAMILY:]: Optional, typically inet or inet6. -:PORT: The port number or name (e.g.,:80 or:http) [1][2]. Key Usage Notes - Shell Escaping: When using operators like ">", "<", or groupings like "", you must wrap the expression in quotes or escape the characters to prevent the shell from interpreting them [3][4]. For example, use '( sport =:80)' or ( sport =:80 ). - Implicit AND: If multiple predicates are provided without a boolean operator, an implicit AND is assumed [1][2]. - Boolean Operators: You can use "or" (|/||), "and" (&/&&), and "not" (!) to combine filters [1][2]. Examples - Filter by a specific port: ss sport =:80 ss 'sport =:80' - Filter with operators (requires quoting/escaping): ss 'sport >:1024' ss 'dport <=:5000' - Complex filters (grouping and boolean logic): ss -ltn '( sport =:80 or sport =:443)' ss -ltn 'state established ( dport =:ssh or sport =:ssh)' ss -ltn 'sport >=:1024 and sport <=:5000' The syntax "( sport =:PORT)" serves to group subexpressions, which is essential when combining multiple conditions with "or" to ensure correct precedence [1][2].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find captureHostCommand implementation
rg -n "function captureHostCommand|captureHostCommand\\(" src/lib/actions/sandbox/doctor.ts

# Print the relevant section (top of file)
sed -n '1,140p' src/lib/actions/sandbox/doctor.ts

# Print captureHostCommand section if it's elsewhere in same file
# (fallback: print around first occurrence line)
line=$(rg -n "function captureHostCommand" src/lib/actions/sandbox/doctor.ts | cut -d: -f1 | head -n1 || true)
if [ -n "${line:-}" ]; then
  start=$((line-20))
  end=$((line+80))
  sed -n "${start},${end}p" src/lib/actions/sandbox/doctor.ts
fi

Repository: NVIDIA/NemoClaw

Length of output: 7856


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show imports at top of doctor.ts to see if it can/does use HOST_GATEWAY_PGREP_PATTERN
sed -n '1,80p' src/lib/actions/sandbox/doctor.ts

# Show any references to host-gateway-process.ts / HOST_GATEWAY_PGREP_PATTERN in doctor.ts
rg -n "host-gateway-process|HOST_GATEWAY_PGREP_PATTERN" src/lib/actions/sandbox/doctor.ts || true

Repository: NVIDIA/NemoClaw

Length of output: 2950


🌐 Web query:

ss filter whitespace "sport = :80" "sport =:80" syntax

💡 Result:

In the ss command filter syntax, both "sport =:80" and "sport =:80" are generally parsed correctly because the filter expression parser is designed to be flexible regarding whitespace around operators [1][2][3]. The underlying grammar for ss filters, implemented in the iproute2 source code (specifically in ssfilter.y), processes tokens like SPORT, operators (e.g., '='), and host conditions (e.g., ':80') as distinct elements regardless of whether they are separated by spaces [3][4]. Key points regarding the syntax: - Operator flexibility: The '=' operator (and its aliases like '==', 'eq') can be used with or without surrounding whitespace [5][3]. - Port notation: The colon prefix (e.g., ':80') is the standard way to specify a port in ss filter expressions [1][6][7]. - Expression grouping: It is best practice to enclose the entire filter expression in single quotes (e.g., 'sport =:80') to prevent the shell from interpreting characters like '>', '<', or '|' as shell operators [1][8][7]. While "sport =:80" works, including a space (i.e., "sport =:80") is the recommended convention for readability and consistency with common documentation examples [1][9][7].

Citations:


Use HOST_GATEWAY_PGREP_PATTERN in dockerInspectGateway’s fallback to avoid false positives.

src/lib/actions/sandbox/doctor.ts currently checks pgrep -af openshell-gateway, which can match any unrelated process whose command line merely contains the substring. Use the anchored HOST_GATEWAY_PGREP_PATTERN from src/lib/onboard/host-gateway-process.ts (via pgrep -f ...) instead. The ss -ltn ( sport = :${GATEWAY_PORT} ) filter is passed as an argv element (no shell parsing) and is consistent with valid ss filter grouping.

🤖 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/doctor.ts` at line 204, The fallback in
dockerInspectGateway is using captureHostCommand("pgrep", ["-af",
"openshell-gateway"], 5000) which can produce false positives; replace the
literal "openshell-gateway" argv with the anchored HOST_GATEWAY_PGREP_PATTERN
imported from src/lib/onboard/host-gateway-process.ts and call
captureHostCommand("pgrep", ["-f", HOST_GATEWAY_PGREP_PATTERN], 5000) so pgrep
runs with the proper anchored pattern; keep the existing timeout and ensure any
variable name references (e.g., processCheck) are preserved.

@cv cv added the v0.0.61 label Jun 7, 2026
@cv cv self-assigned this Jun 7, 2026
cv added a commit that referenced this pull request Jun 7, 2026
## Summary
Salvages the intended fixes from #4583 on a clean branch. `nemoclaw
doctor` now accepts a verified local `openshell-gateway` process when
legacy container inspection fails, and tar listing validation has enough
buffer headroom for large sandbox state backups.

## Changes
- Reuse the anchored host gateway pgrep pattern in `doctor` and report a
local gateway as healthy only when both the process and gateway port are
present.
- Increase the tar listing `spawnSync` buffer used by path validation
and hard-link rejection.
- Add regression coverage for the local gateway doctor fallback and for
large hard-link validation archives.

## 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
- [ ] `npm run 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)

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

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

* **New Features**
* Diagnostics: improved gateway checks — when container inspection
fails, doctor probes the local gateway process/port, adjusts results
based on available evidence and probe-tool availability, and more
reliably captures host command outcomes.
* Reliability: safer handling of very large tar archives by constraining
command output buffering during archive inspection.

* **Tests**
* Added integration and regression tests covering gateway fallback
behaviors and large-tar scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Carlos Villela <cvillela@nvidia.com>
@cv cv closed this Jun 7, 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.

4 participants