cco (Claude Condom if you're so inclined) provides essential protection while Claude Code is up close and personal with your system. It automatically selects the best available sandboxing method - using native OS sandboxing (sandbox-exec on macOS, bubblewrap on Linux) when available, or falling back to Docker as a barrier to keep Claude contained while keeping your real system safe.
UPDATE: cco can now sandbox OpenAI's codex as well! Just start it with cco codex to don your codex condom! (OpenCode, Oh My Pi, Pi coding agent, and factory.ai's droid are supported as well.)
Running Claude Code with --dangerously-skip-permissions feels great - fast, responsive, no interruptions. But going in unprotected has risks: web search makes Claude vulnerable to prompt injections that could trick it into accessing files outside your project or running unexpected commands.
cco lets you have it both ways: all the pleasure of autonomous Claude, with a barrier between Claude and your machine's sensitive areas.
For more information about cco's security model, limitations, and threat analysis, see SECURITY.md.
curl -fsSL https://raw.githubusercontent.com/nikvdp/cco/master/install.sh | bash
Just type cco where you would normally type claude and your protection is on!
cco "write a hello world script"
cco "help me refactor this code"
cco gets out of your way. It's designed to feel natural - like using Claude directly, just safer.
claude but protectedYou should barely notice cco is there, except for that reassuring feeling of safety.
cco runs Claude Code inside a sandboxed environment. It automatically detects and uses the best available sandboxing method:
Native sandboxing (default when available): Uses OS-native tools (sandbox-exec on macOS, bubblewrap on Linux) for lightweight, fast isolation. Note: Native sandboxing exposes the entire host filesystem as read-only by default - use --safe for stronger filesystem isolation.
Docker sandboxing (fallback): Uses Docker containers when native tools aren't available. Provides stronger filesystem isolation by only exposing explicitly mounted paths.
Automatic sandbox selection: Chooses native OS sandboxing when available, Docker as fallback
Native sandbox (preferred): Lightweight, fast startup, direct Keychain access on macOS. Exposes host filesystem read-only by default.
Docker sandbox (fallback): Stronger filesystem isolation with container-only filesystem when native tools unavailable
Host file access: Your project files and Claude state paths are accessible so Claude can read and edit what it needs
Git worktree support: Automatically detects git worktrees and whitelists the main repo's .git directory so git operations work seamlessly
Network access: Full host network access for localhost development servers, MCP servers, and web requests
Credential management: Authentication is handled securely without exposing host credentials
Enhanced features: Background tasks enabled by default for improved code analysis and autonomous development (Docker mode)
Full toolchain: Docker container includes development tools, languages, and utilities Claude needs
The result? Claude gets the --dangerously-skip-permissions experience it needs to be productive, while potential risks are contained within the sandbox.
There are several alternatives for running Claude Code in containers:
Here's why cco is the better choice for developers who want simplicity and seamless integration:
cco "help me code" - that's it. Devcontainers require VS Code setup, configuration files, and "Reopen in Container"--resume, --model, etc.) without configuration| Feature | cco | claudebox | devcontainer |
|---|---|---|---|
| Setup complexity | One command install | Multi-step setup, profiles | VS Code + config files |
| IDE dependency | None | None | VS Code required |
| Startup time | Instant | Slower (profile builds) | Container rebuild delays |
| macOS Keychain | Automatic | Manual setup | Manual setup |
| Terminal resizing | Automatic (SIGWINCH passthrough) | Unknown | Limited |
| Configuration | Zero config | Profile management | devcontainer.json |
| Development profiles | None needed | 15+ profiles | Basic |
| Project isolation | Basic | Advanced (per-project images) | Basic |
| Philosophy | Invisible simplicity | Feature-rich environment | IDE integration |
Choose devcontainer if you:
Choose claudebox if you:
Choose cco if you:
cco isn't trying to be a development environment - it's trying to be invisible protection that lets you use Claude Code exactly as intended, just safely.
curl -fsSL https://raw.githubusercontent.com/nikvdp/cco/master/install.sh | bash
git clone https://github.com/nikvdp/cco.git
cd cco
chmod +x cco
sudo ln -s "$PWD/cco" /usr/local/bin/cco
# Interactive session
cco
# Direct commands
cco "analyze this codebase"
cco --resume # Claude Code option passed through
# Get help
cco --help
# Force a specific sandbox backend
cco --backend native # Use native sandbox (sandbox-exec/bubblewrap)
cco --backend docker # Use Docker sandbox
cco --backend auto # Auto-detect (default)
# Rebuild the protective layer (Docker mode only, also updates to latest Claude Code version)
cco --rebuild
# Pull the latest pre-built image and automatically remove older cco image tags
cco --pull --clean-old-images
# System information and status
cco --info
# Shell access for inspecting the container environment
cco shell
cco shell 'ls -la' # Run shell commands inside the cco container
# Custom environment
cco --env API_KEY=sk-123
# Additional apt packages
cco --packages terraform,kubectl
# Auto-accept startup recovery prompts
cco --yes "review this repo"
# Enable Docker access
cco --docker-socket
# Run Docker sessions from a custom image
cco --backend docker --image my-cco-snapshot:good --persist
# Reuse the default repo-scoped Docker container filesystem across runs
cco --persist
# Select a specific persistent session name for this repo
cco --persist=frontend
cco --persist data-migration
# Attach directly to an existing Docker container by name or ID
cco --persist-container my-devbox
cco --persist-container 4d2b9f8c1a6e
# Update cco installation
cco self-update
# Clean up containers
cco cleanup
# Safe mode (native sandbox): hide $HOME for stronger isolation (experimental)
cco --safe
# Share directories read-only or hide them
cco --add-dir ~/configs:ro
cco --allow-readonly ~/.ssh
cco --deny-path ~/Downloads
cco also honors Claude Code's local project settings file at .claude/settings.local.json. If that file contains an additionalDirectories array, those directories are mounted read/write the same way as --add-dir PATH:rw. cco parses that file with python3 when available and falls back to jq; if neither tool exists it prints a warning and skips those extra mounts.
--docker-socket (experimental): Binds the host Docker socket into the sandbox so Claude can control Docker on your machine. This defeats the isolation barrier—avoid unless you explicitly need host Docker access.--image IMAGE / --docker-image IMAGE (Docker only): Runs cco against a specific Docker image instead of the default managed cco:latest image. This is useful if you docker commit a known-good persistent container yourself and want later cco runs to start from that image. With --pull, cco pulls the chosen image first.--clean-old-images (Docker only): After a successful pre-built image pull, remove older ghcr.io/nikvdp/cco:* tags automatically. Without this flag, cco asks before removing old managed image tags when it can prompt.--force-docker-bridge-network (Docker only): Force bridge networking instead of host networking. By default cco uses --network=host when available (Linux, OrbStack). Use this if you need port isolation or want explicit -p port forwarding.--yes / -y: Auto-accept startup recovery prompts such as macOS Keychain unlock before cco starts. OAuth maintenance is automatic when needed and is not controlled by --yes.--allow-oauth-refresh (experimental): Gives the container write access to your Claude credentials so refreshed tokens sync back to the host. Malicious prompts could corrupt or replace those credentials.--persist (Docker only, opt-in): Reuses the default persistent container for the current repo instead of starting fresh each run. cco starts it for the invocation and stops it again when the run ends.--persist=NAME or --persist NAME: Selects a specific persistent session for the current repo so you can keep multiple reusable container filesystems side by side.--persist-container TARGET: Attaches to an existing Docker container by name or ID instead of using cco's managed session naming.shell, use bare --persist shell ... exactly as before for the default session.cco will not automatically broaden mounts for later worktrees, so reuse fails clearly if the chosen container does not already expose the current path.Use --persist or --persist NAME when you want cco to manage the session for a repo. Use --persist-container TARGET when you already know the exact container you want to attach to and want that choice to win over cco's naming logic.
Use --image IMAGE when you want cco to run against a custom base image, for example after docker commit <container> my-cco-snapshot:good. Custom image overrides are not compatible with --rebuild or --packages, because those flags only make sense for the default cco-managed image path.
--safe (native only, experimental): Provides stronger filesystem isolation by hiding your entire $HOME directory from Claude. Only the project directory and explicitly shared paths remain visible. Trade-off: Increased security but may cause some tools to fail if they need access to configuration files in $HOME. Use --allow-readonly to selectively expose needed paths.--allow-readonly PATH: Share extra files or directories read-only inside the sandbox.--deny-path PATH: Deny read/list/write access to a path so it is fully inaccessible to Claude.You can create exceptions within denied paths by combining --deny-path with --allow-readonly or --add-dir:
# Deny all of /run but allow access to a specific socket
cco --deny-path /run --allow-readonly /run/user/1000/pipewire-0
# Deny a config directory but allow a specific subdirectory
cco --deny-path ~/.config --add-dir ~/.config/myapp
Precedence rules:
--)Arguments after -- are passed directly to the underlying sandbox backend (Docker, bwrap, or sandbox-exec). This enables advanced configuration like port forwarding without cco needing explicit flags for every option:
# Forward ports for development servers (Docker mode)
cco -- -p 3000:3000 # Forward port 3000
cco -- -p 3000:3000 -p 8080:8080 # Forward multiple ports
cco -- -e "MESSAGE=hello world" # Set env with spaces
Shell quoting is preserved - each argument after -- remains a separate argv element.
CCO_SANDBOX_ARGS_FILE)For persistent configuration, set CCO_SANDBOX_ARGS_FILE to point to a file containing sandbox backend arguments (one per line):
export CCO_SANDBOX_ARGS_FILE=~/.config/cco/sandbox-args
Example file (~/.config/cco/sandbox-args):
# Forward dev server port
-p
3000:3000
# Forward database port
-p
5432:5432
# Custom environment
-e
DATABASE_URL=postgres://localhost/mydb
Format rules:
# are commentscco acts as a wrapper - any options it doesn't recognize get passed directly to Claude Code:
# These Claude Code options work normally
cco --resume
cco --model claude-3-5-sonnet-20241022 "write tests"
cco --no-clipboard "analyze this file"
cco --permission-mode auto "work through this task"
# Mix cco and Claude options
cco --env DEBUG=1 --resume # `cco` + Claude options
When you pass a Claude permission mode directly, cco leaves it alone instead of adding its default bypass flag. If your trusted Claude settings set permissions.defaultMode to auto and Auto Mode is not disabled, cco also lets that default apply.
--command)You can use cco as a generic sandbox wrapper for any CLI, not just Claude. This is helpful when you want a tool to run with full autonomy inside a contained environment.
# Run a shell inside the sandbox
cco --command "bash"
# Run a custom program in the sandbox
cco --command "python3" - <<'PY'
print("hello from inside cco")
PY
# With additional project directories mounted read/write
cco --add-dir ~/work/secrets --command "ripgrep TODO"
Notes:
--command replaces the Claude invocation; all following args are passed to your command as-is.cco does not add Claude-specific flags to your command (e.g., it won’t append --dangerously-skip-permissions).--allow-readonly / --deny-path / --add-dir ...:ro to tighten what your command can read or modify.cco codex)cco codex is a convenience for running OpenAI’s Codex CLI with maximum autonomy while keeping it contained by cco.
What it does:
codex --dangerously-bypass-approvals-and-sandbox so codex won’t prompt for permissions and can act autonomously.~/.codex into the sandbox so codex can access it.cco (native Seatbelt/bubblewrap or Docker), so codex cannot escape mounted paths even with bypass flags.Important runtime note:
cco executes tools as the mapped non-root container user with HOME set to that user's home.docker exec sessions as root are unsupported for normal auth/config behavior. If you must run commands manually, use docker exec -u hostuser ... (or -u <HOST_UID>:<HOST_GID>).Examples:
# Start Codex inside cco's sandbox
cco codex "build and run a small demo"
# Equivalent manual form using --command (when ~/.codex exists)
cco --add-dir ~/.codex --command "codex --dangerously-bypass-approvals-and-sandbox" "build and run a small demo"
# Add extra directories Codex should be able to access
cco --add-dir ~/.codex --command "codex --dangerously-bypass-approvals-and-sandbox" "analyze this project"
Security note: --dangerously-bypass-approvals-and-sandbox applies to Codex’s internal permission checks, not to cco. The cco sandbox still constrains filesystem access to your project and explicitly mounted paths. Network access remains unrestricted by design.
cco auto <agent>)cco auto <agent> runs an agent in its native "ask me when it matters" mode instead of the default full-bypass mode:
cco auto claude runs Claude with --permission-mode auto instead of --dangerously-skip-permissions.cco auto codex runs Codex with --ask-for-approval on-request instead of --dangerously-bypass-approvals-and-sandbox; because it no longer uses the bypass flag, Codex's own sandbox may also apply.gemini, droid, opencode, omp, pi) it behaves exactly like running the agent normally.In every case cco's sandbox still applies. For Claude, auto mode only changes Claude's permission posture; for Codex, replacing the bypass flag also lets Codex enforce its own sandbox unless your Codex configuration or arguments say otherwise.
cco auto claude "refactor this module"
cco auto codex "build and run the tests"
cco uses host-based networking so that MCP (Model Context Protocol) servers or other tools you may have running on localhost are accessible to cco.
host.docker.internal bridge - may require MCP server reconfigurationIf you're using MCP servers with localhost addresses and they're not accessible, consider:
host.docker.internal instead of localhostStdio MCP servers run as child processes of Claude Code, so they inherit the selected sandbox's filesystem access.
Native sandbox: Host-installed MCP binaries are available, but the host filesystem is read-only outside the project and CCO's built-in state paths. If a server writes a cache or database elsewhere, explicitly share only that directory read/write with --add-dir:
mkdir -p ~/.cache/codebase-memory-mcp
cco --add-dir ~/.cache/codebase-memory-mcp "help me code"
With native --safe, also expose a host-installed binary's directory read-only if it is under the otherwise hidden home directory:
cco --safe \
--allow-readonly ~/.local/bin \
--add-dir ~/.cache/codebase-memory-mcp \
"help me code"
Prefer the narrow cache directory over making all of ~/.cache writable.
Docker sandbox: The MCP server must be installed inside the Linux container. A macOS host binary cannot run inside a Linux container, even if its path is mounted. For a one-time installation that persists across sessions, use a named persistent container:
cco --backend docker --persist=stdio-mcp shell
# Install the Linux MCP server inside the shell, then exit.
cco --backend docker --persist=stdio-mcp "help me code"
The MCP command configured in Claude must resolve inside the container; prefer a command on the container's PATH instead of an absolute host path. Alternatively, use --packages for software available through apt, or build a custom image and select it with --image.
cco passes through everything you need:
ANTHROPIC_API_KEY - Direct accessCLAUDE_CODE_OAUTH_TOKEN - Externally managed Claude OAuth token; skips local credential checks and startup refreshTERM, NO_COLOR)--disable-background-tasks to turn off)# Use .env files
echo "DEBUG=1" > .env
cco
Blank lines, comments, whitespace-prefixed comments, and non-assignment lines in .env are ignored so shell-style formatting does not break startup.
claude and login)cco automatically finds your Claude credentials:
~/.claude/.credentials.json or config directoryANTHROPIC_API_KEY and CLAUDE_CODE_OAUTH_TOKEN passed through in both modesUnderstanding the filesystem isolation differences between sandbox modes:
| Sandbox Mode | Host Filesystem Visibility | Isolation Level | Performance | Use Case |
|---|---|---|---|---|
| Docker | Only mounted paths | Strongest | Slower startup | Maximum security |
| Native | Entire host (read-only) | Basic | Fastest startup | Development convenience |
Native + --safe | Only project + whitelisted | Strong | Fast startup | Security + performance balance (experimental) |
Key Points:
--safe flag (experimental): Available only with native sandboxing. Hides your $HOME directory entirely while keeping fast native performance. May cause some tools to fail if they require access to dotfiles or configuration in $HOME.Security recommendation: Use Docker mode for maximum filesystem isolation, or try --safe with native sandboxing for better security with fast performance (experimental - may require --allow-readonly for some tools).
--safe: Only project and whitelisted paths visible (stronger isolation)cd my-project
cco
cco "add tests to the auth module"
cco --resume
export ANTHROPIC_API_KEY=sk-key
cco "review this pull request"
⚠️ These features are experimental and may have edge cases. Use with caution.
# OAuth token sync-back (EXPERIMENTAL)
# Lets sandboxed Claude sync refreshed credentials back to the host
cco --allow-oauth-refresh "help me code"
# Credential management (EXPERIMENTAL)
# Backup and restore Claude Code credentials for safety
cco backup-creds # Backup current credentials
cco restore-creds # Restore from most recent backup
cco restore-creds backup-file.json # Restore from specific backup
OAuth refresh feature: When the selected backend cannot safely persist an in-sandbox refresh, cco repairs already-expired Claude OAuth credentials with a fixed host-side Claude call before startup. If stored credentials are still valid but expire within two hours, cco starts Claude normally and runs the host-side refresh in the background. The experimental --allow-oauth-refresh mode is different: it enables bidirectional credential sync when sandboxed Claude refreshes expired tokens. It uses race condition protection and creates automatic backups.
Credential management: Provides manual backup/restore of Claude Code credentials with cross-platform support (macOS Keychain + Linux files).
Authentication issues
claude first to authenticateccocco will offer to unlock your login keychain if it cannot read Claude credentials. Use --yes to auto-accept that recovery step.Token expiration
cco checks stored OAuth expiry before sandbox startup. When the selected backend cannot safely persist an in-sandbox refresh, already-expired credentials are repaired before Claude starts. Valid credentials that expire within two hours are refreshed in the background while startup continues.claude directly (outside cco) to re-authenticate, then retry with cco--allow-oauth-refresh flag will sync container credentials back to your host. Only use it if you accept the additional credential tampering risk.Docker problems
docker infoPermission errors
cco handles user mapping automaticallycco --rebuild if neededExperimental features not working
--allow-oauth-refresh) is beta, reduces credential isolation, and may have issuesclaude if the startup refresh cannot recover credentialscco backup-creds / cco restore-credsToken expires during active session
If Claude stops responding with API errors during an active cco session, your OAuth token has likely expired mid-session. cco normally refreshes valid tokens that expire within two hours in the background during startup, but a long-running session can still cross the expiry boundary.
Root cause: Some sandbox backends cannot safely persist Claude's refreshed OAuth credentials back to the host credential store without granting broader credential or home-directory write access. cco keeps the sandbox narrow, repairs already-expired credentials before startup, and performs background host-side refresh for valid credentials that are close to expiry.
Workaround:
claude (outside cco)/login to re-authenticatecco sessioncco --resume to pick up the refreshed credentialsFor repeated startup failures, run plain claude once to confirm the account can refresh or re-login outside the sandbox, then start cco again.
Stdio-based MCP server cannot start or write its cache
In native mode, expose the server's cache or state directory with --add-dir. In Docker mode, install a Linux-compatible server inside the container using --persist, --packages, or a custom --image. See MCP Server Support for examples.
cco shell # Get inside for inspection
cco --info # Check system status
cco provides protection primarily through filesystem isolation and credential management. Important limitations to understand:
cco protects against:cco does NOT protect against:Network access is intentionally unrestricted to support MCP servers and maintain Claude's full functionality. The primary security benefits come from filesystem and credential isolation, not network isolation.
For detailed security information, threat model, and limitations, see SECURITY.md.
Practice safe computing.
Pull requests welcome! Please maintain all safety mechanisms.
MIT License
Shell
92.1%
C
3.2%
Dockerfile
2.0%
Python
1.8%
cco (Claude Condom if you're so inclined) provides essential protection while Claude Code is up close and personal with your system. It automatically selects the best available sandboxing method - using native OS sandboxing (sandbox-exec on macOS, bubblewrap on Linux) when available, or falling back to Docker as a barrier to keep Claude contained while keeping your real system safe.
UPDATE: cco can now sandbox OpenAI's codex as well! Just start it with cco codex to don your codex condom! (OpenCode, Oh My Pi, Pi coding agent, and factory.ai's droid are supported as well.)
Running Claude Code with --dangerously-skip-permissions feels great - fast, responsive, no interruptions. But going in unprotected has risks: web search makes Claude vulnerable to prompt injections that could trick it into accessing files outside your project or running unexpected commands.
cco lets you have it both ways: all the pleasure of autonomous Claude, with a barrier between Claude and your machine's sensitive areas.
For more information about cco's security model, limitations, and threat analysis, see SECURITY.md.
curl -fsSL https://raw.githubusercontent.com/nikvdp/cco/master/install.sh | bash
Just type cco where you would normally type claude and your protection is on!
cco "write a hello world script"
cco "help me refactor this code"
cco gets out of your way. It's designed to feel natural - like using Claude directly, just safer.
claude but protectedYou should barely notice cco is there, except for that reassuring feeling of safety.
cco runs Claude Code inside a sandboxed environment. It automatically detects and uses the best available sandboxing method:
Native sandboxing (default when available): Uses OS-native tools (sandbox-exec on macOS, bubblewrap on Linux) for lightweight, fast isolation. Note: Native sandboxing exposes the entire host filesystem as read-only by default - use --safe for stronger filesystem isolation.
Docker sandboxing (fallback): Uses Docker containers when native tools aren't available. Provides stronger filesystem isolation by only exposing explicitly mounted paths.
Automatic sandbox selection: Chooses native OS sandboxing when available, Docker as fallback
Native sandbox (preferred): Lightweight, fast startup, direct Keychain access on macOS. Exposes host filesystem read-only by default.
Docker sandbox (fallback): Stronger filesystem isolation with container-only filesystem when native tools unavailable
Host file access: Your project files and Claude state paths are accessible so Claude can read and edit what it needs
Git worktree support: Automatically detects git worktrees and whitelists the main repo's .git directory so git operations work seamlessly
Network access: Full host network access for localhost development servers, MCP servers, and web requests
Credential management: Authentication is handled securely without exposing host credentials
Enhanced features: Background tasks enabled by default for improved code analysis and autonomous development (Docker mode)
Full toolchain: Docker container includes development tools, languages, and utilities Claude needs
The result? Claude gets the --dangerously-skip-permissions experience it needs to be productive, while potential risks are contained within the sandbox.
There are several alternatives for running Claude Code in containers:
Here's why cco is the better choice for developers who want simplicity and seamless integration:
cco "help me code" - that's it. Devcontainers require VS Code setup, configuration files, and "Reopen in Container"--resume, --model, etc.) without configuration| Feature | cco | claudebox | devcontainer |
|---|---|---|---|
| Setup complexity | One command install | Multi-step setup, profiles | VS Code + config files |
| IDE dependency | None | None | VS Code required |
| Startup time | Instant | Slower (profile builds) | Container rebuild delays |
| macOS Keychain | Automatic | Manual setup | Manual setup |
| Terminal resizing | Automatic (SIGWINCH passthrough) | Unknown | Limited |
| Configuration | Zero config | Profile management | devcontainer.json |
| Development profiles | None needed | 15+ profiles | Basic |
| Project isolation | Basic | Advanced (per-project images) | Basic |
| Philosophy | Invisible simplicity | Feature-rich environment | IDE integration |
Choose devcontainer if you:
Choose claudebox if you:
Choose cco if you:
cco isn't trying to be a development environment - it's trying to be invisible protection that lets you use Claude Code exactly as intended, just safely.
curl -fsSL https://raw.githubusercontent.com/nikvdp/cco/master/install.sh | bash
git clone https://github.com/nikvdp/cco.git
cd cco
chmod +x cco
sudo ln -s "$PWD/cco" /usr/local/bin/cco
# Interactive session
cco
# Direct commands
cco "analyze this codebase"
cco --resume # Claude Code option passed through
# Get help
cco --help
# Force a specific sandbox backend
cco --backend native # Use native sandbox (sandbox-exec/bubblewrap)
cco --backend docker # Use Docker sandbox
cco --backend auto # Auto-detect (default)
# Rebuild the protective layer (Docker mode only, also updates to latest Claude Code version)
cco --rebuild
# Pull the latest pre-built image and automatically remove older cco image tags
cco --pull --clean-old-images
# System information and status
cco --info
# Shell access for inspecting the container environment
cco shell
cco shell 'ls -la' # Run shell commands inside the cco container
# Custom environment
cco --env API_KEY=sk-123
# Additional apt packages
cco --packages terraform,kubectl
# Auto-accept startup recovery prompts
cco --yes "review this repo"
# Enable Docker access
cco --docker-socket
# Run Docker sessions from a custom image
cco --backend docker --image my-cco-snapshot:good --persist
# Reuse the default repo-scoped Docker container filesystem across runs
cco --persist
# Select a specific persistent session name for this repo
cco --persist=frontend
cco --persist data-migration
# Attach directly to an existing Docker container by name or ID
cco --persist-container my-devbox
cco --persist-container 4d2b9f8c1a6e
# Update cco installation
cco self-update
# Clean up containers
cco cleanup
# Safe mode (native sandbox): hide $HOME for stronger isolation (experimental)
cco --safe
# Share directories read-only or hide them
cco --add-dir ~/configs:ro
cco --allow-readonly ~/.ssh
cco --deny-path ~/Downloads
cco also honors Claude Code's local project settings file at .claude/settings.local.json. If that file contains an additionalDirectories array, those directories are mounted read/write the same way as --add-dir PATH:rw. cco parses that file with python3 when available and falls back to jq; if neither tool exists it prints a warning and skips those extra mounts.
--docker-socket (experimental): Binds the host Docker socket into the sandbox so Claude can control Docker on your machine. This defeats the isolation barrier—avoid unless you explicitly need host Docker access.--image IMAGE / --docker-image IMAGE (Docker only): Runs cco against a specific Docker image instead of the default managed cco:latest image. This is useful if you docker commit a known-good persistent container yourself and want later cco runs to start from that image. With --pull, cco pulls the chosen image first.--clean-old-images (Docker only): After a successful pre-built image pull, remove older ghcr.io/nikvdp/cco:* tags automatically. Without this flag, cco asks before removing old managed image tags when it can prompt.--force-docker-bridge-network (Docker only): Force bridge networking instead of host networking. By default cco uses --network=host when available (Linux, OrbStack). Use this if you need port isolation or want explicit -p port forwarding.--yes / -y: Auto-accept startup recovery prompts such as macOS Keychain unlock before cco starts. OAuth maintenance is automatic when needed and is not controlled by --yes.--allow-oauth-refresh (experimental): Gives the container write access to your Claude credentials so refreshed tokens sync back to the host. Malicious prompts could corrupt or replace those credentials.--persist (Docker only, opt-in): Reuses the default persistent container for the current repo instead of starting fresh each run. cco starts it for the invocation and stops it again when the run ends.--persist=NAME or --persist NAME: Selects a specific persistent session for the current repo so you can keep multiple reusable container filesystems side by side.--persist-container TARGET: Attaches to an existing Docker container by name or ID instead of using cco's managed session naming.shell, use bare --persist shell ... exactly as before for the default session.cco will not automatically broaden mounts for later worktrees, so reuse fails clearly if the chosen container does not already expose the current path.Use --persist or --persist NAME when you want cco to manage the session for a repo. Use --persist-container TARGET when you already know the exact container you want to attach to and want that choice to win over cco's naming logic.
Use --image IMAGE when you want cco to run against a custom base image, for example after docker commit <container> my-cco-snapshot:good. Custom image overrides are not compatible with --rebuild or --packages, because those flags only make sense for the default cco-managed image path.
--safe (native only, experimental): Provides stronger filesystem isolation by hiding your entire $HOME directory from Claude. Only the project directory and explicitly shared paths remain visible. Trade-off: Increased security but may cause some tools to fail if they need access to configuration files in $HOME. Use --allow-readonly to selectively expose needed paths.--allow-readonly PATH: Share extra files or directories read-only inside the sandbox.--deny-path PATH: Deny read/list/write access to a path so it is fully inaccessible to Claude.You can create exceptions within denied paths by combining --deny-path with --allow-readonly or --add-dir:
# Deny all of /run but allow access to a specific socket
cco --deny-path /run --allow-readonly /run/user/1000/pipewire-0
# Deny a config directory but allow a specific subdirectory
cco --deny-path ~/.config --add-dir ~/.config/myapp
Precedence rules:
--)Arguments after -- are passed directly to the underlying sandbox backend (Docker, bwrap, or sandbox-exec). This enables advanced configuration like port forwarding without cco needing explicit flags for every option:
# Forward ports for development servers (Docker mode)
cco -- -p 3000:3000 # Forward port 3000
cco -- -p 3000:3000 -p 8080:8080 # Forward multiple ports
cco -- -e "MESSAGE=hello world" # Set env with spaces
Shell quoting is preserved - each argument after -- remains a separate argv element.
CCO_SANDBOX_ARGS_FILE)For persistent configuration, set CCO_SANDBOX_ARGS_FILE to point to a file containing sandbox backend arguments (one per line):
export CCO_SANDBOX_ARGS_FILE=~/.config/cco/sandbox-args
Example file (~/.config/cco/sandbox-args):
# Forward dev server port
-p
3000:3000
# Forward database port
-p
5432:5432
# Custom environment
-e
DATABASE_URL=postgres://localhost/mydb
Format rules:
# are commentscco acts as a wrapper - any options it doesn't recognize get passed directly to Claude Code:
# These Claude Code options work normally
cco --resume
cco --model claude-3-5-sonnet-20241022 "write tests"
cco --no-clipboard "analyze this file"
cco --permission-mode auto "work through this task"
# Mix cco and Claude options
cco --env DEBUG=1 --resume # `cco` + Claude options
When you pass a Claude permission mode directly, cco leaves it alone instead of adding its default bypass flag. If your trusted Claude settings set permissions.defaultMode to auto and Auto Mode is not disabled, cco also lets that default apply.
--command)You can use cco as a generic sandbox wrapper for any CLI, not just Claude. This is helpful when you want a tool to run with full autonomy inside a contained environment.
# Run a shell inside the sandbox
cco --command "bash"
# Run a custom program in the sandbox
cco --command "python3" - <<'PY'
print("hello from inside cco")
PY
# With additional project directories mounted read/write
cco --add-dir ~/work/secrets --command "ripgrep TODO"
Notes:
--command replaces the Claude invocation; all following args are passed to your command as-is.cco does not add Claude-specific flags to your command (e.g., it won’t append --dangerously-skip-permissions).--allow-readonly / --deny-path / --add-dir ...:ro to tighten what your command can read or modify.cco codex)cco codex is a convenience for running OpenAI’s Codex CLI with maximum autonomy while keeping it contained by cco.
What it does:
codex --dangerously-bypass-approvals-and-sandbox so codex won’t prompt for permissions and can act autonomously.~/.codex into the sandbox so codex can access it.cco (native Seatbelt/bubblewrap or Docker), so codex cannot escape mounted paths even with bypass flags.Important runtime note:
cco executes tools as the mapped non-root container user with HOME set to that user's home.docker exec sessions as root are unsupported for normal auth/config behavior. If you must run commands manually, use docker exec -u hostuser ... (or -u <HOST_UID>:<HOST_GID>).Examples:
# Start Codex inside cco's sandbox
cco codex "build and run a small demo"
# Equivalent manual form using --command (when ~/.codex exists)
cco --add-dir ~/.codex --command "codex --dangerously-bypass-approvals-and-sandbox" "build and run a small demo"
# Add extra directories Codex should be able to access
cco --add-dir ~/.codex --command "codex --dangerously-bypass-approvals-and-sandbox" "analyze this project"
Security note: --dangerously-bypass-approvals-and-sandbox applies to Codex’s internal permission checks, not to cco. The cco sandbox still constrains filesystem access to your project and explicitly mounted paths. Network access remains unrestricted by design.
cco auto <agent>)cco auto <agent> runs an agent in its native "ask me when it matters" mode instead of the default full-bypass mode:
cco auto claude runs Claude with --permission-mode auto instead of --dangerously-skip-permissions.cco auto codex runs Codex with --ask-for-approval on-request instead of --dangerously-bypass-approvals-and-sandbox; because it no longer uses the bypass flag, Codex's own sandbox may also apply.gemini, droid, opencode, omp, pi) it behaves exactly like running the agent normally.In every case cco's sandbox still applies. For Claude, auto mode only changes Claude's permission posture; for Codex, replacing the bypass flag also lets Codex enforce its own sandbox unless your Codex configuration or arguments say otherwise.
cco auto claude "refactor this module"
cco auto codex "build and run the tests"
cco uses host-based networking so that MCP (Model Context Protocol) servers or other tools you may have running on localhost are accessible to cco.
host.docker.internal bridge - may require MCP server reconfigurationIf you're using MCP servers with localhost addresses and they're not accessible, consider:
host.docker.internal instead of localhostStdio MCP servers run as child processes of Claude Code, so they inherit the selected sandbox's filesystem access.
Native sandbox: Host-installed MCP binaries are available, but the host filesystem is read-only outside the project and CCO's built-in state paths. If a server writes a cache or database elsewhere, explicitly share only that directory read/write with --add-dir:
mkdir -p ~/.cache/codebase-memory-mcp
cco --add-dir ~/.cache/codebase-memory-mcp "help me code"
With native --safe, also expose a host-installed binary's directory read-only if it is under the otherwise hidden home directory:
cco --safe \
--allow-readonly ~/.local/bin \
--add-dir ~/.cache/codebase-memory-mcp \
"help me code"
Prefer the narrow cache directory over making all of ~/.cache writable.
Docker sandbox: The MCP server must be installed inside the Linux container. A macOS host binary cannot run inside a Linux container, even if its path is mounted. For a one-time installation that persists across sessions, use a named persistent container:
cco --backend docker --persist=stdio-mcp shell
# Install the Linux MCP server inside the shell, then exit.
cco --backend docker --persist=stdio-mcp "help me code"
The MCP command configured in Claude must resolve inside the container; prefer a command on the container's PATH instead of an absolute host path. Alternatively, use --packages for software available through apt, or build a custom image and select it with --image.
cco passes through everything you need:
ANTHROPIC_API_KEY - Direct accessCLAUDE_CODE_OAUTH_TOKEN - Externally managed Claude OAuth token; skips local credential checks and startup refreshTERM, NO_COLOR)--disable-background-tasks to turn off)# Use .env files
echo "DEBUG=1" > .env
cco
Blank lines, comments, whitespace-prefixed comments, and non-assignment lines in .env are ignored so shell-style formatting does not break startup.
claude and login)cco automatically finds your Claude credentials:
~/.claude/.credentials.json or config directoryANTHROPIC_API_KEY and CLAUDE_CODE_OAUTH_TOKEN passed through in both modesUnderstanding the filesystem isolation differences between sandbox modes:
| Sandbox Mode | Host Filesystem Visibility | Isolation Level | Performance | Use Case |
|---|---|---|---|---|
| Docker | Only mounted paths | Strongest | Slower startup | Maximum security |
| Native | Entire host (read-only) | Basic | Fastest startup | Development convenience |
Native + --safe | Only project + whitelisted | Strong | Fast startup | Security + performance balance (experimental) |
Key Points:
--safe flag (experimental): Available only with native sandboxing. Hides your $HOME directory entirely while keeping fast native performance. May cause some tools to fail if they require access to dotfiles or configuration in $HOME.Security recommendation: Use Docker mode for maximum filesystem isolation, or try --safe with native sandboxing for better security with fast performance (experimental - may require --allow-readonly for some tools).
--safe: Only project and whitelisted paths visible (stronger isolation)cd my-project
cco
cco "add tests to the auth module"
cco --resume
export ANTHROPIC_API_KEY=sk-key
cco "review this pull request"
⚠️ These features are experimental and may have edge cases. Use with caution.
# OAuth token sync-back (EXPERIMENTAL)
# Lets sandboxed Claude sync refreshed credentials back to the host
cco --allow-oauth-refresh "help me code"
# Credential management (EXPERIMENTAL)
# Backup and restore Claude Code credentials for safety
cco backup-creds # Backup current credentials
cco restore-creds # Restore from most recent backup
cco restore-creds backup-file.json # Restore from specific backup
OAuth refresh feature: When the selected backend cannot safely persist an in-sandbox refresh, cco repairs already-expired Claude OAuth credentials with a fixed host-side Claude call before startup. If stored credentials are still valid but expire within two hours, cco starts Claude normally and runs the host-side refresh in the background. The experimental --allow-oauth-refresh mode is different: it enables bidirectional credential sync when sandboxed Claude refreshes expired tokens. It uses race condition protection and creates automatic backups.
Credential management: Provides manual backup/restore of Claude Code credentials with cross-platform support (macOS Keychain + Linux files).
Authentication issues
claude first to authenticateccocco will offer to unlock your login keychain if it cannot read Claude credentials. Use --yes to auto-accept that recovery step.Token expiration
cco checks stored OAuth expiry before sandbox startup. When the selected backend cannot safely persist an in-sandbox refresh, already-expired credentials are repaired before Claude starts. Valid credentials that expire within two hours are refreshed in the background while startup continues.claude directly (outside cco) to re-authenticate, then retry with cco--allow-oauth-refresh flag will sync container credentials back to your host. Only use it if you accept the additional credential tampering risk.Docker problems
docker infoPermission errors
cco handles user mapping automaticallycco --rebuild if neededExperimental features not working
--allow-oauth-refresh) is beta, reduces credential isolation, and may have issuesclaude if the startup refresh cannot recover credentialscco backup-creds / cco restore-credsToken expires during active session
If Claude stops responding with API errors during an active cco session, your OAuth token has likely expired mid-session. cco normally refreshes valid tokens that expire within two hours in the background during startup, but a long-running session can still cross the expiry boundary.
Root cause: Some sandbox backends cannot safely persist Claude's refreshed OAuth credentials back to the host credential store without granting broader credential or home-directory write access. cco keeps the sandbox narrow, repairs already-expired credentials before startup, and performs background host-side refresh for valid credentials that are close to expiry.
Workaround:
claude (outside cco)/login to re-authenticatecco sessioncco --resume to pick up the refreshed credentialsFor repeated startup failures, run plain claude once to confirm the account can refresh or re-login outside the sandbox, then start cco again.
Stdio-based MCP server cannot start or write its cache
In native mode, expose the server's cache or state directory with --add-dir. In Docker mode, install a Linux-compatible server inside the container using --persist, --packages, or a custom --image. See MCP Server Support for examples.
cco shell # Get inside for inspection
cco --info # Check system status
cco provides protection primarily through filesystem isolation and credential management. Important limitations to understand:
cco protects against:cco does NOT protect against:Network access is intentionally unrestricted to support MCP servers and maintain Claude's full functionality. The primary security benefits come from filesystem and credential isolation, not network isolation.
For detailed security information, threat model, and limitations, see SECURITY.md.
Practice safe computing.
Pull requests welcome! Please maintain all safety mechanisms.
MIT License
Shell
92.1%
C
3.2%
Dockerfile
2.0%
Python
1.8%