fix(sandbox): allow openclaw.json writes for channel configuration (fixes #606)#637
Conversation
…ixes NVIDIA#606) openclaw.json is locked (root:root 444) at build time to prevent agent tampering (NVIDIA#514, NVIDIA#588). However, users legitimately need to modify config at runtime — e.g. running `openclaw onboard` to add a Discord bot token. The atomic write (tmp → copyfile → rename) in OpenClaw's config writer fails with EACCES against the immutable file. PR NVIDIA#601 addressed the env-var path (passing DISCORD_BOT_TOKEN into the sandbox), but the underlying issue remains: any `openclaw onboard` or `/config` write inside the sandbox hits the same EACCES error. Fix: at sandbox startup, copy the immutable openclaw.json to the writable state directory (~/.openclaw-data/) and set OPENCLAW_CONFIG_PATH to redirect all OpenClaw config reads/writes to the copy. The original immutable file stays intact as a read-only reference; the Landlock policy on /sandbox/.openclaw continues to protect it. Changes: - nemoclaw-start.sh: add prepare_writable_config() that copies the locked config to ~/.openclaw-data/openclaw.json and exports OPENCLAW_CONFIG_PATH; update print_dashboard_urls to respect the env var - e2e-test.sh: add test 11 verifying writable overlay works and immutable original stays untouched
📝 WalkthroughWalkthroughThe changes implement a writable config overlay pattern to address read-only permission issues. A new Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment Tip You can enable review details to help with troubleshooting, context usage and more.Enable the |
There was a problem hiding this comment.
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 `@scripts/nemoclaw-start.sh`:
- Around line 140-143: The copy can fail if the parent directory of the target
writable config doesn't exist; before copying immutable_cfg to writable_cfg add
creation of the parent directory for writable_cfg (use mkdir -p on the dirname
of the writable_cfg variable) so the cp will succeed, then proceed to cp
"$immutable_cfg" "$writable_cfg" and chmod 600 "$writable_cfg"; ensure the mkdir
is no-op if the directory already exists and occurs only when writable_cfg is
missing.
In `@test/e2e-test.sh`:
- Around line 258-261: The test copies an immutable config to writable using
shutil.copy2(immutable, writable) but never ensures the parent directory
(~/.openclaw-data) exists, causing copy to fail; update the test before the copy
(around the prepare_writable_config() simulation) to create the writable file's
parent directory (os.path.dirname(writable) or the ~/.openclaw-data path) if it
doesn't exist (mkdir -p semantics) and set proper permissions, then proceed to
assert the immutable exists and copy via shutil.copy2; this mirrors
prepare_writable_config() behavior and prevents the missing-directory error for
variables immutable and writable.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: ef8bb28a-7434-47a0-b51b-e890f8f48863
📒 Files selected for processing (2)
scripts/nemoclaw-start.shtest/e2e-test.sh
| if [ ! -f "$writable_cfg" ] && [ -f "$immutable_cfg" ]; then | ||
| cp "$immutable_cfg" "$writable_cfg" | ||
| chmod 600 "$writable_cfg" | ||
| fi |
There was a problem hiding this comment.
Missing parent directory creation for writable config.
The cp command on line 141 will fail with "No such file or directory" if ~/.openclaw-data/ doesn't exist. Add directory creation before copying.
🛠️ Proposed fix
if [ ! -f "$writable_cfg" ] && [ -f "$immutable_cfg" ]; then
+ mkdir -p "$(dirname "$writable_cfg")"
cp "$immutable_cfg" "$writable_cfg"
chmod 600 "$writable_cfg"
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.
| if [ ! -f "$writable_cfg" ] && [ -f "$immutable_cfg" ]; then | |
| cp "$immutable_cfg" "$writable_cfg" | |
| chmod 600 "$writable_cfg" | |
| fi | |
| if [ ! -f "$writable_cfg" ] && [ -f "$immutable_cfg" ]; then | |
| mkdir -p "$(dirname "$writable_cfg")" | |
| cp "$immutable_cfg" "$writable_cfg" | |
| chmod 600 "$writable_cfg" | |
| fi |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/nemoclaw-start.sh` around lines 140 - 143, The copy can fail if the
parent directory of the target writable config doesn't exist; before copying
immutable_cfg to writable_cfg add creation of the parent directory for
writable_cfg (use mkdir -p on the dirname of the writable_cfg variable) so the
cp will succeed, then proceed to cp "$immutable_cfg" "$writable_cfg" and chmod
600 "$writable_cfg"; ensure the mkdir is no-op if the directory already exists
and occurs only when writable_cfg is missing.
| # Simulate what prepare_writable_config does | ||
| assert os.path.isfile(immutable), f'Immutable config missing: {immutable}' | ||
| shutil.copy2(immutable, writable) | ||
| os.chmod(writable, 0o600) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Search for creation of .openclaw-data directory
rg -n '\.openclaw-data' --type sh --type pyRepository: NVIDIA/NemoClaw
Length of output: 235
🏁 Script executed:
# Search for any mkdir/makedirs operations
rg -n '(mkdir|makedirs)' --type sh --type pyRepository: NVIDIA/NemoClaw
Length of output: 1597
🏁 Script executed:
# Look at prepare_writable_config function in nemoclaw-start.sh
sed -n '130,150p' scripts/nemoclaw-start.shRepository: NVIDIA/NemoClaw
Length of output: 889
🏁 Script executed:
# Examine the complete test setup around lines 244-288
sed -n '244,288p' test/e2e-test.shRepository: NVIDIA/NemoClaw
Length of output: 1692
🏁 Script executed:
# Search for test setup/initialization
sed -n '1,243p' test/e2e-test.sh | grep -n -A2 -B2 "openclaw-data\|mkdirSync\|makedirs"Repository: NVIDIA/NemoClaw
Length of output: 368
🏁 Script executed:
# Check if there's a setup.sh or fixture that creates directories
git ls-files | grep -E "(setup|fixture|init)" | head -20Repository: NVIDIA/NemoClaw
Length of output: 392
Missing directory creation before copying config.
The test and prepare_writable_config() in nemoclaw-start.sh both assume ~/.openclaw-data/ exists. The .openclaw directory is created in test setup (line 196), but .openclaw-data is never created anywhere. When shutil.copy2() executes without the parent directory, it will fail.
🛠️ Proposed fix for the test
# Simulate what prepare_writable_config does
assert os.path.isfile(immutable), f'Immutable config missing: {immutable}'
+os.makedirs(os.path.dirname(writable), exist_ok=True)
shutil.copy2(immutable, writable)
os.chmod(writable, 0o600)📝 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.
| # Simulate what prepare_writable_config does | |
| assert os.path.isfile(immutable), f'Immutable config missing: {immutable}' | |
| shutil.copy2(immutable, writable) | |
| os.chmod(writable, 0o600) | |
| # Simulate what prepare_writable_config does | |
| assert os.path.isfile(immutable), f'Immutable config missing: {immutable}' | |
| os.makedirs(os.path.dirname(writable), exist_ok=True) | |
| shutil.copy2(immutable, writable) | |
| os.chmod(writable, 0o600) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@test/e2e-test.sh` around lines 258 - 261, The test copies an immutable config
to writable using shutil.copy2(immutable, writable) but never ensures the parent
directory (~/.openclaw-data) exists, causing copy to fail; update the test
before the copy (around the prepare_writable_config() simulation) to create the
writable file's parent directory (os.path.dirname(writable) or the
~/.openclaw-data path) if it doesn't exist (mkdir -p semantics) and set proper
permissions, then proceed to assert the immutable exists and copy via
shutil.copy2; this mirrors prepare_writable_config() behavior and prevents the
missing-directory error for variables immutable and writable.
Summary
At sandbox startup, copy the immutable
openclaw.jsonto the writable state directory (~/.openclaw-data/) and setOPENCLAW_CONFIG_PATHso OpenClaw reads/writes the writable copy. The original locked file stays untouched.Problem
openclaw.jsonis locked (root:root 444) at build time to prevent agent tampering (#514, #588). However, users runningopenclaw onboardinside the sandbox to configure channels (e.g. adding a Discord bot token) hit:PR #601 addressed the specific case of passing
DISCORD_BOT_TOKENvia env vars, but the underlying issue remains: anyopenclaw onboardor config write inside the sandbox fails with EACCES.Solution
Add
prepare_writable_config()tonemoclaw-start.sh:~/.openclaw/openclaw.json→~/.openclaw-data/openclaw.json(the writable state dir that already exists for agents, plugins, etc.)OPENCLAW_CONFIG_PATHpointing to the writable copyThis is idempotent: the copy only happens if the writable file doesn't already exist, so container restarts won't overwrite user changes.
Changes
scripts/nemoclaw-start.sh: addprepare_writable_config(); updateprint_dashboard_urlsto respectOPENCLAW_CONFIG_PATHtest/e2e-test.sh: add test 11 verifying the writable overlay works and the immutable original stays untouchedSecurity considerations
/sandbox/.openclaw/openclaw.jsonremainsroot:root 444— unchanged/sandbox/.openclawcontinues to protect the original~/.openclaw-data/which is already the designated writable area for sandbox stateOPENCLAW_CONFIG_PATHis set by the entrypoint (not by the agent), so the agent cannot redirect it to an arbitrary pathTest plan
install-preflight.test.jsis unrelated)openclaw onboardinside sandbox, configure Discord — no EACCES error/sandbox/.openclaw/openclaw.jsonremains immutable after config writesCloses #606
Summary by CodeRabbit
New Features
Tests