Skip to content

feat: add Rabbit R1 device pairing command#589

Open
kalub92 wants to merge 1 commit intoNVIDIA:mainfrom
kalub92:feat/rabbit-r1
Open

feat: add Rabbit R1 device pairing command#589
kalub92 wants to merge 1 commit intoNVIDIA:mainfrom
kalub92:feat/rabbit-r1

Conversation

@kalub92
Copy link

@kalub92 kalub92 commented Mar 21, 2026

Summary

  • Adds nemoclaw <name> r1-connect command that configures the OpenClaw gateway inside a sandbox for Rabbit R1 device pairing over LAN or cloudflared tunnel
  • Includes scripts/r1-connect.sh which handles SSH into the sandbox, gateway configuration (bind to LAN, token auth), port forwarding rebind, QR code generation, and auto-approval of pending R1 devices
  • Refactors prompt() helper in credentials.js to use bash read instead of Node.js readline, fixing terminal hangs after heavy spawnSync usage
  • Switches sandbox build context copy from cp -r + rm -rf to rsync --exclude for efficiency

Test plan

  • Run nemoclaw <sandbox> r1-connect with a running sandbox and verify gateway is configured for LAN access
  • Run nemoclaw <sandbox> r1-connect --tunnel and verify cloudflared tunnel is established
  • Verify QR code is generated and scannable by R1 device
  • Verify auto-approval of pending R1 device connection
  • Run existing tests with npm test to ensure no regressions

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Added r1-connect command to configure Rabbit R1 device pairing within sandboxes over LAN.
    • Support for optional tunnel configuration for remote connectivity.
    • Automatic QR code generation for streamlined device pairing.
    • Device auto-approval upon detection in sandbox.
  • Improvements

    • Optimized sandbox build context handling for improved performance.

Add `nemoclaw <name> r1-connect` command that configures the OpenClaw
gateway inside a sandbox for Rabbit R1 device pairing over LAN or
cloudflared tunnel.

Also refactors the prompt() helper to use bash read instead of Node.js
readline (fixes hanging after heavy spawnSync usage), and switches
sandbox build context copy to rsync for efficiency.

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

coderabbitai bot commented Mar 21, 2026

📝 Walkthrough

Walkthrough

Modified the user input collection mechanism from Node.js readline to bash read builtin via spawnSync. Replaced directory copying with rsync excluding specific directories. Added a new R1 device pairing feature with a supporting bash script that configures gateway connectivity and device discovery over LAN.

Changes

Cohort / File(s) Summary
Input Handling
bin/lib/credentials.js
Replaced async readline-based prompt with synchronous bash read builtin via spawnSync, removing callback/cleanup flow and resolver logic.
Onboarding Setup
bin/lib/onboard.js
Added saveCredential import; replaced cp -r directory copy with rsync -a excluding node_modules and src, removing manual node_modules deletion.
CLI Actions
bin/nemoclaw.js
Introduced new sandbox action r1-connect with sandboxR1Connect() function; updated help text and action dispatch to support R1 device pairing commands with optional --tunnel flag.
R1 Pairing Script
scripts/r1-connect.sh
New script for R1 device pairing over LAN; configures gateway authentication token, port forwarding, LAN/tunnel detection, QR code generation, and device auto-approval with timeout handling.

Sequence Diagram

sequenceDiagram
    participant User
    participant CLI as nemoclaw CLI
    participant Sandbox as NemoClaw Sandbox
    participant Gateway as OpenClaw Gateway
    participant Tunnel as cloudflared Tunnel
    participant Device as Rabbit R1 Device

    User->>CLI: nemoclaw sandbox-name r1-connect [--tunnel]
    CLI->>Sandbox: bash r1-connect.sh --sandbox sandbox-name
    
    Sandbox->>Sandbox: Validate sandbox & SSH access
    Sandbox->>Gateway: Read/generate gateway.auth.token
    Sandbox->>Gateway: Configure gateway (LAN, token, auth)
    Sandbox->>Gateway: Restart gateway service
    
    Sandbox->>Sandbox: Detect host LAN IPv4 addresses
    Sandbox->>Sandbox: Setup port forward to 0.0.0.0:DASHBOARD_PORT
    
    alt --tunnel flag enabled
        Sandbox->>Tunnel: Start cloudflared tunnel to localhost:port
        Tunnel->>Tunnel: Establish secure connection
        Sandbox->>Sandbox: Retrieve tunnel host URL
    else LAN mode
        Sandbox->>Sandbox: Use detected LAN IPs
    end
    
    Sandbox->>Sandbox: Generate QR code with connection params
    Sandbox->>User: Display QR code & connection details
    
    User->>Device: Scan QR code on Rabbit R1
    Device->>Gateway: Connect to gateway (via LAN or tunnel)
    Gateway->>Sandbox: Device registration request
    
    Sandbox->>Sandbox: Poll openclaw devices list
    Sandbox->>Gateway: Auto-approve device
    Sandbox->>User: Pairing complete
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 A rabbit hops through scripts so grand,
With R1 devices close at hand,
The gateway glows with tokens bright,
QR codes dance in LAN's light,
Connected whiskers, tunnels free—
Our devices paired in harmony! 🌐

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: add Rabbit R1 device pairing command' directly reflects the main objective of adding a new CLI command for Rabbit R1 device pairing, which is the primary change across the modified files.

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

Tip

CodeRabbit can use your project's `biome` configuration to improve the quality of JS/TS/CSS/JSON code reviews.

Add a configuration file to your project to customize how CodeRabbit runs biome.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
scripts/r1-connect.sh (3)

290-293: Consider validating PENDING (requestId) format before use in shell command.

The requestId from openclaw devices list is used in a shell command at line 292. While it's likely a safe identifier from the openclaw system, validating it as alphanumeric/UUID would prevent potential injection if the format ever changes.

🛡️ Proposed validation
   if [ -n "$PENDING" ]; then
+    # Validate requestId is alphanumeric/UUID-like
+    if [[ ! "$PENDING" =~ ^[a-zA-Z0-9_-]+$ ]]; then
+      warn "Invalid requestId format, skipping approval"
+      continue
+    fi
     info "Found Rabbit R1 device, approving..."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/r1-connect.sh` around lines 290 - 293, Validate the PENDING value
before using it in the sandbox_exec call: ensure PENDING matches an expected
safe pattern (e.g., UUID or strict alphanumeric with allowed -/_ characters) and
if it fails validation, log an error and skip/exit instead of calling
sandbox_exec "openclaw devices approve '$PENDING'"; update the validation
immediately before the sandbox_exec invocation that uses PENDING and reference
the PENDING variable and the sandbox_exec "openclaw devices approve" call when
making the change.

258-259: Token is printed to stdout - ensure this is intentional.

Line 258 echoes the raw token to stdout. This is likely intentional for the user to copy, but be aware this could appear in logs if output is captured.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/r1-connect.sh` around lines 258 - 259, The script currently prints
the raw token variable via echo "Token: $TOKEN" which can leak secrets into
stdout/logs; update r1-connect.sh to stop printing the raw TOKEN to
stdout—either remove that echo, print a masked version, or send the token to
stderr (e.g., write to >&2) and document the behavior so callers know it may
appear in logs; target the echo that references TOKEN to implement this change.

69-72: Loose sandbox name matching may cause false positives.

The grep -q "$SANDBOX_NAME" check will match substrings. For example, a sandbox named "my" would match "my-assistant" in the list. Consider using word boundaries or exact matching.

♻️ Proposed fix using word boundary
 SANDBOX_LIST=$(openshell sandbox list 2>&1 || true)
-if ! echo "$SANDBOX_LIST" | grep -q "$SANDBOX_NAME"; then
+if ! echo "$SANDBOX_LIST" | grep -qw "$SANDBOX_NAME"; then
   error "Sandbox '$SANDBOX_NAME' not found. Run 'nemoclaw onboard' first."
 fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/r1-connect.sh` around lines 69 - 72, The current check uses a
substring grep against SANDBOX_LIST which can match partial names; replace the
substring match with an exact whole-line/fixed-string comparison when testing
SANDBOX_NAME (the pipeline that echoes SANDBOX_LIST and the grep -q
"$SANDBOX_NAME" check). In practice, change the grep invocation to perform a
fixed-string whole-line match (or use awk/per-line equality) so only exact
sandbox names match, preserving the surrounding logic that prints the error if
no match is found.
bin/lib/credentials.js (1)

6-7: Remove unused readline import.

The readline module is imported but no longer used after refactoring prompt() to use bash read. Additionally, spawnSync is required inline at line 39 but could be destructured here alongside execSync.

♻️ Proposed fix
 const fs = require("fs");
 const path = require("path");
-const readline = require("readline");
-const { execSync } = require("child_process");
+const { execSync, spawnSync } = require("child_process");

And at line 39:

-  const { spawnSync } = require("child_process");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/lib/credentials.js` around lines 6 - 7, Remove the unused readline import
and instead destructure spawnSync alongside execSync from child_process; update
the top-level requires to remove the readline symbol and change const { execSync
} = require("child_process") to include spawnSync so later inline use of
spawnSync (used in prompt() at/near line where spawnSync is called) no longer
requires a separate require.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/r1-connect.sh`:
- Around line 125-140: The existing token read into EXISTING_TOKEN is used
directly in sandbox_exec when setting gateway.auth.token (sandbox_exec "openclaw
config set gateway.auth.token '$TOKEN'") which allows shell injection if the
token contains quotes; validate EXISTING_TOKEN against a safe pattern (e.g.,
only [0-9a-fA-F]{64} for a 32-byte hex token) and if it fails, discard it and
generate a new secure token with openssl rand -hex 32, then assign to TOKEN;
additionally ensure the value passed to sandbox_exec is always a
validated/quoted hex-only TOKEN to prevent any chance of shell escaping when
calling sandbox_exec "openclaw config set gateway.auth.token '$TOKEN'".

---

Nitpick comments:
In `@bin/lib/credentials.js`:
- Around line 6-7: Remove the unused readline import and instead destructure
spawnSync alongside execSync from child_process; update the top-level requires
to remove the readline symbol and change const { execSync } =
require("child_process") to include spawnSync so later inline use of spawnSync
(used in prompt() at/near line where spawnSync is called) no longer requires a
separate require.

In `@scripts/r1-connect.sh`:
- Around line 290-293: Validate the PENDING value before using it in the
sandbox_exec call: ensure PENDING matches an expected safe pattern (e.g., UUID
or strict alphanumeric with allowed -/_ characters) and if it fails validation,
log an error and skip/exit instead of calling sandbox_exec "openclaw devices
approve '$PENDING'"; update the validation immediately before the sandbox_exec
invocation that uses PENDING and reference the PENDING variable and the
sandbox_exec "openclaw devices approve" call when making the change.
- Around line 258-259: The script currently prints the raw token variable via
echo "Token: $TOKEN" which can leak secrets into stdout/logs; update
r1-connect.sh to stop printing the raw TOKEN to stdout—either remove that echo,
print a masked version, or send the token to stderr (e.g., write to >&2) and
document the behavior so callers know it may appear in logs; target the echo
that references TOKEN to implement this change.
- Around line 69-72: The current check uses a substring grep against
SANDBOX_LIST which can match partial names; replace the substring match with an
exact whole-line/fixed-string comparison when testing SANDBOX_NAME (the pipeline
that echoes SANDBOX_LIST and the grep -q "$SANDBOX_NAME" check). In practice,
change the grep invocation to perform a fixed-string whole-line match (or use
awk/per-line equality) so only exact sandbox names match, preserving the
surrounding logic that prints the error if no match is found.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 33076755-6887-43d0-9e47-721a67519433

📥 Commits

Reviewing files that changed from the base of the PR and between a7102a4 and d34ae5b.

📒 Files selected for processing (4)
  • bin/lib/credentials.js
  • bin/lib/onboard.js
  • bin/nemoclaw.js
  • scripts/r1-connect.sh

Comment on lines +125 to +140
EXISTING_TOKEN=$(sandbox_exec "openclaw config get gateway.auth.token 2>/dev/null" || echo "")
EXISTING_TOKEN=$(echo "$EXISTING_TOKEN" | tr -d '[:space:]')

if [ -n "$EXISTING_TOKEN" ] && [ "$EXISTING_TOKEN" != "null" ] && [ "$EXISTING_TOKEN" != "undefined" ]; then
TOKEN="$EXISTING_TOKEN"
info "Reusing existing auth token"
else
TOKEN=$(openssl rand -hex 32)
info "Generated new auth token"
fi

# Apply gateway config inside the sandbox
sandbox_exec "openclaw config set gateway.bind lan"
sandbox_exec "openclaw config set gateway.auth.mode token"
sandbox_exec "openclaw config set gateway.auth.token '$TOKEN'"
sandbox_exec "openclaw config set gateway.controlUi.allowInsecureAuth true"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Potential shell injection via untrusted existing token.

The EXISTING_TOKEN is read from the sandbox and used in a shell command at line 139 with single quotes. If an attacker could set a malicious token containing ', they could escape the quotes and inject commands.

While the new token from openssl rand -hex 32 (line 132) is safe (hex-only), the existing token path should validate the token format.

🛡️ Proposed fix to validate token format
 EXISTING_TOKEN=$(sandbox_exec "openclaw config get gateway.auth.token 2>/dev/null" || echo "")
 EXISTING_TOKEN=$(echo "$EXISTING_TOKEN" | tr -d '[:space:]')

-if [ -n "$EXISTING_TOKEN" ] && [ "$EXISTING_TOKEN" != "null" ] && [ "$EXISTING_TOKEN" != "undefined" ]; then
+# Validate token is hex-only (safe for shell embedding)
+if [ -n "$EXISTING_TOKEN" ] && [ "$EXISTING_TOKEN" != "null" ] && [ "$EXISTING_TOKEN" != "undefined" ] && [[ "$EXISTING_TOKEN" =~ ^[a-fA-F0-9]+$ ]]; then
   TOKEN="$EXISTING_TOKEN"
   info "Reusing existing auth token"
 else
   TOKEN=$(openssl rand -hex 32)
   info "Generated new auth token"
 fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
EXISTING_TOKEN=$(sandbox_exec "openclaw config get gateway.auth.token 2>/dev/null" || echo "")
EXISTING_TOKEN=$(echo "$EXISTING_TOKEN" | tr -d '[:space:]')
if [ -n "$EXISTING_TOKEN" ] && [ "$EXISTING_TOKEN" != "null" ] && [ "$EXISTING_TOKEN" != "undefined" ]; then
TOKEN="$EXISTING_TOKEN"
info "Reusing existing auth token"
else
TOKEN=$(openssl rand -hex 32)
info "Generated new auth token"
fi
# Apply gateway config inside the sandbox
sandbox_exec "openclaw config set gateway.bind lan"
sandbox_exec "openclaw config set gateway.auth.mode token"
sandbox_exec "openclaw config set gateway.auth.token '$TOKEN'"
sandbox_exec "openclaw config set gateway.controlUi.allowInsecureAuth true"
EXISTING_TOKEN=$(sandbox_exec "openclaw config get gateway.auth.token 2>/dev/null" || echo "")
EXISTING_TOKEN=$(echo "$EXISTING_TOKEN" | tr -d '[:space:]')
# Validate token is hex-only (safe for shell embedding)
if [ -n "$EXISTING_TOKEN" ] && [ "$EXISTING_TOKEN" != "null" ] && [ "$EXISTING_TOKEN" != "undefined" ] && [[ "$EXISTING_TOKEN" =~ ^[a-fA-F0-9]+$ ]]; then
TOKEN="$EXISTING_TOKEN"
info "Reusing existing auth token"
else
TOKEN=$(openssl rand -hex 32)
info "Generated new auth token"
fi
# Apply gateway config inside the sandbox
sandbox_exec "openclaw config set gateway.bind lan"
sandbox_exec "openclaw config set gateway.auth.mode token"
sandbox_exec "openclaw config set gateway.auth.token '$TOKEN'"
sandbox_exec "openclaw config set gateway.controlUi.allowInsecureAuth true"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/r1-connect.sh` around lines 125 - 140, The existing token read into
EXISTING_TOKEN is used directly in sandbox_exec when setting gateway.auth.token
(sandbox_exec "openclaw config set gateway.auth.token '$TOKEN'") which allows
shell injection if the token contains quotes; validate EXISTING_TOKEN against a
safe pattern (e.g., only [0-9a-fA-F]{64} for a 32-byte hex token) and if it
fails, discard it and generate a new secure token with openssl rand -hex 32,
then assign to TOKEN; additionally ensure the value passed to sandbox_exec is
always a validated/quoted hex-only TOKEN to prevent any chance of shell escaping
when calling sandbox_exec "openclaw config set gateway.auth.token '$TOKEN'".

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.

1 participant