Skip to content

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

Open
cluster2600 wants to merge 9 commits intoNVIDIA:mainfrom
cluster2600:fix/03-local-inference-preset
Open

fix: add local-inference policy preset for Ollama/vLLM host access (#693)#781
cluster2600 wants to merge 9 commits intoNVIDIA:mainfrom
cluster2600:fix/03-local-inference-preset

Conversation

@cluster2600
Copy link
Copy Markdown
Contributor

@cluster2600 cluster2600 commented Mar 24, 2026

Summary

graph LR
    S[Sandbox] -->|local-inference preset| G[host.openshell.internal]
    G -->|:11434| O[Ollama]
    G -->|:8000| V[vLLM]
    style S fill:#bbf
    style O fill:#9f9
    style V fill:#9f9
Loading

New local-inference.yaml preset allows host.openshell.internal on ports 11434 (Ollama) and 8000 (vLLM) with binaries restriction. Onboard auto-suggests this preset when a local provider is selected.

4 new tests. Closes #693.

Test plan

  • All tests passing
  • Verify preset works with local Ollama setup

Summary by CodeRabbit

  • New Features

    • Added local inference auto-detection for supported local providers
    • Introduced local inference policy preset with network access controls
  • Bug Fixes

    • Implemented secret redaction in logs to mask sensitive credentials
    • Enhanced API key environment variable propagation in privileged contexts
    • Improved permission-based privilege detection during installation
    • Refined file removal logic based on directory permissions
  • Tests

    • Extended test coverage for new local inference preset

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 24, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Changes add local inference provider support with auto-detection in onboarding, a new policy preset for local inference endpoints, secret redaction in command logging, refactored API key propagation via environment variables instead of inline exports, and updated tests validating the new preset.

Changes

Cohort / File(s) Summary
Local Inference Configuration
nemoclaw-blueprint/policies/presets/local-inference.yaml, bin/lib/onboard.js
Added new local-inference preset defining network policies for host.openshell.internal on ports 11434 and 8000 with GET/POST allowances. Onboarding now auto-detects ollama-local and vllm-local providers and appends "local-inference" to suggestion list.
API Key Propagation Refactoring
bin/nemoclaw.js, scripts/telegram-bridge.js, scripts/walkthrough.sh
Changed NVIDIA_API_KEY delivery from inline environment variable exports in command strings to SSH SendEnv forwarding and spawned process env parameters, reducing key exposure in command payloads.
Secret Redaction & Logging
bin/lib/runner.js
Added redactSecrets() helper function and SECRET_PATTERNS list to mask sensitive substrings (API keys, bearer tokens) in logged command excerpts. Exported new function for external use.
Build & Installation
scripts/install.sh, uninstall.sh
Updated npm link privilege selection to detect writability via runtime prefix resolution instead of node manager name. Modified file removal logic to require parent directory writability for non-sudo deletion.
Test Coverage
test/policies.test.js
Extended preset assertions to include local-inference, updated hardcoded preset count to dynamic expectedPresets array, and added dedicated suite validating local-inference preset structure and endpoint extraction.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

Poem

🐰 Hop hop! Local minds now bloom,
No cloud needed in the room,
Keys hidden safe, secrets redacted true,
New paths unlocked—there's work to do! 🌿✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Out of Scope Changes check ❓ Inconclusive Most changes are directly related to local-inference support; however, modifications to scripts/install.sh and uninstall.sh (npm prefix detection and file removal logic) appear tangential to the core local-inference objective. Clarify whether the install.sh and uninstall.sh changes are necessary dependencies for local-inference support or if they should be addressed in separate PRs.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding a local-inference policy preset to enable Ollama/vLLM host access, which is the primary objective of this PR.
Linked Issues check ✅ Passed The PR partially addresses issue #693 by implementing a supported path for local Ollama access via the new local-inference preset and auto-suggestion during onboarding, but does not resolve the root cause hypothesis regarding hardcoded RFC1918 blocking below the policy layer.

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

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: 1

🧹 Nitpick comments (1)
test/policies.test.js (1)

99-124: Good test coverage for the new preset.

The tests thoroughly validate the key aspects of the local-inference preset: host resolution, port configuration, binaries section, and policy extraction.

Minor note: Line 115 references #676 in the comment, but this PR addresses issue #693. Consider updating the comment for accuracy.

📝 Suggested fix
     it("has a binaries section", () => {
       const content = policies.loadPreset("local-inference");
-      assert.ok(content.includes("binaries:"), "must have binaries section (ref: `#676`)");
+      assert.ok(content.includes("binaries:"), "must have binaries section (ref: `#693`)");
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/policies.test.js` around lines 99 - 124, Update the test assertion
message that references the wrong issue number: locate the "has a binaries
section" test in policies.test.js (the assertion checking
content.includes("binaries:") whose message contains "(ref: `#676`)") and change
the reference to the correct issue number "(ref: `#693`)". Ensure the assertion
message string is updated exactly where "binaries:" check is performed so the
test comment matches this PR.
🤖 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/telegram-bridge.js`:
- Around line 105-114: Add an e2e test that verifies NVIDIA_API_KEY is
transmitted via SendEnv by invoking the same ssh spawn flow (the code path that
builds safeSessionId, cmd and calls spawn with env including NVIDIA_API_KEY and
option "-o SendEnv=NVIDIA_API_KEY") but have the remote command (the
openshell-${SANDBOX} target) echo/print the received NVIDIA_API_KEY value or run
a small script that returns it; the test should assert the returned value
matches the local API_KEY, failing if the env is empty or differs, and should
run the exact ssh argument pattern used by spawn to exercise SendEnv behavior.

---

Nitpick comments:
In `@test/policies.test.js`:
- Around line 99-124: Update the test assertion message that references the
wrong issue number: locate the "has a binaries section" test in policies.test.js
(the assertion checking content.includes("binaries:") whose message contains
"(ref: `#676`)") and change the reference to the correct issue number "(ref:
`#693`)". Ensure the assertion message string is updated exactly where "binaries:"
check is performed so the test comment matches this PR.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 73e5aa37-fcf8-420b-a793-b4abb1c71825

📥 Commits

Reviewing files that changed from the base of the PR and between 6e1208c and 147dc92.

⛔ Files ignored due to path filters (1)
  • research/results.tsv is excluded by !**/*.tsv
📒 Files selected for processing (10)
  • bin/lib/onboard.js
  • bin/lib/runner.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
  • uninstall.sh

@cluster2600
Copy link
Copy Markdown
Contributor Author

Fixed the issue reference in the test assertion — was referencing #676 instead of #693. Cheers for catching that.

Regarding the out-of-scope warning: this branch is stacked on top of the test fixes (#779) and secret redaction (#780) PRs. The local-inference preset changes are in the last commit. Happy to rebase once the base PRs land if that's preferred.

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: 1

🧹 Nitpick comments (1)
test/policies.test.js (1)

12-14: Avoid hardcoding preset count separately from the canonical expected list.

Line 14 duplicates the source of truth already maintained in the expected names list, which increases maintenance drift risk.

♻️ Suggested simplification
+    const expected = ["discord", "docker", "huggingface", "jira", "local-inference", "npm", "outlook", "pypi", "slack", "telegram"];
+
     it("returns all 10 presets", () => {
       const presets = policies.listPresets();
-      assert.equal(presets.length, 10);
+      assert.equal(presets.length, expected.length);
     });
@@
     it("returns expected preset names", () => {
       const names = policies.listPresets().map((p) => p.name).sort();
-      const expected = ["discord", "docker", "huggingface", "jira", "local-inference", "npm", "outlook", "pypi", "slack", "telegram"];
       assert.deepEqual(names, expected);
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/policies.test.js` around lines 12 - 14, The test hardcodes the number 10
for preset count which duplicates the canonical expected list; change the
assertion to derive the expected length from the existing expected names array
(e.g., use expectedPresets.length or expectedNames.length) or assert equality
against the expected list returned by policies.listPresets(); update the
assertion in the test that calls policies.listPresets() so it compares lengths
or contents to the single source-of-truth variable instead of the literal 10.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/policies.test.js`:
- Around line 107-123: The test currently uses string includes on
policies.loadPreset output which can falsely pass if host, ports, and binaries
appear in different policy blocks; update the test to parse the preset content
(e.g., using a YAML parser) and then locate the "local_inference" entry returned
by policies.extractPresetEntries or by parsing the YAML, then assert that within
that single local_inference object the host "host.openshell.internal", ports
11434 and 8000 (or the corresponding port fields), and a non-empty "binaries"
section all exist; use the existing functions policies.loadPreset and
policies.extractPresetEntries to obtain content/entries or parse the YAML and
perform property checks on the same policy object rather than independent string
matches.

---

Nitpick comments:
In `@test/policies.test.js`:
- Around line 12-14: The test hardcodes the number 10 for preset count which
duplicates the canonical expected list; change the assertion to derive the
expected length from the existing expected names array (e.g., use
expectedPresets.length or expectedNames.length) or assert equality against the
expected list returned by policies.listPresets(); update the assertion in the
test that calls policies.listPresets() so it compares lengths or contents to the
single source-of-truth variable instead of the literal 10.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: decd4a58-6aeb-425b-87bd-b9874186f028

📥 Commits

Reviewing files that changed from the base of the PR and between 147dc92 and 5a2a7a2.

📒 Files selected for processing (1)
  • test/policies.test.js

@cluster2600
Copy link
Copy Markdown
Contributor Author

Heads up: there's a merge conflict from upstream test file changes (looks like the test suite was migrated to Vitest). Happy to rebase once the dust settles on the current merge queue. The core changes (local-inference preset YAML + onboard auto-suggestion) are conflict-free.

@cluster2600 cluster2600 force-pushed the fix/03-local-inference-preset branch from a39537b to ae8d91a Compare March 24, 2026 10:39
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.

🧹 Nitpick comments (1)
scripts/telegram-bridge.js (1)

146-146: Consider redacting stderr before including in response.

While the command no longer embeds secrets, stderr from the remote process could theoretically contain sensitive output. For defense-in-depth consistency with runner.js changes, consider importing and applying redactSecrets:

♻️ Optional: add redaction to stderr output
-const { shellQuote, validateName } = require("../bin/lib/runner");
+const { shellQuote, validateName, redactSecrets } = require("../bin/lib/runner");

Then at line 146:

-        resolve(`Agent exited with code ${code}. ${stderr.trim().slice(0, 500)}`);
+        resolve(`Agent exited with code ${code}. ${redactSecrets(stderr.trim().slice(0, 500))}`);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/telegram-bridge.js` at line 146, The response currently interpolates
raw stderr into the resolved message in scripts/telegram-bridge.js; import and
use the existing redactSecrets function to sanitize stderr before including it
in the resolve call. Specifically, ensure redactSecrets is imported/required at
top of the module and replace the direct use of stderr.trim().slice(0, 500) in
the resolve(`Agent exited...`) expression with a redacted version (e.g.,
redactSecrets(stderr).trim().slice(0, 500)) so the message contains sanitized
output while preserving the existing truncation and formatting.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@scripts/telegram-bridge.js`:
- Line 146: The response currently interpolates raw stderr into the resolved
message in scripts/telegram-bridge.js; import and use the existing redactSecrets
function to sanitize stderr before including it in the resolve call.
Specifically, ensure redactSecrets is imported/required at top of the module and
replace the direct use of stderr.trim().slice(0, 500) in the resolve(`Agent
exited...`) expression with a redacted version (e.g.,
redactSecrets(stderr).trim().slice(0, 500)) so the message contains sanitized
output while preserving the existing truncation and formatting.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5983e3ed-a22e-4432-97cc-d646ae2cda3e

📥 Commits

Reviewing files that changed from the base of the PR and between a39537b and ae8d91a.

⛔ Files ignored due to path filters (1)
  • research/results.tsv is excluded by !**/*.tsv
📒 Files selected for processing (10)
  • bin/lib/onboard.js
  • bin/lib/runner.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
  • uninstall.sh
✅ Files skipped from review due to trivial changes (2)
  • uninstall.sh
  • nemoclaw-blueprint/policies/presets/local-inference.yaml
🚧 Files skipped from review as they are similar to previous changes (6)
  • scripts/walkthrough.sh
  • bin/nemoclaw.js
  • scripts/install.sh
  • bin/lib/onboard.js
  • test/runner.test.js
  • test/policies.test.js

@basharder
Copy link
Copy Markdown

@cluster2600 Thank you very much for all this information! Claude Code and i are going to have a field day with finally getting this setup running this evening. i will report back tomorrow with my findings.

my usecase/system is complex but is probably very similar to anyone running a gaming desktop and a separate server for docker. in my case, ryzen + 6700xt desktop hosting models, and a fully kitted out 12c/64gb trashcan mac pro running proxmox running docker VMs for my production lab

@cluster2600 cluster2600 force-pushed the fix/03-local-inference-preset branch from ae8d91a to 81e8949 Compare March 25, 2026 12:55
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: 2

🤖 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/lib/onboard.js`:
- Around line 2007-2011: The code currently reads
registry.getSandbox(sandboxName).provider (sandboxProvider) which is often null
because registration doesn't set provider; instead detect the gateway by falling
back to the environment-set GATEWAY_NAME used by setupInference. Change the
detection to read sandbox.provider || sandbox.env?.GATEWAY_NAME (or other place
you set GATEWAY_NAME) before comparing to "ollama-local" / "vllm-local" so
suggestions.push("local-inference") fires; reference registry.getSandbox, the
sandboxProvider variable, setupInference/GATEWAY_NAME, and the
suggestions.push("local-inference") call to locate and update the logic.

In `@scripts/install.sh`:
- Around line 424-428: The root install.sh is missing the npm global prefix
writability check used in scripts/install.sh (NPM_GLOBAL_PREFIX="$(npm config
get prefix 2>/dev/null)" || true and the subsequent [ -n "$NPM_GLOBAL_PREFIX" ]
&& [ ! -w "$NPM_GLOBAL_PREFIX" ] && [ "$(id -u)" -ne 0 ] check that sets
SUDO="sudo"), so update the root install.sh to compute NPM_GLOBAL_PREFIX and set
SUDO the same way, then use that SUDO variable for both npm link invocations in
the root script (ensure both npm link calls are prefixed with $SUDO when
required) so behavior matches the logic in scripts/install.sh.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ad84f2b1-9181-4de9-a15e-8e7701790b3a

📥 Commits

Reviewing files that changed from the base of the PR and between ae8d91a and 81e8949.

⛔ Files ignored due to path filters (1)
  • research/results.tsv is excluded by !**/*.tsv
📒 Files selected for processing (9)
  • bin/lib/onboard.js
  • bin/lib/runner.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
  • uninstall.sh
✅ Files skipped from review due to trivial changes (2)
  • scripts/telegram-bridge.js
  • nemoclaw-blueprint/policies/presets/local-inference.yaml
🚧 Files skipped from review as they are similar to previous changes (5)
  • scripts/walkthrough.sh
  • uninstall.sh
  • bin/lib/runner.js
  • bin/nemoclaw.js
  • test/policies.test.js

@basharder
Copy link
Copy Markdown

I was able to get up and running last night

nemoclaw-pr781-tips.md

Community Tips: Getting Local Ollama Working with NemoClaw (PR #781)

Posted by: basharder Setup: Ubuntu 24.04 Docker host + Fedora desktop (AMD RX 6700 XT) running Ollama


Hey — wanted to share what I learned getting this working end-to-end after PR #781 merged the local-inference preset. Took a fair amount of digging to get past the proxy layer. Hopefully this saves others some time.


What PR #781 Fixes (and What It Doesn't)

The PR correctly adds host.openshell.internal to the policy allowlist. However there are several additional steps required that aren't obvious from the preset alone. The proxy enforces multiple independent layers — getting past policy is only one of them.


Step 1: Apply the local-inference preset during onboarding

At step 7/7 (Policy presets), type list instead of accepting the suggested presets, then enter local-inference to apply it. This is the foundation everything else builds on.


Step 2: The policy needs access: full and allowed_ips

The default preset uses rules: with GET/POST allow entries. This causes the proxy to require a CONNECT tunnel for the endpoint, which breaks plain HTTP to Ollama. Replace rules: with access: full and add allowed_ips pointing to your actual bridge IP:

  local_inference:
    name: local_inference
    endpoints:
      - host: host.openshell.internal
        port: 11434
        access: full
        allowed_ips:
          - 172.18.0.1   # your active Docker bridge IP — check with: ip addr show
    binaries:
      - { path: /usr/local/bin/openclaw }
      - { path: /usr/local/bin/claude }
      - { path: /usr/bin/curl }
      - { path: /usr/bin/bash }
      - { path: /usr/local/bin/node }

Without allowed_ips, the proxy returns a different 403 — "internal IP without allowed_ips" — even with a valid policy entry.

Push the policy:

sudo openshell policy set \
  --policy /home/<user>/.nemoclaw/source/nemoclaw-blueprint/policies/openclaw-sandbox.yaml \
  --wait <sandbox-name>

Step 3: Fix the DNS — host.openshell.internal must resolve to your active bridge

On many Linux systems docker0 (172.17.0.1) is the default but may be DOWN. Check:

ip addr show docker0     # may show DOWN
ip addr show             # find the active bridge — often 172.18.0.x

The sandbox pod's /etc/hosts is Kubernetes-managed. You cannot edit it directly. Patch the Sandbox custom resource and restart the pod:

sudo docker exec <cluster-container-id> kubectl patch sandbox <name> -n openshell \
  --type='json' \
  -p='[{"op": "replace", "path": "/spec/podTemplate/spec/hostAliases/0/ip", "value": "172.18.0.1"}]'

sudo docker exec <cluster-container-id> kubectl delete pod <name> -n openshell

Also update CoreDNS (the proxy uses cluster DNS, not /etc/hosts):

sudo docker exec <cluster-container-id> kubectl patch configmap coredns -n kube-system \
  --type merge \
  -p '{"data":{"NodeHosts":"<bridge-ip> <cluster-container-hostname>\n172.18.0.1 host.openshell.internal\n"}}'

sudo docker exec <cluster-container-id> kubectl rollout restart deployment coredns -n kube-system

Verify both resolve correctly:

sudo docker exec <cluster-container-id> kubectl exec -n openshell <sandbox-name> -- \
  getent hosts host.openshell.internal
# Should return 172.18.0.1

Step 4: Use nginx to bridge the sandbox to Ollama

The sandbox network namespace cannot reach LAN IPs directly — only through the proxy. Set up nginx on your Docker host to listen on the bridge IP and forward to Ollama:

server {
    listen 172.18.0.1:11434;
    location / {
        proxy_pass http://<ollama-host>:11434;
        proxy_read_timeout 300;
        proxy_connect_timeout 300;
        proxy_send_timeout 300;
    }
}

Test from the Docker host first:

curl http://172.18.0.1:11434/api/tags   # should return model list

Step 5: Register Ollama as an OpenShell inference provider

The proxy intercepts all /v1/chat/completions requests and routes them through its own inference system — not directly to your openclaw.json baseUrl. You must register Ollama as a provider at the gateway level:

sudo openshell provider create \
  --name ollama-local \
  --type openai \
  --credential OPENAI_API_KEY=ollama-local \
  --config baseUrl=http://host.openshell.internal:11434/v1

sudo openshell inference set --provider ollama-local --model llama3.1:8b --no-verify

The --no-verify flag is required because the verification step itself can't reach Ollama during setup.


Step 6: Set up auth for the inference provider inside the sandbox

openclaw won't use the provider without an auth profile. The correct format is NOT a flat JSON object — use the CLI inside the sandbox:

openclaw models auth paste-token --provider nvidia
# paste: ollama-local

This writes the correct versioned format to: /sandbox/.openclaw-data/agents/main/agent/auth-profiles.json


Step 7: Update openclaw.json to point at the right provider

The openclaw.json file is read-only inside the sandbox. Edit from outside:

# Find the live path (changes on each sandbox restart)
sudo docker exec <cluster-id> find /run/k3s -name "openclaw.json" 2>/dev/null

Edit it

CONF="<path-from-above>"
sudo docker exec <cluster-id> chmod 666 $CONF
sudo docker exec <cluster-id> sed -i 's|https://inference.local/v1|http://host.openshell.internal:11434/v1|g' $CONF
sudo docker exec <cluster-id> sed -i 's|nvidia/nemotron-3-super-120b-a12b|llama3.1:8b|g' $CONF
sudo docker exec <cluster-id> sed -i 's|inference/nvidia/nemotron-3-super-120b-a12b|nvidia/llama3.1:8b|g' $CONF

Warning: Never use openclaw config set to edit this file — it uses atomic rename which fails on the read-only mount and corrupts the file to zero bytes.


Debugging Tips

The single most useful debug tool is the OpenShell proxy log — it shows exactly why each request was denied:

sudo docker exec <cluster-id> tail -20 \
  /run/k3s/containerd/io.containerd.runtime.v2.task/k8s.io/<hash>/rootfs/var/log/openshell.$(date +%Y-%m-%d).log

Common deny reasons and fixes:

Log message Fix
not in policy 'local_inference' Policy not loaded — run openshell policy set --wait
binary '/usr/bin/curl' not allowed Add curl/bash/node to binaries: in policy
endpoint has L7 rules; use CONNECT Replace rules: with access: full
internal IP without allowed_ips Add allowed_ips: [] to endpoint
resolves to internal address 172.17.0.1 Fix DNS — patch Sandbox CRD and CoreDNS

AMD GPU Notes (RX 6700 XT / RDNA2)

Ollama supports AMD via ROCm but the 6700 XT isn't auto-detected. Add to the ollama systemd override on your Fedora/Linux machine:

[Service]
Environment="OLLAMA_HOST=0.0.0.0"
Environment="HSA_OVERRIDE_GFX_VERSION=10.3.0"

With 12GB VRAM, gpt-oss:20b runs at roughly 19% GPU / 81% CPU. Despite the CPU spill, the MoE architecture (only 3.6B active params) keeps it surprisingly fast. llama3.1:8b fits entirely in 12GB and runs 100% GPU.


Hope this helps others with the same setup. Happy to answer questions.

@cv
Copy link
Copy Markdown
Contributor

cv commented Mar 27, 2026

Hey @cluster2600 — sorry about this, but #961 landing has introduced merge conflicts here as well. I know you already flagged this was coming back on March 24th and offered to rebase once the dust settled — and I realize this is the second rebase ask today on top of #782. I appreciate your patience with all the unplanned extra work, especially given the great community writeup from @basharder showing how valuable this preset is. When you get a chance, could you rebase on main and resolve the conflicts? The core changes (local-inference preset YAML + onboard auto-suggestion) should still be clean as you predicted. This one is also in the critical path for the CLI migration (#924). Thanks again, and sorry for the churn.

@cluster2600 cluster2600 force-pushed the fix/03-local-inference-preset branch from d841ba1 to 84451a0 Compare March 27, 2026 18:42
@cluster2600
Copy link
Copy Markdown
Contributor Author

Rebased — no worries about the churn, happy to keep it tidy. The preset YAML and auto-suggestion came through clean as expected.

- uninstall.sh: check parent directory writability instead of file
  writability when deciding whether sudo is needed for removal. The
  previous check followed symlinks, so a symlink to a user-writable
  target in a root-owned directory would attempt unprivileged rm and
  fail with EACCES.

- scripts/install.sh: replace NODE_MGR-based sudo heuristic with a
  direct writability check on the npm global prefix. The old approach
  always used sudo for nodesource installs, which changes PATH and
  breaks environments where the prefix is already user-writable.
…in process args (NVIDIA#579, NVIDIA#664)

Add redactSecrets() to runner.js with patterns matching env assignments,
nvapi- prefixes, GitHub PATs, and Bearer tokens. Apply to error messages
in run() and runInteractive() so failed commands never leak credentials.

Fix setupSpark() to pass NVIDIA_API_KEY via env option instead of
interpolating into the command string (visible in ps aux).

Fix walkthrough.sh to use tmux -e for env propagation instead of
embedding the key in the shell command. Fix telegram-bridge.js to use
SSH SendEnv instead of command-line export.

9 new tests covering redaction patterns and regression guards.
…VIDIA#693)

RFC1918 addresses were blocked by the egress proxy because no policy
entry existed for host.openshell.internal. Add a local-inference preset
that whitelists ports 11434 (Ollama) and 8000 (vLLM) with binary
restrictions, and auto-suggest it during onboard when a local inference
provider is selected.
Signed-off-by: Maxime Grenu <maxime.grenu@gmail.com>
Show the jq command for extracting the token from inside the sandbox
and the openshell download path for host-side retrieval. Replaces
the vague ~/.openclaw/ reference which was misleading since the
config lives at /sandbox/.openclaw/ inside the container.

Signed-off-by: Maxime Grenu <maxime.grenu@gmail.com>
@cluster2600 cluster2600 force-pushed the fix/03-local-inference-preset branch from 84451a0 to 8792b94 Compare March 30, 2026 06:08
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.

Should this have been .gitignored?

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.

hello, it was .gitignored but still tracked.

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.

Good catch — yes, research/ was already in .gitignore but this file was tracked before the rule was added. Removed it from the index in the latest push.

cluster2600 and others added 2 commits March 30, 2026 16:31
research/ was already in .gitignore but results.tsv was still tracked.

Signed-off-by: Maxime Grenu <maxime.grenu@gmail.com>
@wscurran wscurran added Local Models Running NemoClaw with local models enhancement: feature Use this label to identify requests for new capabilities in NemoClaw. labels Mar 30, 2026
@wscurran
Copy link
Copy Markdown
Contributor

✨ Thanks for submitting this PR with a detailed summary, it proposes a fix to improve the functionality of NemoClaw, which could enhance the user experience.

cv added a commit that referenced this pull request Apr 1, 2026
…ript modules (#1240)

## Summary

- Extract ~210 lines of pure, side-effect-free functions from the
3,800-line `onboard.js` into **5 typed TypeScript modules** under
`src/lib/`:
- `gateway-state.ts` — gateway/sandbox state classification from
openshell output
- `validation.ts` — failure classification, API key validation, model ID
checks
  - `url-utils.ts` — URL normalization, text compaction, env formatting
  - `build-context.ts` — Docker build context filtering, recovery hints
  - `dashboard.ts` — dashboard URL resolution and construction
- Add **56 co-located unit tests** (`src/lib/*.test.ts`) for the
extracted modules
- Set up CLI TypeScript compilation: `tsconfig.src.json` compiles `src/`
→ `dist/` as CJS
- `onboard.js` imports from compiled `dist/lib/` output — transparent to
callers
- Pre-commit hook updated to build TS and include `dist/lib/` in
coverage

These functions are **not touched by any #924 blocker PR** (#781, #782,
#819, #672, #634, #890), so this extraction is safe to land immediately.

## Test plan

- [x] 598 CLI tests pass (542 existing + 56 new)
- [x] `tsc -p tsconfig.src.json` compiles cleanly
- [x] `tsc -p tsconfig.cli.json` type-checks cleanly
- [x] `tsc -p jsconfig.json` type-checks cleanly
- [x] Coverage ratchet passes with `dist/lib/` included

Closes #1237. Relates to #924 (shell consolidation).

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

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

* **New Features**
* Improved sandbox-creation recovery hints and targeted remediation
commands.
  * Smarter dashboard URL resolution and control-UI URL construction.

* **Bug Fixes**
  * More accurate gateway and sandbox state detection.
* Enhanced classification of validation/apply failures and safer
model/key validation.
  * Better provider URL normalization and loopback handling.

* **Tests**
  * Added comprehensive tests covering new utilities.

* **Chores**
* CLI now builds before CLI tests; CI/commit hooks updated to run the
CLI build.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
laitingsheng pushed a commit that referenced this pull request Apr 2, 2026
…ript modules (#1240)

## Summary

- Extract ~210 lines of pure, side-effect-free functions from the
3,800-line `onboard.js` into **5 typed TypeScript modules** under
`src/lib/`:
- `gateway-state.ts` — gateway/sandbox state classification from
openshell output
- `validation.ts` — failure classification, API key validation, model ID
checks
  - `url-utils.ts` — URL normalization, text compaction, env formatting
  - `build-context.ts` — Docker build context filtering, recovery hints
  - `dashboard.ts` — dashboard URL resolution and construction
- Add **56 co-located unit tests** (`src/lib/*.test.ts`) for the
extracted modules
- Set up CLI TypeScript compilation: `tsconfig.src.json` compiles `src/`
→ `dist/` as CJS
- `onboard.js` imports from compiled `dist/lib/` output — transparent to
callers
- Pre-commit hook updated to build TS and include `dist/lib/` in
coverage

These functions are **not touched by any #924 blocker PR** (#781, #782,
#819, #672, #634, #890), so this extraction is safe to land immediately.

## Test plan

- [x] 598 CLI tests pass (542 existing + 56 new)
- [x] `tsc -p tsconfig.src.json` compiles cleanly
- [x] `tsc -p tsconfig.cli.json` type-checks cleanly
- [x] `tsc -p jsconfig.json` type-checks cleanly
- [x] Coverage ratchet passes with `dist/lib/` included

Closes #1237. Relates to #924 (shell consolidation).

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

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

* **New Features**
* Improved sandbox-creation recovery hints and targeted remediation
commands.
  * Smarter dashboard URL resolution and control-UI URL construction.

* **Bug Fixes**
  * More accurate gateway and sandbox state detection.
* Enhanced classification of validation/apply failures and safer
model/key validation.
  * Better provider URL normalization and loopback handling.

* **Tests**
  * Added comprehensive tests covering new utilities.

* **Chores**
* CLI now builds before CLI tests; CI/commit hooks updated to run the
CLI build.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

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

@cv cv left a comment

Choose a reason for hiding this comment

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

Thanks for the policy preset work — the local-inference preset and auto-detection logic are solid.

However, this PR needs a rebase and scope reduction before it can land:

1. Drop the runner.js secret redaction. #1246 just merged with a more comprehensive implementation (stdio interception, URL credential redaction, partial reveal for debuggability). The redactSecrets() added here now conflicts with that work. Please rebase onto main and remove the runner.js changes — the redaction is already handled.

2. Consider splitting the remaining changes. The policy preset, the env var propagation fixes (sudo -E, SSH SendEnv, tmux -e), and the install.sh sudo logic are three independent concerns. The preset is the core fix for #693; the others could be separate PRs. Up to you whether to split or keep bundled, but the runner.js part must go.

3. CI is red (DCO + checks). A rebase onto current main should fix this.

The preset YAML, onboard auto-detection, and test additions all look good — just need the cleanup above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement: feature Use this label to identify requests for new capabilities in NemoClaw. Local Models Running NemoClaw with local models

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

4 participants