The --sandbox flag runs the CLI with OS-level isolation, enforcing the active Read and Write permission scopes at the operating-system level and optionally restricting network traffic.
How the sandbox works
When the sandbox is active:
- Writable paths are derived from granted
Write(...) permission scopes plus the workspace directory
- Readable paths are derived from granted
Read(...) scopes (platform defaults like /usr/bin are always readable)
- Scopes granted mid-session dynamically expand the sandbox for subsequent commands
If sandbox resolution fails (e.g., the sandboxing tools are unavailable on the user’s platform), the CLI will refuse to start rather than running unsandboxed. This fail-closed behavior applies whether sandbox was enabled by a team setting or by the user passing --sandbox directly, ensuring the security intent is never silently bypassed.Common causes of sandbox resolution failure:
- Windows: OS-level sandboxing is not currently supported on Windows. Sessions on Windows will hard-fail when
--sandbox is passed or when sandbox enforcement is Required, including when the CLI runs as an ACP server inside an IDE (e.g., Devin Desktop).
- Linux: Sandboxing requires
bubblewrap (bwrap) and socat to be installed. Sessions hard-fail with installation instructions when these are missing.
- Permission scope errors: Invalid paths in permission scopes that can’t be resolved.
Network filtering
Sandbox network filtering is currently unstable. If you need this feature, please reach out to your account representative for stability timelines.
Configure domain-level network filtering for the sandbox in the sandbox section of your config file (user config only). When --sandbox is active and domain filtering is configured, a managed network proxy starts on loopback and the sandbox restricts all child traffic to route through it.
| Option | Type | Default | Description |
|---|
allowed_domains | string[] | [] | Domain patterns allowed through the proxy. When non-empty, only matching domains are allowed (allowlist mode) |
denied_domains | string[] | [] | Domain patterns always blocked. Deny rules take precedence over allow rules |
network_mode | string | "full" | "full" allows all HTTP methods; "limited" allows only GET/HEAD/OPTIONS |
Domain pattern syntax:
| Pattern | Matches |
|---|
example.com | Exact match only |
*.example.com | Any subdomain (not the apex) |
**.example.com | Apex domain and any subdomain |
Example:
{
"sandbox": {
"allowed_domains": [
"github.com",
"**.npmjs.org",
"**.crates.io",
"**.pypi.org"
],
"denied_domains": ["evil.example.com"],
"network_mode": "full"
}
}
Domain filtering applies when the sandbox is active (--sandbox). Without --sandbox, the sandbox section is ignored.
Excluded commands
Sometimes a specific command needs to run outside the sandbox — for example git commands that must access credentials or hooks the sandbox blocks. The sandbox.excluded config section lets you exclude matching commands from sandbox isolation using the same Exec(...) rule syntax as permissions:
| Option | Type | Description |
|---|
excluded.allow | string[] | Matching commands run outside the sandbox automatically |
excluded.ask | string[] | Matching commands run outside the sandbox after the user approves a prompt |
excluded.deny | string[] | Matching commands are never excluded — they always stay inside the sandbox |
Example:
{
"sandbox": {
"excluded": {
"allow": ["Exec(git status *)"],
"ask": ["Exec(git push *)"],
"deny": ["Exec(git tag *)"]
}
}
}
Rule resolution: for each command, the most specific matching rule wins within a source (e.g., Exec(git push *) beats Exec(git *)), and when both user config and team settings match, the more restrictive verdict wins (deny > ask > allow). Commands with no matching rule — including when sandbox.excluded is not configured at all — always run inside the sandbox.
- Only
Exec(...) rules are supported in sandbox.excluded; any other rule type (e.g., Read(...), Write(...)) is ignored with a warning.
- Exclusion is fail-closed: if a command can’t be safely resolved (e.g., it can’t be parsed), it stays inside the sandbox.
- Exclusions apply to the default per-command exec path. Commands run through a persistent PTY shell (interactive sessions, or when
pty_for_noninteractive_exec is enabled) always stay inside the sandbox.
Enterprise enforcement
Enterprise admins can control sandbox behavior for their entire organization via Team Settings.
Sandbox enforcement mode
Set the enforcement level for the --sandbox flag across your organization:
- Optional (default) — Users choose whether to pass
--sandbox. No enforcement.
- Required — The
--sandbox flag is forced on for all users, even if they don’t pass it on the command line. All CLI sessions run with OS-level file system sandboxing that enforces Read/Write permission scopes.
A future Strict mode may lock down sandbox configuration entirely, preventing users from modifying sandbox settings.
Ensure all target machines are provisioned before setting sandbox enforcement mode to Required across your organization. If any users are on Windows, they will be unable to run the CLI until OS-level sandboxing is supported on Windows or the policy is relaxed to Optional.
Enterprise domain filtering
Admins can also configure organization-wide domain allowlists and denylists:
- Domain allowlist — When set, only the domains in this list are reachable through the sandbox network proxy. This list is authoritative: it completely replaces any user-configured
allowed_domains. Users cannot add additional domains to bypass admin restrictions.
- Domain denylist — Domains that are always blocked. Enterprise denied domains are additive: they are merged with the user’s local
denied_domains, making the combined list more restrictive.
How enterprise and user domain lists interact:
| Scenario | Enterprise config | User config | Effective result |
|---|
| Admin sets allowlist | allowed_domains: ["github.com"] | allowed_domains: ["npmjs.org"] | Only github.com is allowed (enterprise replaces user list) |
| Admin sets denylist | denied_domains: ["evil.com"] | denied_domains: ["risky.io"] | Both evil.com and risky.io are blocked (merged) |
| No admin allowlist | allowed_domains: [] | allowed_domains: ["github.com"] | User’s allowlist is used |
Because the user’s local denied_domains are preserved and merged additively, a user could deny a domain that appears in the enterprise allowlist. This is intentional: the combined effect is always more restrictive, never less. If this causes access issues, the user should remove the conflicting entry from their local config.
Enterprise excluded commands
Admins can also set organization-wide excluded command rules in team settings:
- Excluded allow / ask —
Exec(...) rules for commands that may run outside the sandbox across the organization, automatically or after a prompt.
- Excluded deny —
Exec(...) rules for commands that must never run outside the sandbox. A team deny overrides any user-level allow or ask for matching commands, so users cannot exclude commands their admins have locked down.
Team and user rules are resolved together: the most specific matching rule wins within each source, and the more restrictive verdict wins across sources (deny > ask > allow).
Example: lock down all exclusions except gh. A wildcard deny with an allow carve-out keeps every command inside the sandbox except gh, regardless of what users configure locally. These values go into the team-settings excluded-commands configuration (not the user config file, so there is no enclosing sandbox key):
{
"excluded": {
"deny": ["Exec(**)"],
"allow": ["Exec(gh *)"]
}
}
Because the more specific Exec(gh *) rule beats the wildcard Exec(**), gh commands run outside the sandbox while everything else stays inside — and the team-level wildcard deny overrides any user-level allow or ask rules for other commands.
Further reading