Claude Code in bubblewrap
Find a file
2026-06-01 14:46:03 -04:00
cwrap.sh Added --new-session flag and updated usage notes on readme 2026-06-01 14:32:59 -04:00
README.md Readme updates and formatting improvements 2026-06-01 14:46:03 -04:00

cwrap - Claude Code in bubblewrap

Run Claude Code confined to a single project folder using bubblewrap (bwrap).

The script launches Claude inside an unprivileged Linux namespace that starts from a fresh, empty home directory (a tmpfs). Nothing in your real $HOME is visible to the agent. Only a handful of paths are bound back in (mostly read-only) and the only two writable locations are the project folder and ~/.claude.

Because you launch the jail from outside, the agent cannot escape it by changing its own settings. You may also want to consider using the built-in /sandbox for network ACL in conjunction with this script.

Pronunciation

The project name is pronounced as you would expect: "crap" (/kræp/) — but we'll also accept "cwap" (/kwæp/)

What's isolated

Everything not explicitly bound does not exist inside the sandbox. For example: ~/.ssh, browser profiles, other projects, other users' home directories, and so on.

Read-only / fresh isolated

Bound in but not modifiable, or replaced with a fresh per-sandbox copy:

Path Access Why
/usr, /etc, /lib*, /bin, /sbin read-only system tree needed to run
/proc, /dev, /tmp fresh / isolated per-sandbox, not the host's
/etc/resolv.conf target (e.g. /run/systemd/resolve/...) read-only DNS — the real file behind the symlink, so name resolution works
the claude binary (e.g. ~/.local/share/claude/...) read-only just Claude's own install, not all of ~/.local
/usr/local read-only host tools the agent may run

Read-write

The only two locations the agent can modify:

Path Access Why
~/.claude, ~/.claude.json read-write Claude's settings, credentials, session state
the project folder read-write the code you're working on

Networking is kept (--share-net) so Claude can reach the API; all other namespaces are dropped (--unshare-all). The environment is cleared (--clearenv) so no host secrets leak in, and the sandbox dies with the launching script (--die-with-parent). The sandbox also runs in a fresh session (--new-session), which blocks the TIOCSTI input-injection escape (CVE-2017-5226); the tradeoff is that job control is unavailable inside the sandbox.

Requirements

Usage

./cwrap.sh [--allow-settings-edit] [PROJECT_DIR] [-- <args passed to claude>]

The first non-flag argument is the project directory (defaults to the current directory). --allow-settings-edit may appear before or after it (see Settings lockdown). Anything after -- is forwarded verbatim to claude.

Examples

./cwrap.sh                          # sandbox the current directory
./cwrap.sh ~/code/myrepo            # sandbox a specific folder
./cwrap.sh ~/code/myrepo -- --resume   # pass --resume through to claude
./cwrap.sh ~/code/myrepo -- --dangerously-skip-permissions # not as dangerous
./cwrap.sh --allow-settings-edit   # let the agent edit settings.json

Shell Alias

A simple shell alias can be used:

~/.bashrc:

# Add an alias to where you cloned the project to
alias cwrap='/home/user/projects/cwrap/cwrap.sh'

Then you can call cwrap.sh like:

cwrap
cwrap ~/code/myrepo
cwrap ~/code/myrepo -- --resume
cwrap ~/code/myrepo -- --dangerously-skip-permissions

Authentication

Credentials stored in ~/.claude work inside the sandbox without extra setup. If you authenticate with an API key instead, ANTHROPIC_API_KEY is passed through automatically when it is set in your environment. Other environment variables are not inherited — add them explicitly in the script if you genuinely need them.

Settings lockdown

A self-modification guard re-binds ~/.claude/settings.json read-only on top of the writable ~/.claude mount, preventing the agent from rewriting its own config. It is enabled by default; pass --allow-settings-edit if you need the agent to edit settings.json.

Usage Notes

What the agent can do is intentionally limited. We work in a variety of different types of environments, so we're keeping those specifics out of our sandbox as it exposes a variety of surface areas we don't think are manageable.

Only a minimal set of paths are bound in, so dev tooling that needs your host config or credentials to properly function — git (commits, signing), package managers (private-registry auth), and similar auxiliary tasks — should generally be run outside the sandbox from your normal shell.

System-tools are generally available, while per-user tools (like nvm) won't be available to the agent. This is intentional for our use case.

"Yeah, well, you know, that's just, like, [my] opinion, man." —The Dude [writing this]