Skip to content

fix(sandbox): mount the guest bridge outside the read-only /opt - #218

Merged
quickbeard merged 1 commit into
mainfrom
sandbox-bridge-mount
Aug 13, 2026
Merged

quickbeard merged 1 commit into
mainfrom
sandbox-bridge-mount

Conversation

@quickbeard

Copy link
Copy Markdown
Owner

Problem

Every bwrap sandbox (Linux) fails to start immediately:

sandbox failed to start {
  id: 'sandbox:codev-work',
  message: "sandbox server exited during startup (code=1 signal=null): bwrap: Can't mkdir /opt/codev-bridge: Read-only file system"
}

bwrap creates missing mount points, but only where it can write: its own tmpfs root, never inside a read-only bind. GUEST_BRIDGE was /opt/codev-bridge, and /opt is --ro-bind-mounted from the image rootfs a few args earlier, so the mount point can only exist if the image already ships it — and it doesn't. image/Dockerfile creates /workspace /opt/codev /codev-config only.

/opt/codev works precisely because the image ships it. Every other mount (/workspace, /run/codev, /home/codev) lands on a path bwrap builds on its tmpfs root, since those names are in MANAGED_ROOTFS_ENTRIES and are never bound from the rootfs. The bridge was the one destination that was neither.

Fix

Move GUEST_BRIDGE to a top-level /codev-bridge. CODEV_CONFIG and both forward.mjs invocations follow the same constant, so they move with it.

Host-side only, which matters here: it works with already-downloaded images. Adding the directory to the Dockerfile instead would need both arches rebuilt, republished, and every existing sandbox reset, for the same result.

Verification

Ran the real provider against an existing sandbox and the dev server bundle on Ubuntu 22.04:

READY http://127.0.0.1:44833 codev
health 200 {"healthy":true,"version":"local"}
path   200 {"home":"/home/codev","worktree":"/workspace","directory":"/workspace", ...}

The bridge mount was the only blocker — the guest boots, the server starts, and the workspace maps correctly.

bun test (94 pass, 0 fail) and bun typecheck clean in packages/codev-sandbox.

Why tests missed it

bwrap.integration.test.ts uses the host's / as the rootfs but filters entries to an allowlist that excludes opt. With no /opt bind present, bwrap creates /opt/codev-bridge on the tmpfs root and the test passes — a case the real image hits and the test rootfs structurally cannot.

The added test in args.test.ts covers the invariant directly: no mount destination may nest inside a read-only rootfs bind. Confirmed it fails on the old path and passes on the new one.

Notes

No DIVERGENCES.md row: packages/codev-sandbox is fork-only. CHANGELOG.md gets an Unreleased entry since Linux sandboxes were documented as working.

🤖 Generated with Claude Code

@quickbeard
quickbeard force-pushed the sandbox-bridge-mount branch 2 times, most recently from 1ba7342 to 3856a9e Compare August 13, 2026 06:58
Every bwrap sandbox failed to start with "Can't mkdir /opt/codev-bridge:
Read-only file system". bwrap creates missing mount points only where it
can write — its own tmpfs root, never inside a read-only bind — and /opt
comes from the image rootfs, which ships /opt/codev but no bridge dir.

Move GUEST_BRIDGE to a top-level /codev-bridge, so bwrap creates it on the
tmpfs root alongside /run/codev and /workspace. Host-side only: existing
sandboxes start as they are, with no image republish or re-create.

The integration test missed this because its host-rootfs allowlist excludes
/opt, so the destination was always created on the tmpfs root; cover the
invariant directly instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@quickbeard
quickbeard force-pushed the sandbox-bridge-mount branch from 3856a9e to 1c94826 Compare August 13, 2026 07:35
@quickbeard
quickbeard merged commit f4ba4f7 into main Aug 13, 2026
0 of 5 checks passed
@quickbeard
quickbeard deleted the sandbox-bridge-mount branch August 13, 2026 07:39
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.

1 participant