Skip to content

fix(image): add missing openclaw-data symlinks for runtime-writable paths#1519

Merged
cv merged 1 commit into
mainfrom
fix/missing-openclaw-data-symlinks
Apr 5, 2026
Merged

fix(image): add missing openclaw-data symlinks for runtime-writable paths#1519
cv merged 1 commit into
mainfrom
fix/missing-openclaw-data-symlinks

Conversation

@ericksoa

@ericksoa ericksoa commented Apr 5, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add symlinks for exec-approvals.json, telegram/, and credentials/ from .openclaw/ to .openclaw-data/
  • These paths are written by OpenClaw at runtime but had no writable target, causing EACCES errors
  • Follows the exact same pattern as the 11 existing symlinks in Dockerfile.base

Security posture

Unchanged. The .openclaw/ directory remains root-owned (755), all symlinks are root-owned and chattr +i locked at runtime by nemoclaw-start.sh. The agent can only write file contents through the symlinks to sandbox-owned .openclaw-data/.

Fixes

Summary by CodeRabbit

  • Chores
    • Extended Docker image filesystem with new data storage directories for telegram and credentials management.
    • Added approvals configuration file to the Docker environment setup.

…legram, credentials

OpenClaw writes to exec-approvals.json, telegram/, and credentials/
at runtime, but these paths had no symlinks from the root-owned
.openclaw/ directory to the sandbox-writable .openclaw-data/.
This caused EACCES permission denied errors for tool approvals,
Telegram offset persistence, and WhatsApp credential storage.

Fixes #1027
Refs #975, #1114

Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
@coderabbitai

coderabbitai Bot commented Apr 5, 2026

Copy link
Copy Markdown
Contributor

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: 85156840-8635-4d04-ba2e-94f15d0a7c1b

📥 Commits

Reviewing files that changed from the base of the PR and between d162bbe and be0167e.

📒 Files selected for processing (1)
  • Dockerfile.base

📝 Walkthrough

Walkthrough

The Docker base image is extended with two new state subdirectories (telegram and credentials) under /sandbox/.openclaw-data, each symlinked to the read-facing /sandbox/.openclaw directory. An exec-approvals.json file is initialized in the data directory and symlinked alongside the existing update-check.json to resolve filesystem access issues.

Changes

Cohort / File(s) Summary
Sandbox State Initialization
Dockerfile.base
Added telegram and credentials subdirectories under /sandbox/.openclaw-data with symlinks to /sandbox/.openclaw. Created exec-approvals.json file in /sandbox/.openclaw-data and symlinked it to /sandbox/.openclaw alongside update-check.json.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~2 minutes

Poem

🐰 New directories bloom in the sandbox's nested home,
Symlinks dance where permissions roam,
exec-approvals finds its place,
No more permission errors to face! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR partially addresses issue #1027 by adding symlinks for exec-approvals.json, telegram/, and credentials/, but does not implement directory ownership changes or complete the full requirements. Address remaining requirements from #1027: ensure /sandbox/.openclaw/ is owned by sandbox:sandbox with proper permissions, fix file/directory conflicts during extraction, and ensure all config files are writable by the sandbox user.
✅ 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 accurately describes the main change: adding missing openclaw-data symlinks for runtime-writable paths in the Dockerfile.
Out of Scope Changes check ✅ Passed All changes are within scope—the PR adds symlinks following the existing pattern in Dockerfile.base, which aligns with the provisioning layer approach specified in issue #1027.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ 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/missing-openclaw-data-symlinks

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

@ericksoa ericksoa self-assigned this Apr 5, 2026
@cv cv merged commit f7d4121 into main Apr 5, 2026
15 of 17 checks passed
@ericksoa ericksoa deleted the fix/missing-openclaw-data-symlinks branch April 5, 2026 23:40
tranzmatt pushed a commit to tranzmatt/NemoClaw that referenced this pull request Apr 6, 2026
…aths (NVIDIA#1519)

## Summary

- Add symlinks for `exec-approvals.json`, `telegram/`, and
`credentials/` from `.openclaw/` to `.openclaw-data/`
- These paths are written by OpenClaw at runtime but had no writable
target, causing EACCES errors
- Follows the exact same pattern as the 11 existing symlinks in
`Dockerfile.base`

## Security posture

Unchanged. The `.openclaw/` directory remains root-owned (755), all
symlinks are root-owned and `chattr +i` locked at runtime by
`nemoclaw-start.sh`. The agent can only write file contents through the
symlinks to sandbox-owned `.openclaw-data/`.

## Fixes

- Fixes NVIDIA#1027 — `exec-approvals.json` EACCES
- Refs NVIDIA#975 — Telegram offset persistence EACCES
- Refs NVIDIA#1114 — WhatsApp credentials EACCES

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

## Summary by CodeRabbit

* **Chores**
* Extended Docker image filesystem with new data storage directories for
telegram and credentials management.
  * Added approvals configuration file to the Docker environment setup.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
ericksoa pushed a commit that referenced this pull request Apr 7, 2026
… (#1305)

## Summary

Fixes the four issues reported in #1114 — EACCES permission errors and
missing gateway token when running inside the NemoClaw sandbox.

### Issue mapping

| # | Reported error | Fix |
|---|----------------|-----|
| 1 | `EACCES: open '/sandbox/.openclaw/openclaw.json.*.tmp'` |
`install_configure_guard` — intercepts `openclaw configure` with a clear
error and directs users to `nemoclaw onboard --resume` on the host |
| 2 | Same as #1 (different PID) | Same fix |
| 3 | `EACCES: mkdir '/sandbox/.openclaw/credentials'` | Already
resolved on main via #1519 (credentials symlink to `.openclaw-data/`) |
| 4 | No WhatsApp QR code | Consequence of #3, also resolved by #1519 |

### Root cause (issues 1 & 2)

OpenClaw's `configure` command performs atomic writes — it creates a
temp
file (`openclaw.json.PID.UUID.tmp`) in the same directory as the config.
Since `/sandbox/.openclaw/` is Landlock read-only at the kernel level,
file creation is rejected with EACCES. This is by design: the sandbox
config is intentionally immutable at runtime.

Rather than weakening Landlock (security regression), we intercept the
command in the sandbox shell and guide users to the correct host-side
workflow.

### Changes

**1. `install_configure_guard()`** — Writes a shell function wrapper to
`.bashrc`/`.profile` that intercepts `openclaw configure` and prints:
```
Error: 'openclaw configure' cannot modify config inside the sandbox.
The sandbox config is read-only (Landlock enforced) for security.

To change your configuration, exit the sandbox and run:
  nemoclaw onboard --resume

This rebuilds the sandbox with your updated settings.
```
All other `openclaw` subcommands pass through to the real binary.

**2. `export_gateway_token()`** — Reads `gateway.auth.token` from
`openclaw.json` and exports it as `OPENCLAW_GATEWAY_TOKEN`, so
interactive sessions (`openshell sandbox connect`) can authenticate
with the gateway. Persists to `.bashrc`/`.profile` using idempotent
marker blocks and cleans stale tokens on revocation.

**3. `_read_gateway_token()` helper** — Shared Python snippet used by
both `export_gateway_token` and `print_dashboard_urls` (deduplication,
uses `with open()` context manager).

All three are called in both root and non-root startup paths.

## Security properties preserved

- `/sandbox/.openclaw` remains root-owned, Landlock read-only
- `openclaw.json` remains chmod 444 (immutable)
- No new attack surface — token is read-only from existing config
- `command openclaw` bypass preserves all non-configure functionality

Fixes #1114

Signed-off-by: Dongni Yang <dongniy@nvidia.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Signed-off-by: Dongni Yang <dongniy@nvidia.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
gemini2026 pushed a commit to gemini2026/NemoClaw that referenced this pull request Apr 14, 2026
…aths (NVIDIA#1519)

## Summary

- Add symlinks for `exec-approvals.json`, `telegram/`, and
`credentials/` from `.openclaw/` to `.openclaw-data/`
- These paths are written by OpenClaw at runtime but had no writable
target, causing EACCES errors
- Follows the exact same pattern as the 11 existing symlinks in
`Dockerfile.base`

## Security posture

Unchanged. The `.openclaw/` directory remains root-owned (755), all
symlinks are root-owned and `chattr +i` locked at runtime by
`nemoclaw-start.sh`. The agent can only write file contents through the
symlinks to sandbox-owned `.openclaw-data/`.

## Fixes

- Fixes NVIDIA#1027 — `exec-approvals.json` EACCES
- Refs NVIDIA#975 — Telegram offset persistence EACCES
- Refs NVIDIA#1114 — WhatsApp credentials EACCES

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

## Summary by CodeRabbit

* **Chores**
* Extended Docker image filesystem with new data storage directories for
telegram and credentials management.
  * Added approvals configuration file to the Docker environment setup.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
gemini2026 pushed a commit to gemini2026/NemoClaw that referenced this pull request Apr 14, 2026
…IA#1114) (NVIDIA#1305)

## Summary

Fixes the four issues reported in NVIDIA#1114 — EACCES permission errors and
missing gateway token when running inside the NemoClaw sandbox.

### Issue mapping

| # | Reported error | Fix |
|---|----------------|-----|
| 1 | `EACCES: open '/sandbox/.openclaw/openclaw.json.*.tmp'` |
`install_configure_guard` — intercepts `openclaw configure` with a clear
error and directs users to `nemoclaw onboard --resume` on the host |
| 2 | Same as NVIDIA#1 (different PID) | Same fix |
| 3 | `EACCES: mkdir '/sandbox/.openclaw/credentials'` | Already
resolved on main via NVIDIA#1519 (credentials symlink to `.openclaw-data/`) |
| 4 | No WhatsApp QR code | Consequence of NVIDIA#3, also resolved by NVIDIA#1519 |

### Root cause (issues 1 & 2)

OpenClaw's `configure` command performs atomic writes — it creates a
temp
file (`openclaw.json.PID.UUID.tmp`) in the same directory as the config.
Since `/sandbox/.openclaw/` is Landlock read-only at the kernel level,
file creation is rejected with EACCES. This is by design: the sandbox
config is intentionally immutable at runtime.

Rather than weakening Landlock (security regression), we intercept the
command in the sandbox shell and guide users to the correct host-side
workflow.

### Changes

**1. `install_configure_guard()`** — Writes a shell function wrapper to
`.bashrc`/`.profile` that intercepts `openclaw configure` and prints:
```
Error: 'openclaw configure' cannot modify config inside the sandbox.
The sandbox config is read-only (Landlock enforced) for security.

To change your configuration, exit the sandbox and run:
  nemoclaw onboard --resume

This rebuilds the sandbox with your updated settings.
```
All other `openclaw` subcommands pass through to the real binary.

**2. `export_gateway_token()`** — Reads `gateway.auth.token` from
`openclaw.json` and exports it as `OPENCLAW_GATEWAY_TOKEN`, so
interactive sessions (`openshell sandbox connect`) can authenticate
with the gateway. Persists to `.bashrc`/`.profile` using idempotent
marker blocks and cleans stale tokens on revocation.

**3. `_read_gateway_token()` helper** — Shared Python snippet used by
both `export_gateway_token` and `print_dashboard_urls` (deduplication,
uses `with open()` context manager).

All three are called in both root and non-root startup paths.

## Security properties preserved

- `/sandbox/.openclaw` remains root-owned, Landlock read-only
- `openclaw.json` remains chmod 444 (immutable)
- No new attack surface — token is read-only from existing config
- `command openclaw` bypass preserves all non-configure functionality

Fixes NVIDIA#1114

Signed-off-by: Dongni Yang <dongniy@nvidia.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Signed-off-by: Dongni Yang <dongniy@nvidia.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
TonyLuo-NV added a commit to TonyLuo-NV/NemoClaw that referenced this pull request Apr 23, 2026
, NVIDIA#514)

The original code fix for GatewayRequestError EACCES on exec-approvals.json
landed via NVIDIA#1519, which added just the two symlink/touch lines in
Dockerfile.base without any regression tests. This adds static assertions
on the image-build sources so a future refactor that drops the
provisioning steps fails fast in CI, before a docker build runs.

Covers:
- NVIDIA#1027/NVIDIA#1519: exec-approvals.json + update-check.json backing files and
  symlinks in Dockerfile.base (including ordering: data file created
  before the symlink that points at it).
- NVIDIA#514: openclaw.json stays mode 0444, .config-hash stays root:root 0444,
  and /sandbox/.openclaw/ itself stays root:root 0755 so the agent cannot
  replace symlinks or forge an integrity hash.

Signed-off-by: Tony Luo <xialuo@nvidia.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ericksoa added a commit that referenced this pull request Apr 24, 2026
) (#1236)

## Summary

Reboot of this PR after #1519 merged the actual code fix for issue
#1027. This branch is now **test-only** — it adds static regression
guards so a future refactor that drops the provisioning steps fails fast
in CI, before a docker build runs.

The original #1236 carried the code fix alongside tests; since upstream
already has the fix from #1519, I rebased to upstream/main and kept only
the missing regression coverage.

## What this adds

`test/sandbox-provisioning.test.ts` — 8 static assertions on the
image-build sources:

**#1027 / #1519 (runtime-writable symlinks)**
- `Dockerfile.base` creates the `exec-approvals.json` backing file in
`.openclaw-data`
- `Dockerfile.base` symlinks `.openclaw/exec-approvals.json` →
`.openclaw-data/exec-approvals.json`
- Same pair for `update-check.json`
- Ordering guard: the data file is created before the symlink that
points at it

**#514 (root-owned read-only config)**
- `Dockerfile` keeps `openclaw.json` at mode 0444 (agent cannot tamper
with auth token / CORS)
- `Dockerfile` keeps `.config-hash` at root:root 0444 (agent cannot
forge integrity hash)
- `Dockerfile` keeps `.openclaw/` at root:root 0755 (agent cannot
replace symlinks)

## Why these tests matter

- The existing `test/exec-approvals-path-regression.test.ts` only
verifies the path-patching grep logic in `Dockerfile.base`, not the
actual symlink/data file creation
- The existing `test/e2e-gateway-isolation.sh` catches regressions at
image-build time, but requires docker and a full build; static tests
fail in seconds on every PR
- #1519 was a 6-line code fix with zero test coverage. If a future
cleanup of `Dockerfile.base` drops those six lines, the issue returns
silently

## Test plan

- [x] `npx vitest run test/sandbox-provisioning.test.ts` — 8/8 passing
- [x] Rebased onto current `upstream/main` (`d9aced49`)
- [x] No code changes; test-only PR

Signed-off-by: Tony Luo <xialuo@nvidia.com>

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

Signed-off-by: Tony Luo <xialuo@nvidia.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Aaron Erickson 🦞 <aerickson@nvidia.com>
DemianHeyGen pushed a commit to DemianHeyGen/NemoClaw that referenced this pull request Apr 30, 2026
, NVIDIA#514) (NVIDIA#1236)

## Summary

Reboot of this PR after NVIDIA#1519 merged the actual code fix for issue
NVIDIA#1027. This branch is now **test-only** — it adds static regression
guards so a future refactor that drops the provisioning steps fails fast
in CI, before a docker build runs.

The original NVIDIA#1236 carried the code fix alongside tests; since upstream
already has the fix from NVIDIA#1519, I rebased to upstream/main and kept only
the missing regression coverage.

## What this adds

`test/sandbox-provisioning.test.ts` — 8 static assertions on the
image-build sources:

**NVIDIA#1027 / NVIDIA#1519 (runtime-writable symlinks)**
- `Dockerfile.base` creates the `exec-approvals.json` backing file in
`.openclaw-data`
- `Dockerfile.base` symlinks `.openclaw/exec-approvals.json` →
`.openclaw-data/exec-approvals.json`
- Same pair for `update-check.json`
- Ordering guard: the data file is created before the symlink that
points at it

**NVIDIA#514 (root-owned read-only config)**
- `Dockerfile` keeps `openclaw.json` at mode 0444 (agent cannot tamper
with auth token / CORS)
- `Dockerfile` keeps `.config-hash` at root:root 0444 (agent cannot
forge integrity hash)
- `Dockerfile` keeps `.openclaw/` at root:root 0755 (agent cannot
replace symlinks)

## Why these tests matter

- The existing `test/exec-approvals-path-regression.test.ts` only
verifies the path-patching grep logic in `Dockerfile.base`, not the
actual symlink/data file creation
- The existing `test/e2e-gateway-isolation.sh` catches regressions at
image-build time, but requires docker and a full build; static tests
fail in seconds on every PR
- NVIDIA#1519 was a 6-line code fix with zero test coverage. If a future
cleanup of `Dockerfile.base` drops those six lines, the issue returns
silently

## Test plan

- [x] `npx vitest run test/sandbox-provisioning.test.ts` — 8/8 passing
- [x] Rebased onto current `upstream/main` (`d9aced49`)
- [x] No code changes; test-only PR

Signed-off-by: Tony Luo <xialuo@nvidia.com>

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

Signed-off-by: Tony Luo <xialuo@nvidia.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Aaron Erickson 🦞 <aerickson@nvidia.com>
@cv cv added the integration: whatsapp WhatsApp integration or channel behavior label May 30, 2026
@wscurran wscurran added the bug-fix PR fixes a bug or regression label Jun 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug-fix PR fixes a bug or regression integration: whatsapp WhatsApp integration or channel behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OpenClaw GatewayRequestError — EACCES permission denied

3 participants