The sandbox is on by default (macOS Seatbelt, Linux Landlock, and on Windows a write-restricted token that confines writes only, see below). Shell commands can write only inside the project, temp dirs and the package managers' download caches: ~/.cargo/registry and git, ~/.npm (_cacache, _npx), ~/.m2/repository, ~/.gradle/caches, go/pkg, .bun/install/cache, .pnpm-store, and named entries of ~/.cache and ~/Library/Caches (pip, uv, pnpm, Yarn, deno, node-gyp, go-build, Homebrew, Playwright, …), not the rest of those two. Dependency downloads are writable on purpose, like the project's own node_modules; toolchains, installed tools and settings are not.
- What stays read-only inside those trees (macOS): the project's
.git(pinned, also when missing, so it can't be swapped or created) with its hooks, config andcommondir, submodule and worktree git dirs (pinned too), nested repositories'.git/hooksand.git/config; agent and editor settings that carry hooks (.chanto/,.claude/settings*.jsonandhooks/,.mcp.json,.husky/,.githooks/,.vscode/{settings,tasks}.json, with.claudeand.vscodepinned against rename); Chanto's own home and shell startup files; toolchain bins and settings (~/.cargo/binandconfig.toml,~/.rustup, pyenv, uv's Pythons,~/.local/bin, Gradle wrappers,init.dandgradle.properties, Maven settings). - What needs an unsandboxed run (approval): installing a toolchain or tool, a first dependency fetch on Linux when its cache directory doesn't exist yet,
git initandgit cloneinside the project,git worktree add,git submodule add/update,git remote add,git configandgit branch -u(they write.git/config), and git over SSH unlesssandbox.allow_ssh_agent = true. The blocked-command hint says so. - Reads: on macOS and Linux commands can't read SSH private keys, AWS/gcloud/GitHub/Docker/Kubernetes/npm/crates.io/1Password credentials,
.netrc, git credentials, GPG private keys or Chanto's own token files (add more withsandbox.deny_read). A private npm registry ordocker pullthat needs those logins fails quietly inside the sandbox. On Linux, read denials cover files that exist when the command starts. On macOS the keychain is still reachable, sogit credential-osxkeychain,gh auth tokenand Docker's credential helper can return tokens. - Local IPC (macOS): commands start in a new session without a controlling terminal, can't read, write or
ioctlChanto's terminal or the other terminal windows' ttys (ptys they open themselves, asscriptdoes, work), can't send Apple Events, use LaunchServices (open,osascript), the clipboard or Shortcuts, and can connect to Unix sockets only inside the project and their own$TMPDIR(so notdocker.sock, SSH or GPG agents, tmux, or other apps' sockets in the shared temp dirs). The SSH, GPG, D-Bus, X11/Wayland, Docker andXDG_RUNTIME_DIRenvironment variables are removed. - Linux status: Landlock grants the project, temp dirs and
/devwhole, because carving a path out of a tree takes away the right to create files next to it (target/,.git/index.lock). So the in-project guards above (.git,.chanto,.claude, …) are not enforced by the sandbox on Linux; the permission layer still asks before the file tools touch them, but a shell command can write them. The home guards apply where a configuredallow_writeor--add-dircovers them. Unix sockets aren't scoped: removing the environment variables is advisory, and a command that knows a socket's path (/var/run/docker.sock,/run/user/<uid>/bus) can connect to it. Seccomp refuses TIOCSTI and TIOCLINUX, and UDP/raw sockets when the network is restricted. - Network stays allowed by default. Set
sandbox.network = "deny"to block it, or"allowlist"to use the domain filtering proxy (deny_network = trueremains a legacy alias; an explicitnetworkwins); neither is enforced on Windows. - Windows status: commands run under a restricted copy of your own token (
WRITE_RESTRICTED) inside a job object. No administrator, driver, extra account or AppContainer is needed. What's enforced: writes are limited to the project, the temp dir,allow_writeand--add-dirpaths, the package managers' download caches (.cargo\registry,.cargo\git,.gradle\caches,.m2\repository,.bun\install\cache,go\pkgand.nuget\packagesin your profile; the npm, pnpm, Yarn, pip, uv, Go build and Poetry caches under%LOCALAPPDATA%), plus the rare locations that grant write access to Everyone; and the job kills every descendant when the shell or the launcher exits (the launcher also watches the Chanto process). What isn't: none of the read-only guards above apply, and reads are not restricted at all (Windows only consults a write-restricted token's extra SIDs for writes), sodeny_readand the built-in credential denials don't apply and commands can read anything you can. The network isn't restricted either:network = "deny"gets a startup warning and commands keep network access, andnetwork = "allowlist"fails closed with an error. Withsandbox.auto_allow, Chanto warns at startup that auto-allowed commands can read credentials and use the network. - On Windows each writable root gets its own write SID (derived from its path) and an inheritable allow ACE for it. The ACE stays on the directory after the command: the first grant on a big tree waits while Windows propagates it to existing files, later commands only check it. The tool caches are granted in the background when Chanto starts, so commands don't wait on them. It grants nothing to ordinary processes, since that SID can only narrow a token's access. Files with protected ACLs, or moved in from elsewhere with their own ACL, stay read-only inside the sandbox. Grant paths must be on an ACL-capable filesystem, and setup failures stop the command rather than running it without isolation. WSL shells can't be contained and are refused. If the startup probe fails, Chanto warns and runs commands unsandboxed, as on any system without a sandbox.
- When the sandbox blocks a command, the model can retry it with
dangerouslyDisableSandbox: true. That is never automatic and always needs a human: you're asked in default, acceptEdits and plan modes, auto mode asks too (the classifier does not get to hand the sandbox back), and only bypass mode allows it. An explicitBash(...)allow rule you wrote also covers it. /sandboxshows the policy and/sandbox on|offtoggles it for the session;[sandbox] enabled = falseturns it off.--sandbox read-only | workspace-write | danger-full-accesswork as in Codex.- In allowlist mode,
allowed_domainsaccepts exact names and*.example.org(subdomains only);denied_domainswins over allowed entries. Omitallowed_domainsto use package registry defaults for GitHub, npm/Yarn, PyPI, crates.io, Go modules, Maven Central and RubyGems. An explicit[]permits nothing. IP literals, including loopback, require an exact entry. - Commands receive
HTTP_PROXY,HTTPS_PROXY,ALL_PROXYand their lowercase equivalents, withNO_PROXY=localhost. HTTPS uses CONNECT; plain HTTP checks Host and the request target. A lazy loopback proxy uses separate credentials and policies for each command. Tools that ignore proxy settings cannot connect directly to normal service ports. - macOS Seatbelt restricts outbound traffic to the proxy's loopback endpoint. Linux requires Landlock ABI v4 for
denyorallowlist, restricts TCP connects to the proxy port, and uses seccomp to block UDP/raw/packet sockets and io_uring bypasses. Landlock port rules apply to every destination address, so Linux also permits TCP connections to that same port on other hosts; it cannot enforce the loopback address. Unix sockets remain available for local DNS services. Unsupported kernels fail closed with an actionable error;network = "allow"retains unrestricted networking. - A rejected domain receives HTTP 403 and appears in the tool result even if the command hides proxy output. Interactive commands offer permission to allow it for the next sandboxed command or always for this project (saved in
.chanto/config.toml, preserving other settings). Retry the command after approving; commands are never rerun automatically. Background shells offer this when you pollBashOutput. An explicit denied-domain rule must be removed before a domain can be approved. - Systems without a working sandbox (including Linux without Landlock) get a startup warning. In auto mode, sandboxed commands can skip the classifier (
sandbox.auto_allow).
toml
[sandbox]
network = "allowlist"
# Omit allowed_domains for package registry defaults, or supply your own list:
allowed_domains = ["github.com", "*.npmjs.org", "pypi.org", "files.pythonhosted.org"]
denied_domains = ["untrusted.npmjs.org"]