Skip to content

Isolating AI commands with --isolate

The --isolate flag re-executes bnerd x (TUI) and bnerd mcp-server (MCP) inside a filesystem-isolated environment. Inside the sandbox, the AI's filesystem tools (fs_read_file, fs_write_file, shell_exec, etc.) cannot read or write outside the current working directory plus a small allowlist of credential files.

It composes with --ai-mode (capability boundary). --ai-mode decides which tools the AI can call; --isolate decides where the filesystem-touching tools can reach.

Quick start

# Default isolation: workdir + creds + kubeconfig accessible
bnerd --isolate x

# Strict isolation: same, minus ~/.kube/config (so kube_* tools fail)
bnerd --isolate=strict x

# MCP server, isolated
bnerd --isolate mcp-server

# Maximum lockdown for a sketchy local model
bnerd --isolate=strict --ai-mode read-only x

Bare --isolate is equivalent to --isolate=on.

What's mounted

Both modes mount the same set, except strict drops ~/.kube/config:

Path Mode Purpose
$PWD rw Working directory — the only place the AI can mutate files
~/.bnerd.yaml ro CloudAPI token, AI provider keys
~/.bnerd/ rw Audit log, conversations, reports persist across runs
~/.gitconfig, ~/.config/git/ ro git author/email/signing settings
~/.config/helm/ ro Helm repository config
~/.gnupg/ ro GPG keyring + trustdb (commit signing)
~/.kube/config ro Kubernetes cluster access (omitted in --isolate=strict)
$SSH_AUTH_SOCK rw SSH agent (git push, signed pushes)
/run/user/$UID/gnupg/ rw GPG agent runtime socket

Everything else under $HOME is hidden by an in-memory tmpfs: ~/.ssh, ~/.aws, ~/.azure, ~/.config/gcloud, ~/.docker, other repos in ~/projects/, your scratchpad in ~/Documents/ — all invisible to the AI.

Network egress is unrestricted in both modes. A future --isolate-allow-net flag will add destination allowlisting.

Runtime prerequisites

--isolate picks the first available runtime in this order:

  1. bubblewrap (bwrap) — preferred on Linux. ~50ms cold start, no daemon, host PATH inherited (so terraform, ansible, etc. all work).
  2. podman — used when bwrap is unavailable. Rootless by default; clean UID/GID via --userns=keep-id.
  3. docker — universal fallback.

Run bnerd isolate doctor to see which runtime is selected and what mounts will be applied:

$ bnerd isolate doctor
bnerd isolation diagnostics
===========================

Runtimes (priority order):
  bwrap    available
  podman   available
  docker   available

Selected: bwrap

Mounts for --isolate=on:
  bind   rw     ~/projects/myrepo
  bind   ro     ~/.bnerd.yaml
  ...

Installing bubblewrap

sudo apt install bubblewrap
sudo dnf install bubblewrap
sudo pacman -S bubblewrap
sudo apk add bubblewrap

Installing podman / docker

sudo apt install podman   # or dnf, pacman
brew install podman
podman machine init
podman machine start

Container image (podman/docker only)

When the runtime is podman or docker, --isolate runs inside a locally-built image tagged bnerd-isolate:v<bnerd-version>. The first invocation auto-builds it (~2 min). Subsequent runs use the cache.

The image bakes in git, kubectl, helm, jq, yq, curl, wget, make, openssh-client, gnupg2, plus standard coreutils. Not baked in: terraform, ansible, flux, node, npm, python. If your AI workflow needs those, either:

  • Use bubblewrap (Linux) — host PATH is inherited, so any host-installed tool works.
  • Install them inside $PWD/.local/bin/ — that path is mounted RW so binaries persist.
  • Submit a PR adding the tool to pkg/isolate/Dockerfile.isolate if widely useful.

Image management:

bnerd isolate build   # build the image now
bnerd isolate prune   # remove the cached image
bnerd isolate doctor  # show which image (if any) is cached

What --isolate protects against

  • The AI reading files outside $PWD and the explicit RO mounts (e.g. ~/.ssh/id_rsa, other repos, sibling worktrees).
  • The AI writing files outside $PWD (e.g. accidentally creating ~/scratch.txt).
  • A confused or prompt-injected AI exfiltrating arbitrary host files via web_fetch — those files aren't visible to the AI in the first place.
  • The AI killing host processes (PID namespace), affecting host IPC/UTS, or escalating privileges (--cap-drop=ALL, --security-opt=no-new-privileges).

What --isolate does NOT protect against

  • Network exfiltration of in-scope data. The AI can still POST $PWD/CLAUDE.md to attacker.com via web_fetch. (v1 limitation; future --isolate-allow-net will fix this — see the deferred design notes.)
  • Cluster damage via mounted kubeconfig in non-strict mode. kube_apply, kube_delete, kube_drain still run with whatever permissions the kubeconfig grants. Use --isolate=strict to drop the kubeconfig, or --ai-mode read-only to bound the AI to read-only operations.
  • CloudAPI damage. ~/.bnerd.yaml is mounted, so the AI can call destructive CloudAPI tools subject to --ai-mode.
  • Audit log tampering. ~/.bnerd/audit.log is mounted RW so writes persist across runs, but a determined AI inside isolation can fs_write_file over it. The log is a debugging tool, not a tamper-evident audit trail.
  • Forwarded-agent abuse. SSH and GPG agent sockets are forwarded so git push and git commit -S work. A poisoned model could authenticate as you to any host your keys reach. If this matters, set SSH_AUTH_SOCK= (empty) before running bnerd --isolate.

Troubleshooting

"no isolation runtime found"

You don't have bubblewrap, podman, or docker installed. See the Installing bubblewrap and Installing podman / docker sections.

"kubeconfig not found" inside --isolate=strict

Expected — strict mode intentionally omits ~/.kube/config. Use plain --isolate if you need cluster access.

terraform: command not found inside --isolate (podman/docker)

The image doesn't bake in terraform. Use bubblewrap on Linux, or install terraform into $PWD/.local/bin/ (which is mounted RW).

TUI looks wrong / colors broken

Ensure TERM is forwarded (it's on the env allowlist by default) and that you're invoking with a real PTY. Inside an MCP client (which uses stdio), bnerd --isolate mcp-server correctly suppresses TTY allocation.

Image build fails

Run bnerd isolate build directly to see the build output. Common causes: no internet (the image fetches kubectl/helm/yq during build), insufficient disk space, or selinux/apparmor blocking the build daemon.

See also