Документация доступна только на английском языке.

Sandbox

Shell commands the agent runs are contained by the operating system sandbox — Seatbelt on macOS, bubblewrap on Linux — with workspace-only writes, no credential reads, and network access through a filtering proxy.

Permissions decide whether a command may run. The sandbox limits what a command can do once it runs. The sandbox is on by default: a shell command the agent starts can write only inside your workspace and temp directories, can't read your credential stores, and reaches the internet only through Nib's filtering proxy, which lets package registries through and asks about any other host. Services on localhost are reachable, except Nib's own.

The two layers work together. A sandboxed command is contained even if it does something you didn't expect, which means you can approve more commands without reading each one closely.

How it works

PlatformMechanismNotes
macOSSeatbelt (sandbox-exec) with a generated profileBuilt into macOS. Nothing to install
Linuxbubblewrap (bwrap) with a generated argument listInstall bubblewrap from your package manager. Without it, commands run unsandboxed. See Without a sandbox
WindowsNot available yetCommands run unsandboxed; WSL2 adds the sandbox. See Without a sandbox

The sandbox applies to shell commands (Bash, Process, Terminal, RunTests and Monitor) and to helpers Nib starts for the session, such as type checkers and language servers. Nib's own file tools (Read, Edit, Write, …) don't go through it; they are governed by permissions and protected paths.

Sandboxed commands also run with a cleaned environment: provider keys and Nib's own credentials are removed.

The default policy

AllowedBlocked
WriteThe workspace roots (your working directory and any additional directories), the per-user temp directory and /tmpEverything else. Inside the workspace, git configuration and hooks, and agent and editor configuration, stay read-only
ReadEverything elseSecret stores in your home directory: ~/.ssh, ~/.aws, ~/.gnupg, ~/.azure, ~/.kube, ~/.config/gcloud, ~/.docker/config.json, ~/.netrc, ~/.password-store, ~/Library/Keychains, CLI tokens (~/.config/gh, ~/.git-credentials, ~/.pypirc, ~/.cargo/credentials, ~/.vault-token, …), browser profiles, container runtime sockets, and agent data directories (~/.nib, ~/.claude, ~/.codex, ~/.gemini) apart from the skills, plugins, agents, commands and output styles in the first two, and the skills in ~/.codex
NetworkLocalhost services. Package registries and toolchain downloads, through the proxy: npm, Yarn, PyPI, crates.io, the Go module proxy, RubyGems, Maven Central, nodejs.org, bun.sh and GitHub's download hostsNib's own ports on localhost. Any other host until it's allowed (see Domains)

Paths are resolved to their real locations before the profile is built, so a symlink can't be used to escape (on macOS, /tmp becomes /private/tmp).

Opening up the network

Local ports

Dev servers, local databases and test suites need localhost, so it's open by default: commands can listen on local ports and connect to the services on them. The daemon's own ports are never reachable.

To close localhost, turn allowLocalBinding off and list the ports commands may connect to. Commands can still listen on any local port:

~/.nib/settings.json
{
  "sandbox": {
    "network": {
      "allowLocalBinding": false,
      "allowLocalPorts": [3000, 5432]
    }
  }
}

Domains

Commands reach the internet through Nib's filtering proxy. Package registries and toolchain downloads are allowed unless deniedDomains lists them. To let commands reach other hosts, for example a private registry, list them in allowedDomains:

~/.nib/settings.json
{
  "sandbox": {
    "network": {
      "allowedDomains": ["registry.example.org", "*.example.com"],
      "deniedDomains": ["uploads.example.com"]
    }
  }
}
  • An entry is an exact host, a subdomain wildcard (*.example.com matches api.example.com but not example.com), * for any host, or an IP address, each with an optional :port.
  • deniedDomains wins over allowedDomains.
  • A request to a host on neither list asks you: allow once, allow for the session, or deny. In auto mode the classifier decides: a host it allows is allowed for the session, a host it blocks is refused, and you're asked only while auto mode is paused or when the classifier can't judge the request. In dontAsk and headless runs the request is refused; in bypass it's allowed.
  • Private and local addresses are reachable only through an IP entry naming the address. A localhost entry reaches this machine's loopback, and nothing else. An allowed host name never reaches one, because whoever runs its DNS decides where it points.
  • A refused request gets a 403 response whose body starts "Blocked by Nib network policy:", so the agent can tell you which host it needs.

On Linux, the domain allowlist is advisory for now. Programs are pointed at the proxy through HTTP_PROXY and related variables, but there's no network namespace isolation yet, so a program that ignores proxy settings can reach the network directly, and localhost stays open, the daemon's ports included (its API still needs the daemon's token). On macOS the sandbox profile enforces both. Nib shows these gaps as a notice when the session starts.

Settings

Sandbox settings use the common sandbox settings schema, so an existing .claude/settings.json sandbox block works as is.

KeyTypeMeaning
sandbox.enabledbooleanRun shell commands in the sandbox (default true)
sandbox.failIfUnavailablebooleantrue refuses shell commands when the sandbox can't be applied; false runs them unsandboxed. Unset: unsandboxed on Windows and on Linux without bubblewrap, refused elsewhere. See Without a sandbox
sandbox.filesystem.allowWritestring[]Extra writable paths
sandbox.filesystem.allowReadstring[]Exceptions to the read deny list
sandbox.filesystem.denyReadstring[]Extra paths that may not be read
sandbox.filesystem.denyWritestring[]Extra paths that may not be written, even inside the workspace
sandbox.allowUnsandboxedCommandsbooleanfalse turns on strict mode: Bash refuses dangerouslyDisableSandbox, and the sandbox stays on in bypass mode. A false wins over a true in another file
sandbox.network.allowLocalBindingbooleanLet commands connect to localhost services (default true)
sandbox.network.allowLocalPortsnumber[]With allowLocalBinding off, the localhost ports commands may connect to
sandbox.network.allowedDomainsstring[]Hosts reachable through the filtering proxy
sandbox.network.deniedDomainsstring[]Hosts that are always refused
sandbox.network.allowUnixSocketsstring[]Unix sockets commands may connect to

These keys are accepted so existing settings load, but Nib doesn't apply them yet: autoAllowBashIfSandboxed, excludedCommands, filesystem.disabled, network.allowAllUnixSockets, network.httpProxyPort and network.socksProxyPort. No command is excluded from the sandbox.

Who can change what

A repository must not be able to switch off its own sandbox. These settings are read only from your user settings, managed settings or --settings, and ignored, with a warning, in a project's .nib/ or .claude/ settings, whichever way they're set:

  • enabled: false and failIfUnavailable: false
  • allowUnsandboxedCommands and excludedCommands
  • allowLocalBinding, allowLocalPorts, allowUnixSockets and allowAllUnixSockets
  • filesystem.allowRead, filesystem.allowWrite and filesystem.disabled

A project can still make its sandbox stricter, for example with filesystem.denyRead or network.deniedDomains.

When a command is blocked

Sandbox failures are hard to diagnose from a bare Operation not permitted. The Bash tool tells the agent every limit the sandbox sets, so it can recognize a failure on one of them in the program's own message, whatever words or language the program uses. When Nib's network proxy refused a host while the command ran, the result names the host and the allowed domains. The agent can then work within the limit or explain what it needs.

If the agent decides it genuinely needs to run a command outside the sandbox, it sets dangerouslyDisableSandbox on the Bash call. That asks you in every mode but bypass, auto mode included, even when the command also does something auto mode would otherwise hold back; no rule or classifier can grant it, and in dontAsk and headless runs it's refused. With allowUnsandboxedCommands: false, Bash refuses it outright.

In bypass mode, the agent's shell commands run outside the sandbox, as you chose, unless allowUnsandboxedCommands: false keeps it on. Code someone else wrote stays contained: extensions' servers and hooks, language servers and type checkers still run sandboxed.

Without a sandbox

On Windows, and on Linux without bubblewrap, there is no OS sandbox. Nib then runs the agent's shell commands unsandboxed, with your full user permissions, and says so once when the session starts. WSL2 on Windows, or installing bubblewrap on Linux, adds the sandbox. Until then permissions are the only layer:

  • In the ask modes, Nib asks before each command that isn't read-only, as usual.
  • In auto mode, the classifier checks every command, knowing it runs unsandboxed. A command it blocks is held back, and you can allow it.
  • In bypass mode, commands run.
  • A command the agent asks to run outside the sandbox doesn't ask you for that: there's no sandbox to leave. The other checks that always need you still apply, auto mode's cards included: writes that can make code run, CI workflow changes that widen access, and a crew member's spending.
  • Type checkers and language servers from your project don't run, since they would run the project's code without anyone approving it. Extensions' servers and hooks don't start either.

To refuse shell commands instead, set "sandbox": { "failIfUnavailable": true } in your settings.

Failing closed

Elsewhere, Nib doesn't run commands unsandboxed when the sandbox can't be applied: on macOS if sandbox-exec is missing, on any other platform, when failIfUnavailable is true in your, your organization's or the project's settings, and in Nib's cloud sandboxes. It refuses shell commands and shows a notice when the session starts. You can then:

  • Install the sandbox (on Linux, the bubblewrap package).
  • Approve single commands to run unsandboxed when the agent asks (not in the cloud).
  • Set "sandbox": { "failIfUnavailable": false } in your user settings to run shell commands unsandboxed, with the permission engine as the only layer.

The same rule applies to every safety layer in Nib: a check that can't run denies or asks, never allows.

Audit

Every permission decision and every connection through the network proxy is recorded in <data directory>/harness/audit/<session>.jsonl, readable only by you.

Limits

  • The sandbox constrains the processes the agent starts. It doesn't constrain the daemon itself or Nib's file tools.
  • Commands that run outside the sandbox, because you approved it, the session is in bypass mode or the platform has no sandbox, are not contained.
  • Reads are broad by design. Anything readable outside the deny list, such as source in other folders, can be read by a sandboxed command. Add paths to filesystem.denyRead to hide them.
  • Long-running processes started with Process stay sandboxed for their whole lifetime, including children.
  • On Linux, the domain allowlist is advisory (see above).
  • On Linux, /tmp is private to each command and your runtime directory ($XDG_RUNTIME_DIR) is hidden, so file sockets such as ssh-agent's, gpg-agent's or a rootless container runtime's can't be reached. Abstract sockets (X11, the D-Bus session bus) stay reachable unless the network is fully blocked, and a protected file that doesn't exist yet (a new .mcp.json) can be created by a sandboxed command; Nib's file tools still ask before writing one. Nib lists these gaps when the session starts.

На этой странице