Skip to content

Sandboxes

Alex Clarke edited this page Jun 18, 2026 · 4 revisions

Coyote can launch itself inside an isolated Docker Sandbox (sbx) with one command. This gives you a disposable, hypervisor-isolated environment where Coyote runs against your project workspace without having access to the rest of your host machine.

Sandbox mode is powered by Docker Sandboxes. Coyote does not implement its own container layer. It delegates everything to the sbx CLI: lifecycle, isolation, networking, image management, and process supervision. Coyote's only job is to orchestrate the bootstrap (build a sandbox of the right shape, copy your config + vault credentials in, attach you to a running session) and then get out of the way.

Quick Start

Install the sbx CLI first (Docker Sandboxes install guide). Then from any directory:

# Sandbox named after the current directory's basename
coyote --sandbox

# Or with an explicit name
coyote --sandbox my-project

# Create without copying your host config (clean-slate sandbox)
coyote --sandbox throwaway --fresh

# Skip all sbx mixin discovery and application (debugging / minimum sandbox)
coyote --sandbox --no-mixins

# True bare-bones, isolated sandbox with fresh Coyote install
coyote --sandbox --fresh --no-mixins

The first run takes a few minutes (building the Coyote sandbox image, installing Rust/uv/build deps, compiling coyote-ai). Subsequent attaches to the same sandbox are instant: Your config, vault state, sessions, OAuth tokens, and installed tools persist inside the sandbox until you sbx rm it.

Re-running coyote --sandbox [NAME] with the same name re-attaches to the existing sandbox silently rather than creating a fresh one. --fresh and --no-mixins are ignored on re-attach (they only affect new sandbox creation).

Tip: Inside the sandbox REPL, prefix any line with ! to run a shell command without going through sbx exec <name> -- <cmd> to modify the sandbox state; .e.g, !apt-get update, !git pull, !cargo build, etc. Output streams to your terminal, Ctrl-C interrupts long-running commands, and you don't spend any tokens because no output is sent to the LLM. See REPL - !<command> for details.

What --sandbox actually does

Coyote bundles a pre-built sbx kit (its sandbox manifest) directly inside the binary. When you run --sandbox, it executes this sequence:

# 1. Extract the embedded base kit to your local cache (skipped on hash-match)
coyote --info | grep -i "sbx_kit_dir" | awk '{print $2}'

# 2. Discover mixins:
#    - Walk known discovery paths for user-authored sbx-mixin.yaml files
#    - Inspect your secrets_provider type; if non-Local, also extract the
#      matching built-in vault-provider mixin to:
#      $XDG_CACHE_HOME/coyote/sbx-vault-mixins/<provider>/

# 3. Log what's about to be applied (info! and println!). See "Verbose
#    mixin log" below.

# 4. Check if a sandbox with this name already exists
sbx ls

# 5. If not, create it with the base kit + every discovered mixin layered on
sbx create \
  --kit <cache>/sbx-kit/ \
  --kit <vault-provider-mixin-if-any> \
  --kit <user-mixin-1> \
  --kit <user-mixin-N> \
  coyote --name <NAME> .

# 6. Copy your host config into the sandbox (skipped if --fresh)
sbx exec <NAME> sh -c "sudo mkdir -p /home/agent/.config && sudo chown agent:agent /home/agent/.config"
sbx cp ~/.config/coyote/ <NAME>:/home/agent/.config/

# 7. Copy your vault password file, if a local provider is configured (skipped if --fresh)
sbx exec <NAME> sh -c "sudo mkdir -p <parent> && sudo chown agent:agent <parent>"
sbx cp <host-password-file> <NAME>:<destination>

# 8. Hand control to sbx (Coyote's process is replaced)
exec sbx run <NAME> --kit <cache>/sbx-kit/

Once sbx run takes over, Coyote on the host exits and your terminal is connected to Coyote inside the sandbox. All signals (Ctrl-C, etc.) flow straight through.

Coyote handles steps 1–3, 6, 7, and the --name argument of step 5. Everything else is sbx doing its job.

Verbose mixin log

Before sbx create runs, Coyote emits a single block to both the log and stdout naming every mixin about to be applied:

Applying 3 sbx mixin(s):
  <built-in: vault-one_password>                          (adds: 1 install, 6 domains)
  ~/.config/coyote/functions/sbx-mixin.yaml               (adds: 0 installs, 20 domains)
  ~/.config/coyote/agents/my-python-dev/sbx-mixin.yaml    (adds: 1 install, 1 domain)

If zero mixins were discovered, you'll see No sbx mixins discovered. in the log (no terminal noise). If you launched with --no-mixins, you'll see Mixin discovery disabled via --no-mixins. instead.

Skim this log on first launch and after installing any shared configuration bundle. It's your audit point for what each mixin grants in terms of installs and network domain allowances.

Lifecycle: use the sbx CLI

Coyote intentionally does not wrap sandbox lifecycle commands. Use sbx directly as it's the single source of truth:

Task Command
List sandboxes sbx ls
Open a shell in a running sandbox sbx exec <NAME>
Run a one-off command in a sandbox sbx exec <NAME> <command>
Copy files in/out sbx cp <src> <dest> (use <NAME>:/path to reference a sandbox)
Stop a sandbox without removing it sbx stop <NAME>
Remove a sandbox (destroys all state) sbx rm <NAME>
Forward a port to the host sbx ports <NAME>
Diagnose problems sbx diagnose

Run sbx --help for the full surface. None of these are reimplemented in Coyote.

Vault Behavior

Coyote's sandbox bootstrap is aware of your vault configuration and behaves differently depending on which provider you've chosen:

Local provider (default)

Coyote resolves your vault_password_file (or secrets_provider.password_file) from your host config and copies it into the sandbox at the matching location. The path is rewritten when it lives under $HOME (so ~/.coyote_password on your host becomes /home/agent/.coyote_password in the sandbox), but kept verbatim for absolute paths outside $HOME (so /etc/coyote/.coyote_password is copied to /etc/coyote/.coyote_password inside the sandbox). No further action is needed — your vault works on first launch.

Additionally, at vault initialization time inside the sandbox, Coyote auto-retranslates any vault_password_file (or secrets_provider.password_file) that points to a host home path (/home/<X>/..., /Users/<X>/..., or C:\Users\<X>\...) to the corresponding /home/agent/... path. This means custom password-file locations under your host's home directory (e.g. /home/atusa/.config/coyote/.password, /Users/atusa/..., or C:\Users\atusa\...) resolve correctly in the sandbox without any config rewriting — your config stays pristine. An INFO-level log line is emitted whenever this translation kicks in, so you can verify the resolution if needed.

Host OS support for the password file copy

Host OS Password file under $HOME/%USERPROFILE% Password file outside $HOME/%USERPROFILE%
Linux Copied to /home/agent/<rel> Copied verbatim (e.g. /etc/coyote/.password -> /etc/coyote/.password)
macOS Copied to /home/agent/<rel> Copied verbatim
Windows Copied to /home/agent/<rel> (with backslashes normalized to forward slashes) Refused with a clear error. Windows paths outside %USERPROFILE% (e.g. C:\Program Files\Coyote\vault.txt) can't be projected into a Linux sandbox. Move the file under your user profile (C:\Users\<you>\...).

Non-Local provider (auto-installed CLI mixin)

If secrets_provider.type is anything other than local, Coyote automatically applies a built-in mixin that installs the corresponding provider CLI inside the sandbox. The mixin also allowlists the domains the CLI needs to authenticate:

Provider CLI installed Auto-applied mixin
one_password op <built-in: vault-one_password>
azure_key_vault az <built-in: vault-azure_key_vault>
gopass gopass <built-in: vault-gopass>
aws_secrets_manager aws (v2) <built-in: vault-aws_secrets_manager>
gcp_secret_manager gcloud <built-in: vault-gcp_secret_manager>

The mixin installs the CLI but does not log you in — see the next section.

Re-Authenticating Your Vault in a Sandbox

After the sandbox is created, you must authenticate the provider CLI inside the sandbox once. Either use the built-in REPL command passthrough to execute the login command in the sandbox when already open to the REPL, or use the Docker sbx CLI directly via sbx exec <NAME> <command>:

Provider First-time sandbox auth
local Nothing (password file auto-copied)
one_password
  • Command Passthrough: > !op signin
  • Docker Sbx CLI: sbx exec <NAME> op signin
azure_key_vault
  • Command Passthrough: > !az login
  • Docker Sbx CLI: sbx exec <NAME> az login
gopass
  • Command Passthrough: > !gopass clone <your-git-remote> (or !gopass setup)
  • Docker Sbx CLI: sbx exec <NAME> gopass clone <your-git-remote> (or gopass setup)
aws_secrets_manager
  • Command Passthrough: > !aws configure sso (or !aws sso login --profile <profile>)
  • Docker Sbx CLI: sbx exec <NAME> aws configure sso (or aws sso login --profile <profile>)
gcp_secret_manager
  • Command Passthrough: > !gcloud auth application-default login
  • Docker Sbx CLI: sbx exec <NAME> gcloud auth application-default login

These commands authenticate once per sandbox, meaning credentials persist on the sandbox filesystem until you sbx rm <NAME>.

If you forget to re-auth, the vault will fail to decrypt the first time Coyote needs a secret inside the sandbox, and you'll see a clear error from gman pointing at the right login command.

Manual credential transfer (alternative)

If you'd rather not re-authenticate inside the sandbox, you can transfer your host credentials directly:

sbx cp ~/.aws <NAME>:/home/agent/.aws
sbx cp ~/.config/gcloud <NAME>:/home/agent/.config/gcloud

This is faster but bleeds host state into the sandbox. It's your call. Coyote intentionally doesn't do this automatically.

Extending the Sandbox: Auto-Discovered Mixins

To add applications, network allowances, environment variables, or files to your sandbox beyond what the base kit provides, drop an sbx-mixin.yaml file at any of these locations and Coyote will discover and apply it automatically on every coyote --sandbox:

Discovery path Purpose
<config-dir>/sbx-mixin.yaml Top-level user mixin. Applies to every sandbox you launch
<config-dir>>/functions/sbx-mixin.yaml Mixin for global custom tools
<config-dir>/functions/<tool>/sbx-mixin.yaml Per-custom-tool mixin (alphabetical)
<config-dir>/agents/<agent>/sbx-mixin.yaml Per-agent mixin (alphabetical), applied for every sandbox
<workspace-root>/.coyote/sbx-mixin.yaml Workspace mixin (walk-up search from cwd)

Coyote does not recursively scan for sbx-mixin.yaml anywhere else. These five paths are the whole surface, meaning anything outside is ignored.

The <workspace-root> walk follows the same convention as Memory: Coyote walks up from your current directory looking for the first ancestor containing .coyote/sbx-mixin.yaml. Use this to ship per-project sandbox extensions in your repo.

Mixin file format

Mixins are standard sbx kit YAML with kind: mixin. Here's a complete working example that adds ruff to every sandbox:

# <config-dir>/sbx-mixin.yaml
schemaVersion: "1"
kind: mixin
name: my-python-tooling
description: Install the ruff Python linter for use in any sandbox

network:
  allowedDomains:
    - "files.pythonhosted.org:443"
    - "pypi.org:443"

commands:
  install:
    - command: "uv tool install ruff"
      user: "1000"
      description: Install ruff via uv

After saving this file, your next coyote --sandbox automatically applies it. See the official sbx kit reference for the full mixin schema.

Built-in mixins Coyote ships

Coyote already ships mixins for the most common needs. They get auto-applied whenever they're relevant — you don't need to do anything to enable them:

  • Built-in tools mixin (auto-applied when coyote --install functions has run). Allowlists the domains used by every built-in global tool and the default MCP server set: Wikipedia, arxiv, jina, wttr, WolframAlpha, Perplexity, Tavily, Twilio, github MCP, atlassian MCP, ddg-search MCP, npm registry, Docker registries.
  • Vault provider mixins (auto-applied when secrets_provider.type matches). See Vault Behavior above. One for each of 1Password, Azure, gopass, AWS, and GCP.

You can list everything that's about to be applied via the verbose mixin log on every launch.

Sharing mixins with others

A mixin in <config-dir>/agents/<agent>/sbx-mixin.yaml travels with the agent if you publish it via the Sharing Configurations mechanism. See that page for the security implications. Installing a bundle that ships an sbx-mixin.yaml grants the included install commands and network domains the next time you coyote --sandbox.

Custom Kit Override

If you want to point Coyote at a completely different kit instead of the embedded one (e.g. for development, hardening, or a fork), set COYOTE_SANDBOX_KIT:

COYOTE_SANDBOX_KIT=./my-fork-of-coyote-kit/ coyote --sandbox

When this environment variable is set, Coyote skips the embedded-kit extraction entirely and passes the override path straight to sbx. Use this sparingly. The embedded kit is what every documented coyote --sandbox workflow assumes.

Nesting

Sandbox mode refuses to start a sandbox if $IS_SANDBOX is already set. The bundled kit exports IS_SANDBOX=1 inside every Coyote sandbox, so running coyote --sandbox from within a sandbox is a no-op error because you're already in one.

Troubleshooting

If something looks wrong, these are your three debugging flags:

Flag Effect Use when…
--no-mixins Skips all mixin discovery (built-in vault + user-authored) A mixin is causing sbx create to fail and you want to bisect
--fresh Skips host config + vault password file copy You suspect your host config is contaminating the sandbox; isolation test
COYOTE_SANDBOX_KIT Points at an alternate base kit instead of the embedded one Developing a fork of the kit or hardening for a specific environment

Common failures

  • A mixin causes sbx create to abort. Re-run with --no-mixins to confirm the base kit works. Then check the verbose mixin log to identify which mixin was being applied when it failed. Open that mixin file and inspect its commands.install block.
  • A malformed sbx-mixin.yaml aborts launch before sbx create runs. Coyote fails fast with a parse error naming the file. Fix the YAML or temporarily move it aside.
  • Vault decryption fails inside the sandbox. You forgot to re-auth your non-Local provider. See Re-Authenticating Your Vault in a Sandbox.
  • A custom tool fails with command not found. The tool's required binary isn't installed in the sandbox. Add it to the matching mixin (per-tool, per-agent, or top-level user mixin). See Custom Tools.
  • A network request from inside the sandbox hangs or fails. The domain isn't in any allowedDomains block. Add it to a user mixin. The sandbox's proxy denies all unlisted traffic silently.

When not to use sandbox mode

  • You only need filesystem isolation, not network or process isolation. sandbox mode trades a few minutes of first-run setup and ongoing VM overhead (one Docker daemon per sandbox) for full hypervisor isolation. If you only want to restrict which files Coyote can touch, the existing per-tool permission model is lighter weight.
  • Fast one-off shell commands. Sandbox attach is fast on warm sandboxes, but cold-start (image + kit installs) takes minutes. Plain coyote --execute "..." on the host is faster for trivial work.
  • You need to interact with host-only resources (the host's clipboard, host system services, certain GUI integrations). Sandboxes can't see the host beyond the mounted workspace and the proxy-mediated network.

See Also

Clone this wiki locally