Skip to content

fix(sandbox): keep Homebrew on Linuxbrew prefix#4459

Merged
ericksoa merged 8 commits into
mainfrom
fix/brew-linuxbrew-prefix
May 28, 2026
Merged

fix(sandbox): keep Homebrew on Linuxbrew prefix#4459
ericksoa merged 8 commits into
mainfrom
fix/brew-linuxbrew-prefix

Conversation

@ericksoa
Copy link
Copy Markdown
Contributor

@ericksoa ericksoa commented May 28, 2026

Summary

Fixes #3757 as a follow-up to #3916. The base image now exposes brew on the normal sandbox PATH without making Homebrew infer /usr/local as its prefix.

Problem

#3916 correctly baked Homebrew into /home/linuxbrew/.linuxbrew, but it exposed the entry point with a plain /usr/local/bin/brew symlink. In a fresh sandbox that made Homebrew report /usr/local as the prefix, which broke the QA path in two ways:

  • brew install --quiet hello tried to create locks under root-owned /usr/local/var/homebrew.
  • On arm64, Homebrew rejected the Intel/default /usr/local prefix.

After fixing the prefix, installed formula commands also need to be reachable in sandbox shell sessions without asking users to source brew shellenv manually.

Changes

  • Replace the /usr/local/bin/brew symlink with a wrapper that execs /home/linuxbrew/.linuxbrew/bin/brew, preserving the Linuxbrew prefix.
  • Add build-time assertions that /usr/local/bin/brew --prefix and sandbox login-shell brew --prefix both return /home/linuxbrew/.linuxbrew.
  • Add /home/linuxbrew/.linuxbrew/bin through /etc/profile.d/nemoclaw-linuxbrew.sh only for the sandbox user, while deliberately avoiding a global Docker ENV PATH entry for the sandbox-writable Linuxbrew prefix.
  • Add network-policy E2E coverage for the real QA path: apply brew, install hello, resolve hello on PATH, and run it.
  • Update the Homebrew docs wording from symlink to wrapper and show shell-session execution for installed formula commands.

Validation

  • npm run build:cli
  • npm run typecheck:cli
  • npx vitest run test/policies.test.ts
  • npm run source-shape:check
  • hadolint Dockerfile.base
  • bash -n test/e2e/test-network-policy.sh
  • shellcheck test/e2e/test-network-policy.sh
  • git diff --check
  • Throwaway base-image behavior smoke with this wrapper/profile change applied in-container and no global Linuxbrew PATH:
    • root login shell does not include /home/linuxbrew/.linuxbrew/bin
    • direct /usr/local/bin/brew --prefix -> /home/linuxbrew/.linuxbrew
    • sandbox login shell includes /home/linuxbrew/.linuxbrew/bin
    • brew install --quiet hello succeeds
    • command -v hello -> /home/linuxbrew/.linuxbrew/bin/hello
    • hello -> Hello, world!

Local caveats

  • Full docker build -f Dockerfile.base ... could not run on this host because Docker has no working buildx/BuildKit component; the legacy builder fails on the existing RUN --mount step before reaching this change.
  • npx vitest run test/nemoclaw-start.test.ts test/policies.test.ts passed the related policy coverage but still hit existing local fixture failures in test/nemoclaw-start.test.ts because nemoclaw/node_modules/json5 is absent in this worktree. test/policies.test.ts passes by itself.
  • npx prek run --files Dockerfile.base docs/network-policy/integration-policy-examples.mdx test/policies.test.ts passed the visible file hooks including hadolint, gitleaks, docs-to-skills, and source-shape, then failed in the full CLI test hook on the same unrelated test/nemoclaw-start.test.ts baseline-capture failures plus a coverage temp-file ENOENT.

Summary by CodeRabbit

  • Chores

    • Switched Homebrew runtime to a wrapper-script approach and conditionally expose the package manager bin on sandbox shell PATH
    • Added runtime validations to ensure wrapper and PATH behave correctly across privileged and sandbox sessions
    • Made sandbox command execution timeouts configurable
  • Documentation

    • Clarified wrapper behavior and how installed commands are available in sandbox sessions
  • Tests

    • Updated test wording to reference "PATH wrapper"
    • Added an end-to-end test validating install/use flow and expected runtime behavior (TC-NET-11)

Review Change Stack

Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 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

Replace symlink with a /usr/local/bin/brew wrapper delegating to the Linuxbrew prefix; add build-time brew --prefix/--version checks; install /etc/profile.d/nemoclaw-linuxbrew.sh to expose Linuxbrew bin only for the sandbox user; update docs/tests and add TC-NET-11 E2E brew install test.

Changes

Homebrew wrapper script and PATH provisioning

Layer / File(s) Summary
Dockerfile wrapper implementation and PATH configuration
Dockerfile.base
Comments updated to document the wrapper approach; generates /usr/local/bin/brew wrapper, verifies brew --prefix and brew --version at build time, and creates /etc/profile.d/nemoclaw-linuxbrew.sh to add /home/linuxbrew/.linuxbrew/bin to PATH only for the sandbox user; build-time checks validate root vs sandbox PATH and brew resolution.
Documentation and unit test text update
docs/network-policy/integration-policy-examples.mdx, test/policies.test.ts
Docs reword the Homebrew integration from "symlink" to "wrapper" and clarify that installed formula commands are available from the Linuxbrew bin in sandbox shells; unit test description updated from "PATH shim" to "PATH wrapper".
E2E test: brew preset install flow
test/e2e/test-network-policy.sh
Adds TC-NET-11, implements test_net_11_brew_install_hello which applies the brew preset, installs/runs hello inside the sandbox, and asserts installation paths and output; adds the test invocation to main(); introduces SANDBOX_EXEC_TIMEOUT_SECONDS and PACKAGE_MANAGER_SANDBOX_TIMEOUT_SECONDS and makes sandbox_exec use configurable timeouts.
sequenceDiagram
  participant BuildStep
  participant Wrapper
  participant Linuxbrew
  participant ProfileD
  participant Sandbox

  BuildStep->>Wrapper: write wrapper script
  BuildStep->>BuildStep: run brew --prefix and brew --version checks
  BuildStep->>ProfileD: create runtime PATH rule file
  Sandbox->>Wrapper: invoke brew (via gosu sandbox)
  Wrapper->>Linuxbrew: exec linuxbrew bin/brew with args
  Linuxbrew->>Sandbox: return brew output
Loading

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels: Docker

Suggested reviewers:

  • cv

"🐰 I hopped a script into the tree,
I wrap the brew so calls run free.
Sandbox PATH is gently set,
Hello prints when installs are met.
Tiny hops, a happy CI!"

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 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 describes the main change: replacing symlinks with a wrapper to keep Homebrew on the Linuxbrew prefix.
Linked Issues check ✅ Passed The PR directly addresses issue #3757 by ensuring brew is usable in sandbox login shells, preventing /usr/local prefix inference, and enabling the documented brew install test case to run end-to-end.
Out of Scope Changes check ✅ Passed All changes (Dockerfile wrapper, profile.d script, docs updates, E2E test addition, timeout configurability) are directly aligned with making the brew preset functional and validating it works end-to-end.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/brew-linuxbrew-prefix

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

@github-actions
Copy link
Copy Markdown
Contributor

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 28, 2026

E2E Advisor Recommendation

Required E2E: network-policy-e2e, openclaw-onboard-security-posture-e2e
Optional E2E: test-e2e-sandbox, sandbox-operations-e2e

Dispatch hint: network-policy-e2e,openclaw-onboard-security-posture-e2e

Workflow run

Full advisor summary

E2E Recommendation Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E

  • network-policy-e2e (high): Directly covers the changed network-policy E2E script and the affected Homebrew user flow: restricted-policy onboard, policy-add brew, endpoint allowlisting, brew wrapper prefix/temp handling, formula install, and command execution inside the sandbox.
  • openclaw-onboard-security-posture-e2e (high): Dockerfile.base changes sandbox startup/profile behavior and security-sensitive PATH handling. This full OpenClaw onboard lane validates a real sandbox boot with non-root/security posture assertions against the PR's locally rebuilt base image.

Optional E2E

  • test-e2e-sandbox (medium): Useful image-level confidence for Dockerfile.base changes because the sandbox image workflow builds the production/test images from the resolved base and runs the containerized sandbox E2E smoke suite, but it is adjacent rather than the primary brew/network-policy user flow.
  • sandbox-operations-e2e (high): Optional lifecycle confidence if maintainers want broader coverage that the rebuilt sandbox base still supports normal sandbox operations after the profile/PATH changes.

New E2E recommendations

  • sandbox runtime PATH security (high): Existing coverage now validates the positive Homebrew install path, but there is no focused runtime assertion that privileged startup/PID 1 does not inherit /home/linuxbrew/.linuxbrew/bin while sandbox user login shells do, or that hostile PATH/id injection cannot affect the profile gate after sandbox boot.
    • Suggested test: Add a security-posture E2E assertion for Linuxbrew PATH isolation: inspect PID 1/startup environment for absence of the Linuxbrew bin path, verify sandbox bash -lc includes it for the sandbox user, and verify /usr/local/bin/brew forces HOMEBREW_TEMP/TMPDIR=/tmp when hostile env values are supplied.

Dispatch hint

  • Workflow: .github/workflows/nightly-e2e.yaml
  • jobs input: network-policy-e2e,openclaw-onboard-security-posture-e2e

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 28, 2026

E2E Scenario Advisor Recommendation

Required scenario E2E: None
Optional scenario E2E: None

Workflow run

Full scenario advisor summary

E2E Scenario Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required scenario E2E

  • None. No scenario workflow, scenario metadata, scenario runtime, or validation-suite files changed.

Optional scenario E2E

  • None.

Relevant changed files

  • None.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 28, 2026

PR Review Advisor

Findings: 0 needs attention, 0 worth checking, 0 nice ideas
Since last review: 4 prior items resolved, 0 still apply, 0 new items found

Workflow run details

This is an automated advisory review. A human maintainer must make the final merge decision.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

Actionable comments posted: 0

@ericksoa ericksoa added bug Something isn't working NV QA Bugs found by the NVIDIA QA Team UAT Issues flagged for User Acceptance Testing. enhancement: policy Sandbox Use this label to identify issues related to the NemoClaw isolated environment based on OpenShell. fix v0.0.54 Release target labels May 28, 2026
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

Actionable comments posted: 0

Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
@github-actions
Copy link
Copy Markdown
Contributor

Selective E2E Results — ✅ All requested jobs passed

Run: 26597813048
Target ref: 36642ec3f08efdf5b641bf0643e5bc86dfce1f3b
Workflow ref: main
Requested jobs: rebuild-openclaw-e2e
Summary: 0 passed, 0 failed, 0 skipped

Job Result
rebuild-openclaw-e2e ⚠️ cancelled

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

🧹 Nitpick comments (1)
test/e2e/test-network-policy.sh (1)

339-345: ⚡ Quick win

Assert wrapper resolution explicitly in TC-NET-11

Line 339 validates brew --prefix, but not that brew resolves to /usr/local/bin/brew (the wrapper contract from Dockerfile.base Line 233-Line 288). Add a direct path assertion to catch PATH-order regressions.

Proposed diff
-  response=$(sandbox_exec "bash -lc 'set -e; export HOMEBREW_NO_AUTO_UPDATE=1 HOMEBREW_NO_ENV_HINTS=1; brew --prefix; brew install --quiet hello; command -v hello; hello'" 2>&1) || true
+  response=$(sandbox_exec "bash -lc 'set -e; export HOMEBREW_NO_AUTO_UPDATE=1 HOMEBREW_NO_ENV_HINTS=1; command -v brew; brew --prefix; brew install --quiet hello; command -v hello; hello'" 2>&1) || true
@@
-  if echo "$response" | grep -q "/home/linuxbrew/.linuxbrew" \
+  if echo "$response" | grep -q "/usr/local/bin/brew" \
+    && echo "$response" | grep -q "/home/linuxbrew/.linuxbrew" \
     && echo "$response" | grep -q "/home/linuxbrew/.linuxbrew/bin/hello" \
     && echo "$response" | grep -q "Hello, world!"; then
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/e2e/test-network-policy.sh` around lines 339 - 345, The test calls
sandbox_exec and captures response but never verifies that the brew executable
resolves to the wrapper path; update the sandbox_exec command string (in the
same invocation that runs "brew --prefix; brew install ...; command -v hello;
hello") to also run "command -v brew" so the path is emitted, then add an
assertion alongside the existing greps to check response contains
"/usr/local/bin/brew" (i.e. include a grep for that path in the if condition
that currently checks for "/home/linuxbrew/.linuxbrew",
"/home/linuxbrew/.linuxbrew/bin/hello", and "Hello, world!").
🤖 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.

Nitpick comments:
In `@test/e2e/test-network-policy.sh`:
- Around line 339-345: The test calls sandbox_exec and captures response but
never verifies that the brew executable resolves to the wrapper path; update the
sandbox_exec command string (in the same invocation that runs "brew --prefix;
brew install ...; command -v hello; hello") to also run "command -v brew" so the
path is emitted, then add an assertion alongside the existing greps to check
response contains "/usr/local/bin/brew" (i.e. include a grep for that path in
the if condition that currently checks for "/home/linuxbrew/.linuxbrew",
"/home/linuxbrew/.linuxbrew/bin/hello", and "Hello, world!").

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 22732571-17f9-41e8-85c7-54327aaca5a9

📥 Commits

Reviewing files that changed from the base of the PR and between 36642ec and b2edcc7.

📒 Files selected for processing (2)
  • Dockerfile.base
  • test/e2e/test-network-policy.sh
🚧 Files skipped from review as they are similar to previous changes (1)
  • Dockerfile.base

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

Actionable comments posted: 0

@github-actions
Copy link
Copy Markdown
Contributor

Selective E2E Results — ✅ All requested jobs passed

Run: 26598137061
Target ref: b2edcc7b5f41fa521aafcd63238d5e85e7a9bb7c
Workflow ref: main
Requested jobs: network-policy-e2e,openclaw-onboard-security-posture-e2e
Summary: 0 passed, 0 failed, 0 skipped

Job Result
network-policy-e2e ⚠️ cancelled
openclaw-onboard-security-posture-e2e ⚠️ cancelled

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

Actionable comments posted: 0

@github-actions
Copy link
Copy Markdown
Contributor

Selective E2E Results — ✅ All requested jobs passed

Run: 26598419637
Target ref: 6bd8fc90a9c9092b3d873b953ae1170781a5b226
Workflow ref: main
Requested jobs: network-policy-e2e,openclaw-onboard-security-posture-e2e
Summary: 0 passed, 0 failed, 0 skipped

Job Result
network-policy-e2e ⚠️ cancelled
openclaw-onboard-security-posture-e2e ⚠️ cancelled

@ericksoa ericksoa added the Docker Support for Docker containerization label May 28, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Selective E2E Results — ❌ Some jobs failed

Run: 26598669807
Target ref: 2a23e181303428a09e69802e1e4e1f0dd27905b2
Workflow ref: main
Requested jobs: network-policy-e2e
Summary: 0 passed, 1 failed, 0 skipped

Job Result
network-policy-e2e ❌ failure

Failed jobs: network-policy-e2e. Check run artifacts for logs.

@github-actions
Copy link
Copy Markdown
Contributor

Selective E2E Results — ✅ All requested jobs passed

Run: 26599920696
Target ref: 6d8e960cd4c1c5d984fc9695474cce3d2f17ee74
Workflow ref: main
Requested jobs: network-policy-e2e
Summary: 1 passed, 0 failed, 0 skipped

Job Result
network-policy-e2e ✅ success

@ericksoa ericksoa merged commit 195bbcc into main May 28, 2026
29 checks passed
@ericksoa ericksoa deleted the fix/brew-linuxbrew-prefix branch May 28, 2026 20:41
@github-actions
Copy link
Copy Markdown
Contributor

Selective E2E Results — ✅ All requested jobs passed

Run: 26601603327
Target ref: 3c4ce7b17a04ec690ced01153487b93f06197646
Workflow ref: main
Requested jobs: network-policy-e2e,openclaw-onboard-security-posture-e2e
Summary: 2 passed, 0 failed, 0 skipped

Job Result
network-policy-e2e ✅ success
openclaw-onboard-security-posture-e2e ✅ success

@miyoungc miyoungc mentioned this pull request May 29, 2026
12 tasks
miyoungc added a commit that referenced this pull request May 29, 2026
## Summary
Refreshes the NemoClaw documentation for the v0.0.54 release and
regenerates user skills from the Fern MDX source. Also keeps the Fern
CLI pin current so local docs checks use the upgraded Fern version.

## Related Issue
<!-- No single related issue. This is release-prep documentation
catch-up. -->

## Changes
- #4403 -> `docs/manage-sandboxes/messaging-channels.mdx`,
`docs/reference/commands.mdx`, `docs/about/release-notes.mdx`: Document
Telegram, Discord, and Slack post-rebuild bridge verification and
summarize channel activation fixes.
- #4222 -> `docs/about/release-notes.mdx`: Include Slack generated
channel enablement in the v0.0.54 messaging summary.
- #4346 -> `docs/get-started/windows-preparation.mdx`,
`docs/about/release-notes.mdx`: Document safer Windows bootstrap
behavior for Ubuntu first-run and Docker Desktop WSL integration.
- #4416 -> `docs/inference/use-local-inference.mdx`,
`docs/about/release-notes.mdx`: Document the Docker Desktop WSL
requirement for Windows-host Ollama.
- #4442 -> `docs/about/release-notes.mdx`: Summarize the optional
NemoHermes native web dashboard and related environment variables.
- #4426 -> `docs/about/release-notes.mdx`: Summarize copy-paste recovery
hints for invalid sandbox names and missing NVIDIA API keys.
- #4459 -> `docs/about/release-notes.mdx`: Summarize the Linuxbrew
prefix fix for sandbox Homebrew usage.
- #4450 -> `docs/about/release-notes.mdx`: Summarize `/nemoclaw` slash
command startup activation.
- #4468 -> `docs/about/release-notes.mdx`: Summarize scope-upgrade
approval recovery.
- #4325 -> `docs/about/release-notes.mdx`: Summarize the narrowed
`web_fetch` host-gateway allowance.
- #4474 -> `docs/about/release-notes.mdx`: Summarize Hermes Provider
smoke-check behavior for OAuth versus Nous API key setup.
- Refresh generated `.agents/skills/nemoclaw-user-*` references from
`docs/` and update `fern/fern.config.json` to Fern `5.41.2`.

## Type of Change
- [ ] Code change (feature, bug fix, or refactor)
- [ ] Code change with doc updates
- [x] Doc only (prose changes, no code sample modifications)
- [ ] Doc only (includes code sample changes)

## Verification
<!-- Check each item you ran and confirmed. Leave unchecked items you
skipped. Doc-only changes do not require npm test unless you ran it. -->
- [ ] `npx prek run --all-files` passes
- [ ] `npm test` passes
- [ ] Tests added or updated for new or changed behavior
- [x] No secrets, API keys, or credentials committed
- [x] Docs updated for user-facing behavior changes
- [ ] `npm run docs` builds without warnings (doc changes only)
- [x] 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)

---
<!-- DCO sign-off required by CI. Run: git config user.name && git
config user.email -->
Signed-off-by: Miyoung Choi <miyoungc@nvidia.com>

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

* **New Features**
  * Optional NemoHermes native web dashboard (configurable port and TUI)
* GPU memory cleanup now unloads Ollama models when switching providers
or stopping services

* **Bug Fixes**
  * Improved sandbox name validation with suggested slug recovery
* Windows-host Ollama now requires Docker Desktop WSL integration and
exits with remediation guidance when unsupported

* **Documentation**
* Clarified quickstart/onboard flow, installer TTY/non‑TTY guidance,
Hermes Docker prerequisites, sandbox hardening, and channels add rebuild
checks

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/NVIDIA/NemoClaw/pull/4539?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working Docker Support for Docker containerization enhancement: policy fix NV QA Bugs found by the NVIDIA QA Team Sandbox Use this label to identify issues related to the NemoClaw isolated environment based on OpenShell. UAT Issues flagged for User Acceptance Testing. v0.0.54 Release target

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[NemoClaw][Linux] Brew policy preset applies network rules but does not provision brew binary, so brew install step in test cannot succeed

3 participants