Shell sandbox
The shell sandbox runs each command the agent starts inside your operating system’s own sandbox. A sandboxed command can write only where you allow, cannot see your credential folders, and reaches the network only for hosts you approve. It is a second fence behind permission rules: even a command you approved cannot wander off.
| System | Sandbox used |
|---|---|
| macOS | Seatbelt (sandbox-exec, built into the system) |
| Linux | bubblewrap (bwrap, from your distribution’s packages) |
| Windows | Not available |
The sandbox is off until you turn it on. It applies to commands the agent runs through the Bash tool. Hooks and MCP servers are started without it.
Turn it on
| How | Lasts |
|---|---|
/sandbox on · /sandbox off | This session |
/sandbox network on · /sandbox network off | This session |
SYNTIC_BASH_SANDBOX=1 | Every run with that variable |
"sandbox": { "enabled": true } in settings | Every session that reads the file |
/sandbox with no arguments shows the current state: on or off, which sandbox program, network access, writable folders and excluded commands. A /sandbox choice beats the environment variable, which beats the settings file.
Check that it works
Turn it on, then ask the agent to run something that should fail:
> /sandbox on
> run: touch ~/sandbox-test && echo createdThe write outside the working folder is refused by the operating system, and the agent sees the error. A command inside your project still works as normal.
What a sandboxed command may do
| Area | Default |
|---|---|
| Write | The working folder and the per-user temp folder, plus folders you add. With strictCwd, only the working folder and added folders. |
| Read | The whole system, except credential folders and anything you deny. |
| Network | Off, unless you allow hosts or switch it on (see Network rules). |
| Git internals | A repository’s .git/hooks and git config can be read but never written, so a command cannot plant a hook. A linked worktree may still write its shared git folder, so commits work. |
| Credentials | Hidden even when a readable folder covers them (see below). |
Credentials stay hidden
When a command runs in the sandbox, these folders and files in your home folder are masked, even if you run in your home folder or allow reads of ~:
.ssh, .gnupg, the credential folders of cloud command-line tools, .kube, .docker/config.json, .netrc, .npmrc, .pypirc, .git-credentials, .config/gh/hosts.yml, and Syntic’s own .syntic folder.
Secret-looking environment variables are removed too. That covers well-known names such as cloud access keys and database passwords, and any variable whose name ends in TOKEN, SECRET, PASSWORD, PASSWD, PAT, APIKEY, API_KEY, ACCESS_KEY, SECRET_KEY, PRIVATE_KEY or AUTH_TOKEN. Values you set for the session with /env are passed through on purpose. Syntic Code’s own sign-in is never passed to any command, sandboxed or not.
Settings
{
"sandbox": {
"enabled": true,
"autoAllowBashIfSandboxed": true,
"network": {
"allowedDomains": ["github.com", "*.npmjs.org"],
"deniedDomains": ["paste.example"]
},
"filesystem": {
"allowWrite": ["~/.cache/build"],
"denyWrite": ["./secrets"],
"denyRead": ["~/private/**"]
},
"excludedCommands": ["docker *"]
}
}| Key | Effect |
|---|---|
enabled | Turn the sandbox on. |
failIfUnavailable | Refuse commands when the sandbox program cannot run, instead of running them unsandboxed with a warning. |
autoAllowBashIfSandboxed | A command that will really run sandboxed needs no prompt. Deny rules, ask rules and the safety checks still apply. |
strictCwd | Only the working folder (and added folders) is writable; the temp folder is not. |
network.allowedDomains | Hosts commands may reach: example.com exactly, *.example.com for its subdomains, * for anything. |
network.deniedDomains | Hosts that are always refused, even with *. |
network.allowManagedDomainsOnly | Policy: only hosts from managed settings count; users’ own entries are ignored. |
filesystem.allowWrite | Extra writable folders. ~/x is under your home folder; a relative path is under the working folder. |
filesystem.denyWrite | Folders that are never writable (they can still be read). |
filesystem.allowRead | Extra readable folders. |
filesystem.denyRead | Folders that can be neither read nor written. Glob patterns work. |
filesystem.allowManagedReadPathsOnly | Policy: only read paths from managed settings count. |
excludedCommands | Commands that always run outside the sandbox, such as docker * or bazel:*. |
allowUnsandboxedCommands | Let a single call ask to run outside the sandbox. Off by default. |
allowAppleEvents | macOS: let sandboxed commands control other apps (for example through osascript). Off by default. |
bwrapPath | Linux: the absolute path of the bwrap program to use instead of the one on your PATH. User and policy settings only. |
extraDirs | Older spelling of filesystem.allowWrite; both are honored. |
How files combine
Settings from every file are merged, with stronger files winning for single values. The deny lists (deniedDomains, denyWrite, denyRead) are different: entries from every file add up, so no file can remove another file’s deny entry.
A repository’s own settings can only make the sandbox stricter. From .syntic/settings.json or .syntic/settings.local.json, only these are read: enabled, strictCwd and failIfUnavailable when set to true, and the three deny lists. A project cannot allow hosts, add writable folders, exclude commands, permit unsandboxed runs or change bwrapPath.
Network rules
The operating system can only switch networking fully on or off for a process; it cannot filter by host. So Syntic Code looks at each command and decides before it starts:
- If the command uses a denied host, it is refused before it runs, and you are told which entry matched. This beats every way of turning the network on.
- If every host the command uses is allowed (or
allowedDomainsholds*), it gets the network. - Otherwise it runs with the network off. If it then fails, the agent gets a note that a host was not on the list.
Hosts are found in:
- URLs (
https://host/...),user@host:pathremotes, and host arguments to network tools such ascurl,wget,ssh,scpandnc; - the default registry of a package manager the command clearly uses: npm, pnpm and yarn; pip, uv and poetry; cargo; gem and bundle; go; brew. A
--registryor--index-urlon the command replaces the default; - git remotes:
git clone,fetch,pull,pushandls-remotewithout a URL use the remote’s address from.git/config.
A command that names no host it can see gets the network only when allowedDomains holds *.
Hosts a program chooses by itself are not seen: a script that downloads from an address in a file, a proxy or mirror set in a config file, or an image registry chosen by a container tool. Such commands get the network only with *, and then the deny list cannot stop them. Treat host rules as a guard against the commands you can read, not a firewall.
/sandbox network on and SYNTIC_BASH_SANDBOX_NET=1 switch the network on for every sandboxed command, except those that name a denied host.
Running a command outside the sandbox
Some tools do not work in a sandbox: container engines, tools that talk to system services, or builds that write to many places. You have two ways out:
excludedCommands: list commands that should always run normally. They still go through the usual permission prompt.allowUnsandboxedCommands: lets the agent mark one call as needing to run outside. Without this setting (orSYNTIC_BASH_ALLOW_UNSANDBOXED=1), such a request is ignored and the command stays sandboxed, so the model can never turn the fence off by itself.
How the sandbox relates to permissions
The sandbox and permission rules work at different moments. Rules decide whether a command runs; the sandbox limits what it can touch once it does.
| Setting | Effect |
|---|---|
| Deny and ask rules | Always apply first, sandbox or not. |
autoAllowBashIfSandboxed | Skips the prompt only for commands that will truly run sandboxed. An excluded command, or one marked to run outside, still asks. |
| Permission modes | Unchanged. In auto mode, the checks for high-impact actions still apply. |
Read and Edit deny rules | Govern the agent’s file tools. Use sandbox.filesystem.denyRead to keep shell commands away from the same files. |
For organizations
Put the sandbox block in managed settings to set it for everyone. Two policy-only switches stop users from widening it:
{
"sandbox": {
"enabled": true,
"failIfUnavailable": true,
"network": { "allowedDomains": ["github.example.com", "registry.npmjs.org"], "allowManagedDomainsOnly": true },
"filesystem": { "allowRead": ["/opt/tools"], "allowManagedReadPathsOnly": true }
}
}With allowManagedDomainsOnly, users’ allowedDomains entries are ignored and only the policy’s count. allowManagedReadPathsOnly does the same for allowRead. Deny lists from users and projects still add up on top.
Environment variables
| Variable | Effect |
|---|---|
SYNTIC_BASH_SANDBOX | 1, true, yes or on turns the sandbox on; any other value turns it off. |
SYNTIC_BASH_SANDBOX_NET | Allow or block the network for every sandboxed command. |
SYNTIC_BASH_SANDBOX_EXCLUDED | Excluded commands, separated by commas or new lines. Replaces excludedCommands. |
SYNTIC_BASH_ALLOW_UNSANDBOXED | Same as allowUnsandboxedCommands. |
Want a different fence for everything? SYNTIC_SHELL_PREFIX puts your own isolation tool in front of every command, for example firejail --net=none.
Troubleshooting
| Problem | What to check |
|---|---|
| A warning says the sandbox program was not found, and commands run unsandboxed | On Linux, install bubblewrap or set bwrapPath. On Windows the sandbox is not available. Set failIfUnavailable to refuse instead. |
| A command is refused for a denied host | A deniedDomains entry in some settings file matched. Deny entries add up across files; run syntic doctor or check your organization’s policy. |
| A download or install fails offline | The host is not on allowedDomains. Add it, or use /sandbox network on for this session. |
| A build cannot write its cache | Add the cache folder to filesystem.allowWrite, or check whether strictCwd is on. |
| A container command fails | Add it to excludedCommands, for example docker *. |
git push over SSH fails | SSH keys are hidden in the sandbox by design. Push from your own terminal, or exclude the command. |
| A tool that drives other macOS apps fails | Set allowAppleEvents in your user settings. |
| A project’s sandbox settings seem ignored | Projects can only tighten the sandbox. Allowing hosts or folders must come from your user settings or policy. |