Skip to content

fix(cli): add local-inference policy preset for Ollama/vLLM host access (#693)#1469

Open
cv wants to merge 3 commits intomainfrom
fix/local-inference-preset-781
Open

fix(cli): add local-inference policy preset for Ollama/vLLM host access (#693)#1469
cv wants to merge 3 commits intomainfrom
fix/local-inference-preset-781

Conversation

@cv
Copy link
Copy Markdown
Contributor

@cv cv commented Apr 4, 2026

Summary

  • Add local-inference network policy preset allowing sandbox egress to host.openshell.internal on ports 11434 (Ollama) and 8000 (vLLM)
  • Auto-suggest the preset during onboarding when provider is ollama-local or vllm-local
  • Fix API key exposure in setupSpark, telegram-bridge.js, and walkthrough.sh by passing credentials via environment instead of command arguments
  • Conditional sudo for npm link in installer when global prefix is not writable

Supersedes #781. Closes #693.

Test plan

  • All pre-commit hooks pass (prettier, eslint, shellcheck, gitleaks, commitlint)
  • All pre-push hooks pass (TypeScript type-check, tests)
  • test/policies.test.js updated: preset count 9→10, new local-inference preset tests
  • test/runner.test.js updated: walkthrough credential exposure guard allows safe tmux -e pattern
  • Manual: nemoclaw onboard with ollama-local provider suggests local-inference preset at step 7
  • Manual: verify scripts/telegram-bridge.js passes API key via SendEnv (not in cmd args)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a local-inference preset for Ollama and vLLM via host gateway.
    • Onboarding now auto-detects provider and suggests the local-inference preset when applicable.
  • Bug Fixes

    • Improved API key propagation to subprocesses, SSH, and tmux sessions.
    • Made npm linking conditional on system permissions to avoid failures.
  • Tests

    • Added tests for the local-inference preset and tightened credential-exposure checks.

…ss (#693)

Add a local-inference network policy preset that allows sandbox egress
to host.openshell.internal on ports 11434 (Ollama) and 8000 (vLLM).
Auto-suggest the preset during onboarding when provider is ollama-local
or vllm-local.

Also fixes API key exposure in setupSpark, telegram-bridge, and
walkthrough by passing credentials via environment instead of command
arguments.

Supersedes #781.

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

coderabbitai bot commented Apr 4, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2a0732d8-22e7-4c8b-b964-27f12b2a3ca4

📥 Commits

Reviewing files that changed from the base of the PR and between c398197 and 944a4fc.

📒 Files selected for processing (1)
  • scripts/install.sh
✅ Files skipped from review due to trivial changes (1)
  • scripts/install.sh

📝 Walkthrough

Walkthrough

Added a new local-inference policy preset and made onboarding/policy selection provider-aware to auto-suggest it for local providers; tightened NVIDIA_API_KEY handling across setup scripts (SSH SendEnv, tmux env forwarding, ensureApiKey before spark setup); tests updated to cover the new preset and credential-pattern validation.

Changes

Cohort / File(s) Summary
Local Inference Preset
nemoclaw-blueprint/policies/presets/local-inference.yaml
Added local-inference preset permitting REST access to host.openshell.internal:11434 (Ollama) and host.openshell.internal:8000 (vLLM) with enforcement and declared binaries /usr/local/bin/openclaw, /usr/local/bin/claude.
Onboarding / Policy Selection
bin/lib/onboard.js
Extended _setupPolicies and setupPoliciesWithSelection to accept/derive provider and auto-append local-inference suggestion when provider is ollama-local or vllm-local; onboarding passes current provider into the call.
CLI / Spark Setup
bin/nemoclaw.js
setupSpark() now awaits ensureApiKey(); spark setup subprocess invoked with sudo -E and explicit NVIDIA_API_KEY provided in child process env.
Installer Script
scripts/install.sh
npm link invocation now conditionally prefixes sudo when global npm prefix is non-writable for non-root users.
Credential Propagation Scripts
scripts/telegram-bridge.js, scripts/walkthrough.sh
telegram-bridge.js moves NVIDIA_API_KEY into SSH SendEnv and local env (removes inline export); walkthrough.sh injects NVIDIA_API_KEY into tmux pane via -e "NVIDIA_API_KEY=...".
Tests
test/policies.test.js, test/runner.test.js
Added tests asserting local-inference preset presence and contents; updated preset count (10→11); adjusted credential exposure guard to permit NVIDIA_API_KEY only when passed via tmux -e "NVIDIA_API_KEY=..." pattern.

Sequence Diagram

sequenceDiagram
    participant User
    participant Onboard as Onboarding
    participant Registry as Sandbox Registry
    participant Policy as Policy System
    participant Sandbox as Sandbox Environment
    participant Local as Local Inference

    User->>Onboard: complete onboarding / select provider
    Onboard->>Registry: getSandbox(sandboxName)
    Registry-->>Onboard: { provider: "ollama-local" / "vllm-local" }
    Onboard->>Onboard: detect provider -> append `local-inference` suggestion
    Onboard->>Policy: load/apply preset `local-inference`
    Policy-->>Onboard: preset loaded
    User->>Sandbox: perform inference request
    Sandbox->>Local: connect to host.openshell.internal:11434/8000
    Local-->>Sandbox: inference response
    Sandbox-->>User: return result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I hopped through presets and sniffed the host gate,
Suggested local models so sandboxes relate,
Keys sent by SendEnv and tmux keep them tight,
Ollama and vLLM answer through the night,
A rabbit's tiny cheer for local inference delight! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.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 clearly and specifically summarizes the main change: adding a local-inference policy preset for Ollama/vLLM host access, which is the primary feature of this pull request.
Linked Issues check ✅ Passed The PR implements the requested solution to issue #693 by adding local-inference preset support and auto-detection for ollama-local/vllm-local providers during onboarding, directly addressing the core problem of enabling local Ollama inference access.
Out of Scope Changes check ✅ Passed Changes to setupSpark, telegram-bridge.js, walkthrough.sh, and install.sh are security-related improvements (preventing API key exposure, conditional sudo) that support or align with the local-inference feature; none appear out-of-scope.

✏️ 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/local-inference-preset-781

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

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

Inline comments:
In `@bin/nemoclaw.js`:
- Around line 650-653: Remove the unnecessary credential prompting and secret
export around the Spark setup: delete the call to ensureApiKey() and stop
injecting NVIDIA_API_KEY into the sudo run of setup-spark.sh; update the run
invocation that calls `run(\`sudo -E bash "\${SCRIPTS}/setup-spark.sh"\`, { env:
{ NVIDIA_API_KEY: process.env.NVIDIA_API_KEY } })` to a plain invocation without
the env property (or with only required non-secret env vars), so
`setup-spark.sh` is executed without prompting for or passing the NVIDIA_API_KEY
secret; target the ensureApiKey() call and the run(...) invocation in
bin/nemoclaw.js that reference SCRIPTS, setup-spark.sh, and NVIDIA_API_KEY.

In `@scripts/walkthrough.sh`:
- Around line 87-90: The tmux usage in scripts/walkthrough.sh currently expands
the secret into the shell argv via -e "NVIDIA_API_KEY=$NVIDIA_API_KEY", which
can leak via process inspection; instead, set the secret into the tmux
server/session environment (use tmux set-environment -t "$SESSION"
NVIDIA_API_KEY "$NVIDIA_API_KEY") and then call tmux split-window with -e
NVIDIA_API_KEY (no shell expansion) so the value is supplied by tmux internally;
after the split, remove the secret from the tmux environment (unset-environment)
to avoid lingering secrets. Ensure you update the tmux command that creates the
right pane (the split-window invocation) and add the set-environment and
unset-environment steps around it.

In `@test/runner.test.js`:
- Around line 511-514: The test's guard currently allows argv-based secret
exposure by accepting patterns like tmux -e "NVIDIA_API_KEY=..." — change the
credential-exposure checks in test/runner.test.js to reject environment
assignments passed as command-line arguments (e.g., any argv token matching
/[A-Z0-9_]+=.+/ or the specific pattern tmux -e "<KEY>=<VALUE>") rather than
whitelisting them; update the validation logic used in the credential-exposure
test (the block that comments about `tmux -e "NVIDIA_API_KEY=..."`) so it treats
such -e "KEY=..." usages as failures and add the same check for the other
occurrence mentioned (around lines 524-527).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: eb151673-5688-47b7-a3ae-a4c3b0c4314b

📥 Commits

Reviewing files that changed from the base of the PR and between 3f4d6fe and ea6b1bc.

📒 Files selected for processing (8)
  • bin/lib/onboard.js
  • bin/nemoclaw.js
  • nemoclaw-blueprint/policies/presets/local-inference.yaml
  • scripts/install.sh
  • scripts/telegram-bridge.js
  • scripts/walkthrough.sh
  • test/policies.test.js
  • test/runner.test.js

Comment on lines +650 to +653
await ensureApiKey();
run(`sudo -E bash "${SCRIPTS}/setup-spark.sh"`, {
env: { NVIDIA_API_KEY: process.env.NVIDIA_API_KEY },
});
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

Remove API-key prompting from setup-spark; it’s unrelated and expands secret exposure.

Line 650 now forces credential collection for a Docker-group setup flow, and Lines 651-653 pass that secret into sudo even though scripts/setup-spark.sh does not consume NVIDIA_API_KEY.

Proposed fix
 async function setupSpark() {
-  await ensureApiKey();
-  run(`sudo -E bash "${SCRIPTS}/setup-spark.sh"`, {
-    env: { NVIDIA_API_KEY: process.env.NVIDIA_API_KEY },
-  });
+  run(`sudo bash "${SCRIPTS}/setup-spark.sh"`);
 }
📝 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
await ensureApiKey();
run(`sudo -E bash "${SCRIPTS}/setup-spark.sh"`, {
env: { NVIDIA_API_KEY: process.env.NVIDIA_API_KEY },
});
run(`sudo bash "${SCRIPTS}/setup-spark.sh"`);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/nemoclaw.js` around lines 650 - 653, Remove the unnecessary credential
prompting and secret export around the Spark setup: delete the call to
ensureApiKey() and stop injecting NVIDIA_API_KEY into the sudo run of
setup-spark.sh; update the run invocation that calls `run(\`sudo -E bash
"\${SCRIPTS}/setup-spark.sh"\`, { env: { NVIDIA_API_KEY:
process.env.NVIDIA_API_KEY } })` to a plain invocation without the env property
(or with only required non-secret env vars), so `setup-spark.sh` is executed
without prompting for or passing the NVIDIA_API_KEY secret; target the
ensureApiKey() call and the run(...) invocation in bin/nemoclaw.js that
reference SCRIPTS, setup-spark.sh, and NVIDIA_API_KEY.

Comment on lines +87 to 90
# Split right pane for the agent — pass NVIDIA_API_KEY via tmux -e so it
# reaches the sandbox environment without being embedded in the command string.
tmux split-window -h -t "$SESSION" -e "NVIDIA_API_KEY=$NVIDIA_API_KEY" \
"openshell sandbox connect nemoclaw -- bash -c 'nemoclaw-start openclaw agent --agent main --local --session-id live'"
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

Avoid placing NVIDIA_API_KEY in tmux command arguments.

Line 89 expands the secret into argv (KEY=value), which can leak via process inspection.

Proposed fix
-# Split right pane for the agent — pass NVIDIA_API_KEY via tmux -e so it
-# reaches the sandbox environment without being embedded in the command string.
-tmux split-window -h -t "$SESSION" -e "NVIDIA_API_KEY=$NVIDIA_API_KEY" \
+# Split right pane for the agent. Inherit NVIDIA_API_KEY from the script
+# environment instead of embedding KEY=value in argv.
+tmux split-window -h -t "$SESSION" \
   "openshell sandbox connect nemoclaw -- bash -c 'nemoclaw-start openclaw agent --agent main --local --session-id live'"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/walkthrough.sh` around lines 87 - 90, The tmux usage in
scripts/walkthrough.sh currently expands the secret into the shell argv via -e
"NVIDIA_API_KEY=$NVIDIA_API_KEY", which can leak via process inspection;
instead, set the secret into the tmux server/session environment (use tmux
set-environment -t "$SESSION" NVIDIA_API_KEY "$NVIDIA_API_KEY") and then call
tmux split-window with -e NVIDIA_API_KEY (no shell expansion) so the value is
supplied by tmux internally; after the split, remove the secret from the tmux
environment (unset-environment) to avoid lingering secrets. Ensure you update
the tmux command that creates the right pane (the split-window invocation) and
add the set-environment and unset-environment steps around it.

Comment on lines +511 to +514
// Check only executable lines (tmux spawn, openshell connect) — not comments/docs.
// The safe `tmux -e "NVIDIA_API_KEY=..."` pattern is allowed because it
// passes the key through the environment rather than embedding it in the
// shell command that runs inside the sandbox.
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

This guard now whitelists argv-based secret exposure.

Allowing -e "NVIDIA_API_KEY=..." weakens the credential-exposure test by permitting the raw key in executable command arguments.

Proposed fix
-      // Check only executable lines (tmux spawn, openshell connect) — not comments/docs.
-      // The safe `tmux -e "NVIDIA_API_KEY=..."` pattern is allowed because it
-      // passes the key through the environment rather than embedding it in the
-      // shell command that runs inside the sandbox.
+      // Check only executable lines (tmux spawn, openshell connect) — not comments/docs.
+      // NVIDIA_API_KEY must not appear in executable command text.
       const cmdLines = src
         .split("\n")
         .filter(
@@
       for (const line of cmdLines) {
-        if (line.includes("NVIDIA_API_KEY")) {
-          // Only the tmux -e env-passing pattern is acceptable
-          expect(line).toMatch(/-e\s+"NVIDIA_API_KEY=/);
-        }
+        expect(line.includes("NVIDIA_API_KEY")).toBe(false);
       }

Also applies to: 524-527

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

In `@test/runner.test.js` around lines 511 - 514, The test's guard currently
allows argv-based secret exposure by accepting patterns like tmux -e
"NVIDIA_API_KEY=..." — change the credential-exposure checks in
test/runner.test.js to reject environment assignments passed as command-line
arguments (e.g., any argv token matching /[A-Z0-9_]+=.+/ or the specific pattern
tmux -e "<KEY>=<VALUE>") rather than whitelisting them; update the validation
logic used in the credential-exposure test (the block that comments about `tmux
-e "NVIDIA_API_KEY=..."`) so it treats such -e "KEY=..." usages as failures and
add the same check for the other occurrence mentioned (around lines 524-527).

cv and others added 2 commits April 3, 2026 21:08
Keep both provider (ours) and webSearchConfig (main) options in
setupPoliciesWithSelection. Update preset count 10→11 to account
for the brave preset added on main.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.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.

Local Ollama inference blocked by OpenShell egress proxy — RFC1918 addresses return 403 regardless of policy

1 participant