Skip to content

Bash sandbox

The Bash tool in biu wraps every command in an OS-level sandbox by default, so commands generated by the LLM (and commands you approve) cannot escape the project directory and damage the host. The sandbox and the permissions system (permissions.md) are independent of each other: permissions decide "may this call run at all", while the sandbox decides "what it may touch once it runs".

Platform strategy

Platform Mechanism Notes
macOS sandbox-exec (SBPL profile) Ships with the system; nothing to install
Linux Bubblewrap (bwrap) Must be installed (Debian/Ubuntu: apt install bubblewrap). At startup it actually probes once whether user namespaces are available; if not, it falls back to running unsandboxed (when bwrap exists but userns is disabled by a hardened kernel / container, it does not pretend to be sandboxed)
Other platforms No sandbox Bash runs directly as a subprocess

When the sandbox tooling is missing, biu doctor reports a ! warning (e.g. bwrap not in PATH — Bash will run unsandboxed).

Default policy

Without any sandbox rules configured, every Bash command runs under these constraints:

  • Read: the whole filesystem is readable (the model often needs to look at /tmp, /etc, system frameworks).
  • Write: only the current working directory (plus /tmp, /var/folders on macOS, /dev/null, standard streams, and other necessary devices).
  • Network: off by default (on macOS, only loopback binds are allowed). The model can explicitly request network access for a single call via the allow_network parameter, which then shows up in the permission prompt.
  • Extra writable directories: working directories registered via /add-dir, --add-dir, or permissions.additionalDirectories in settings.json are automatically added to the writable roots.

Custom rules: the sandbox block in settings.json

Four list fields form a "read / write × deny / exempt" matrix:

{
  "sandbox": {
    "fsReadDeny": [
      "~/.ssh",
      "~/.aws",
      "~/.config/gcloud"
    ],
    "fsReadAllowWithinDeny": [
      "~/.aws/config"
    ],
    "fsWriteAllowExtra": [
      "${PROJECT_ROOT}/../build-output"
    ],
    "fsWriteDenyWithinAllow": [
      "${PROJECT_ROOT}/../build-output/keep-out"
    ]
  }
}
Field Purpose
fsReadDeny Absolute paths that may not be read — typically used to shield credential directories (~/.ssh, ~/.aws, cloud CLI configs, etc.)
fsReadAllowWithinDeny Re-allows individual paths inside denied directories (e.g. deny ~/.aws but allow reading ~/.aws/config)
fsWriteAllowExtra Extra writable roots beyond the current directory (for example a build-output directory in a sibling directory)
fsWriteDenyWithinAllow Punches holes inside writable roots — even if the outer directory allows writes, these subpaths remain read-only

Path syntax

  • Paths must be absolute (relative paths are silently dropped, preventing ../ escapes);
  • Three expansions are supported: ~ (home directory), ${VAR} (environment variable), ${PROJECT_ROOT} (project root).

Three-layer merging: tighten only, never loosen

Like permission rules, the sandbox block spans the three settings.json layers (~/.biumind/settings.json → project .biumind/settings.json → .biumind/settings.local.json), and the merge rule is a pure union: the project and local layers can only append entries to the four lists — they can never remove or empty entries from the user layer.

[!WARNING] This is a deliberate security baseline: a malicious repository's .biumind/settings.json cannot quietly cancel your global "deny reads of ~/.ssh" rule with "fsReadDeny": []. For the same reason, the plugin disable list (plugins.disabled) is likewise an append-only union.

You can verify the merged result at any time with biu doctor — the sandbox check prints the entry counts of the four lists:

✓ sandbox            read-deny=3 allow-within=1 write-extra=2 deny-within=1

[!TIP] Keys in settings.json are camelCase (fsReadDeny). Writing fs_read_deny produces no error — you just silently get an empty rule set. biu doctor is where you catch this kind of typo.

Implementation semantics per platform

macOS (SBPL, the last matching rule wins):

  1. baseline allows all reads;
  2. fsReadDeny denies each entry;
  3. fsReadAllowWithinDeny re-allows each entry;
  4. deny all writes;
  5. allow the current directory + /tmp + /var/folders + fsWriteAllowExtra;
  6. fsWriteDenyWithinAllow denies each entry (so a path appearing in both allow and deny ends up denied — the "hole in the writable root" semantics).

Linux (bwrap, mount arguments applied in order, later ones overriding earlier ones):

  • fsReadDeny → the directory is covered by an empty tmpfs (the command sees an empty directory instead of the contents);
  • fsReadAllowWithinDeny → re-mounted read-only on top of the masked area;
  • the current directory and fsWriteAllowExtra → mounted read-write;
  • fsWriteDenyWithinAllow → downgraded to a read-only mount;
  • network off → --unshare-net.

Skipping the sandbox

  • The model can pass dangerously_disable_sandbox: true on a single call — it skips all sandbox layers (equivalent to running unsandboxed). Bash is treated as a destructive tool by the permissions system and usually needs your confirmation, but the sandbox lists themselves cannot be loosened per call — the four lists are assembled from configuration only at startup; the model's parameter can only tighten or skip entirely;
  • Background task exception: commands run with run_in_background: true (dev servers, log tails, etc.) are not sandboxed — these scenarios need network and access outside the project, and they already went through your confirmation when launched;
  • On platforms where the sandbox tooling is missing / unavailable, commands run unsandboxed and the Bash result notes the actual sandbox mode, so both the model and you can see whether the command was constrained.
Command Purpose
biu doctor Check sandbox tool availability and print merged rule counts
/permissions View permission rules and modes
/add-dir <path> --remember Append a writable working directory (persisted)
biu config validate Load all configuration layers and report problems