docs: Added documentation for the new sandbox mode

2026-06-18 11:53:45 -06:00
parent 43e470b463
commit 89fb4d62b3
11 changed files with 682 additions and 10 deletions
+60
@@ -21,6 +21,10 @@ coyote --info | grep 'agents_dir' | awk '{print $2}'
If you're looking for more example agents, refer to the [built-in agents](https://github.com/Dark-Alex-17/coyote/blob/main/assets/agents).
> **Running an agent inside a [Docker Sandbox](Sandboxes)?** If the agent needs external binaries (`pandoc`, `usql`,
> etc.) or specific network domains, ship a per-agent `sbx-mixin.yaml` alongside its `config.yaml`. See
> [Sandbox Support](#sandbox-support) at the bottom of this page.
---
# Directory Structure
@@ -796,3 +800,59 @@ Coyote writes these built-in agents to your agents directory on first run and ne
edits you make to them are preserved across Coyote updates. To discard your local changes and reinstall the built-in
agents from the current Coyote build, run `coyote --install agents` (or `.install agents` in the REPL). Agents you created
yourself are not affected.
# Sandbox Support
If you run Coyote inside a [Sandbox](Sandboxes), agents that need external binaries or specific network domains
must ship a per-agent `sbx-mixin.yaml` alongside their `config.yaml` (or `graph.yaml`). Coyote discovers these
automatically on every `coyote --sandbox`. No flags or registration required.
Layout:
```
<coyote-config-dir>/agents/<my-agent>/
├── config.yaml
├── tools.sh
└── sbx-mixin.yaml # ← gets auto-applied alongside the embedded base kit
```
## Example
A custom agent that uses `httpie` to call an internal API:
```yaml
# <coyote-config-dir>/agents/internal-api-agent/sbx-mixin.yaml
schemaVersion: "1"
kind: mixin
name: agent-internal-api
description: Installs httpie and allows access to our internal API host
network:
allowedDomains:
- "internal-api.example.com:443"
commands:
install:
- command: |
sudo apt-get update
sudo apt-get install -y httpie
user: "1000"
description: Install httpie for the internal-api-agent
```
See [Sandboxes: Extending the Sandbox](Sandboxes#extending-the-sandbox-auto-discovered-mixins) for the full discovery
path table and the [official sbx mixin reference](https://docs.docker.com/ai/sandboxes/customize/kits/) for the schema.
## Sandbox caveats
- **Per-agent mixins are applied on _every_ `coyote --sandbox` regardless of which agent you invoke.** Mixin discovery
is global by design (matches Coyote's "everything in the config dir is live" model). Users with many agents pay
the install cost for all of their mixins once on first-run; subsequent attaches are instant.
- **Agents that spawn sub-agents need the spawned agents' mixin requirements too.** Either co-locate them in the
parent agent's mixin or rely on each spawned agent shipping its own; both work.
- **Built-in agents shipped via `coyote --install agents` may project new `sbx-mixin.yaml` files into your config
on update.** Re-running the sandbox after such an install surfaces them in the verbose log with the extra
installs/domains they bring. Not a bug, just visibility.
- **The `sql` agent's `usql` install is in the base kit, not a per-agent mixin.** Same for `pandoc` (used by
`fetch_url_via_curl`). Both ship with every Coyote sandbox automatically. See `assets/sbx-kit/spec.yaml` for the
full base prereq list.
+70
@@ -24,6 +24,76 @@ Once you've created your custom tool, remember to add it to the `visible_tools`
to enable it globally. See the [Tools](Tools#enablingdisabling-global-tools) documentation for more information on how Coyote utilizes the
`visible_tools` array.
## Sandbox Support
If you run Coyote inside a [Sandbox](Sandboxes), custom tools need to declare their external binary and network
dependencies in an `sbx-mixin.yaml` next to the tool. Otherwise the tool fails silently inside the sandbox even though
it works on your host.
Two layouts work:
**Per-tool mixin:** Co-located with the tool itself, applies whenever any sandbox is launched. Use this when the tool
is meaningful as a portable unit you'd share via [Sharing Configurations](Sharing-Configurations):
```
<coyote-config-dir>/functions/<your-tool-dir>/
├── tools.sh
└── sbx-mixin.yaml
```
**Global custom-tools mixin:** Covers all your custom tools at once. Use this when several tools share the same
binary/domain needs and you don't want to duplicate:
```
<coyote-config-dir>/functions/sbx-mixin.yaml
```
Both are auto-discovered by Coyote on every `coyote --sandbox`. No flags or registration required.
### Example: a custom tool that uses `httpie`
```
<coyote-config-dir>/functions/my-httpie-tool/
├── tools.sh
└── sbx-mixin.yaml
```
`sbx-mixin.yaml`:
```yaml
schemaVersion: "1"
kind: mixin
name: my-httpie-tool
description: Adds httpie + access to example-api.com for the my-httpie-tool custom tool
network:
allowedDomains:
- "example-api.com:443"
commands:
install:
- command: |
sudo apt-get update
sudo apt-get install -y httpie
user: "1000"
description: Install httpie for use by my-httpie-tool
```
See [Sandboxes: Extending the Sandbox](Sandboxes#extending-the-sandbox-auto-discovered-mixins) for the full discovery
path table and the [official sbx mixin reference](https://docs.docker.com/ai/sandboxes/customize/kits/) for the schema.
### Sandbox caveats
- **Custom tools that shell out to host-installed binaries fail in the sandbox** unless those binaries are also
installed by the tool's mixin (or a global mixin, or the base kit).
- **Custom tools that hit external APIs fail on DNS resolution** if the domain isn't in any `allowedDomains` block.
The sandbox proxy denies all unlisted traffic.
- **Mixin install steps run as UID 1000 (the `agent` user) with passwordless sudo.** Don't assume root home paths;
use `~/` or explicit `/home/agent/` if you need a known location.
- **When sharing a custom tool, ship its `sbx-mixin.yaml` alongside it.** Without it, recipients will hit silent
sandbox failures with no obvious cause. See [Sharing Configurations: Sandbox Implications](Sharing-Configurations#sandbox-implications)
for the security implications of a shared mixin.
## Environment Variables
All tools have access to the following environment variables that provide context about the current execution environment:
+16
@@ -83,6 +83,22 @@ You can also customize the location of full agent configurations using the follo
| `<AGENT_NAME>_INSTRUCTIONS` | Customize the `instructions` for the agent; e.g. `SQL_INSTRUCTIONS` |
| `<AGENT_NAME>_VARIABLES` | Customize the `variables` used for the agent (in JSON format of `[{"key1": "value1", "key2": "value2"}]`); <br>e.g. `SQL_VARIABLES` |
# Sandbox Related Variables
The following variable controls Coyote's [Sandbox mode](Sandboxes):
| Environment Variable | Description | Default Value |
|----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------|
| `COYOTE_SANDBOX_KIT` | Override the path to the sbx kit used by `coyote --sandbox`. When set, Coyote skips extracting its embedded kit and passes this directory to `sbx create --kit` instead. Use for kit development, hardening, or pointing at a fork of the embedded kit. | `coyote --info \| grep sbx_kit_dir \| awk '{print $2}'` (extracted) |
**Notes:**
- If `COYOTE_SANDBOX_KIT` points to a non-existent path, `coyote --sandbox` aborts with an error before any sandbox is
created.
- The path is passed verbatim to `sbx create --kit`; it must be a valid sbx kit (containing `spec.yaml`), not just any
directory.
- No environment variables map to the new sandbox-mode CLI flags (`--fresh`, `--no-mixins`). These are intentionally
CLI-only. They're one-time per-invocation decisions, not configuration knobs.
# Logging Related Variables
The following variables can be used to change the log level of Coyote or the location of the log file:
+1
@@ -27,6 +27,7 @@ Coming from [AIChat](https://github.com/sigoden/aichat)? Follow the [migration g
* [REPL](REPL): Interactive Read-Eval-Print Loop for conversational interactions with LLMs and Coyote.
* [Custom REPL Prompt](REPL-Prompt): Customize the REPL prompt to provide useful contextual information.
* [Vault](Vault): Securely store and manage sensitive information such as API keys and credentials.
* [Sandboxes](Sandboxes): Launch Coyote inside an isolated [Docker Sandbox](https://docs.docker.com/ai/sandboxes/) with one command. Host config and vault credentials are projected in automatically; everything else is delegated to the `sbx` CLI.
* [Shell Integrations](Shell-Integrations): Seamlessly integrate Coyote with your shell environment for enhanced command-line assistance.
* [Function Calling](Tools#Tools): Leverage function calling capabilities to extend Coyote's functionality with custom tools
* [Creating Custom Tools](Custom-Tools): You can create your own custom tools to enhance Coyote's capabilities.
+46 -1
@@ -36,12 +36,16 @@ shell-style expansion.
Every server entry **must** include a `"type"` field set to one of: `"stdio"`, `"http"`, or `"sse"`.
> **Running inside a [Docker Sandbox](Sandboxes)?** MCP servers often need extra network allowances beyond what the base
> kit provides. See [Sandbox Compatibility](#sandbox-compatibility) at the bottom of this page for details and
> common gotchas.
## Transport Types
Coyote supports three MCP transport types:
| Type | Use Case |
|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|
|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `stdio` | Spawns a local subprocess and communicates over stdin/stdout |
| `http` | Connects to a remote server via [Streamable HTTP](https://modelcontextprotocol.io/docs/concepts/transports#streamable-http) |
| `sse` | Connects to a remote server via the legacy [HTTP+SSE](https://modelcontextprotocol.io/docs/concepts/transports#http-with-sse) transport (deprecated in the MCP spec; prefer `http` where the server supports it) |
@@ -214,3 +218,44 @@ The values for `mapping_mcp_servers` are inherited from the [global configuratio
For more information about agents, refer to the [Agents](Agents) documentation.
For a full example configuration for an agent, see the [Agent Configuration Example](https://github.com/Dark-Alex-17/coyote/blob/main/config.agent.example.yaml) file.
# Sandbox Compatibility
If you run Coyote inside a [Sandbox](Sandboxes), MCP servers often need extra setup beyond what works on your
host because the sandbox's network proxy denies all unlisted domains.
The bundled `assets/functions/sbx-mixin.yaml` (installed via `coyote --install functions`) already allowlists the
defaults shipped in the built-in `mcp.json`: github MCP (`api.githubcopilot.com`), atlassian MCP (`mcp.atlassian.com`),
ddg-search MCP (`duckduckgo.com` + subdomains), plus the npm registry and Docker registries that npx/uvx-based MCP
servers pull from.
For your own MCP servers, add the relevant domains to a user-level `sbx-mixin.yaml`. Brief example:
```yaml
# ~/.config/coyote/sbx-mixin.yaml
schemaVersion: "1"
kind: mixin
name: my-mcp-domains
network:
allowedDomains:
- "api.example-mcp.com:443"
- "auth.example-mcp.com:443"
```
See [Sandboxes: Extending the Sandbox](Sandboxes#extending-the-sandbox-auto-discovered-mixins) for the full discovery
path table.
## Sandbox caveats
- **Containerized MCP servers (`docker run -i ...`) require their image registry to be in `allowedDomains`.** The
sandbox's nested Docker daemon needs to pull the image. Declare `ghcr.io`, `registry-1.docker.io`, and
`auth.docker.io` (or whichever registry you pull from) explicitly. The built-in `functions/sbx-mixin.yaml` already
covers these for the defaults.
- **OAuth-based remote MCP servers (e.g. Atlassian via `mcp-remote`) need the OAuth host's domain too.** Failures
here often look like indefinite hangs at "Authorizing..." with no useful error.
- **Host file mounts do not survive into the sandbox.** Only the project workspace is mounted. If your MCP server
reads secrets from a file on the host (e.g. `~/.config/<service>/credentials`), copy it in via `sbx cp` before
starting Coyote, or inject the secret as an env var via the [Vault](Vault) instead.
- **The `npx`/`uvx` MCP server install pulls from public registries on first run.** That can take 30+ seconds
inside a fresh sandbox while it downloads and compiles. This is normal. Subsequent attaches reuse the cached
install.
+313
@@ -0,0 +1,313 @@
# Sandboxes
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](https://docs.docker.com/ai/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](https://docs.docker.com/ai/sandboxes/get-started/)). Then from any directory:
```bash
# 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>`](REPL#command---run-an-arbitrary-shell-command) for details.
## What `--sandbox` actually does
Coyote bundles a pre-built [sbx kit](https://docs.docker.com/ai/sandboxes/customize/kits/) (its sandbox manifest) directly inside the binary. When you run `--sandbox`,
it executes this sequence:
```bash
# 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 13, 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](Sharing-Configurations). 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](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](REPL#command---run-an-arbitrary-shell-command) 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` | <ul><li>**Command Passthrough:** `> !op signin` </li><li>**Docker Sbx CLI:** `sbx exec <NAME> op signin` </li></ul> |
| `azure_key_vault` | <ul><li>**Command Passthrough:** `> !az login` </li><li>**Docker Sbx CLI:** `sbx exec <NAME> az login` </li></ul> |
| `gopass` | <ul><li>**Command Passthrough:** `> !gopass clone <your-git-remote>` (or `!gopass setup`) </li><li>**Docker Sbx CLI:** `sbx exec <NAME> gopass clone <your-git-remote>` (or `gopass setup`) </li></ul> |
| `aws_secrets_manager` | <ul><li>**Command Passthrough:** `> !aws configure sso` (or `!aws sso login --profile <profile>`) </li><li>**Docker Sbx CLI:** `sbx exec <NAME> aws configure sso` (or `aws sso login --profile <profile>`) </li></ul> |
| `gcp_secret_manager` | <ul><li>**Command Passthrough:** `> !gcloud auth application-default login` </li><li>**Docker Sbx CLI:** `sbx exec <NAME> gcloud auth application-default login` </li></ul> |
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:
```bash
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](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:
```yaml
# <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](https://docs.docker.com/ai/sandboxes/customize/kits/)
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](#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](#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](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`:
```bash
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](#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](Custom-Tools#sandbox-support).
- **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
- [Docker Sandboxes: Official Docs](https://docs.docker.com/ai/sandboxes/)
- [Vault](Vault): How Coyote resolves the password file that gets copied in
- [Environment Variables](Environment-Variables): Including `COYOTE_SANDBOX_KIT`
- [Clients](Clients#authentication): Auth flows you may need to redo inside the sandbox for OAuth providers
+55 -2
@@ -1,5 +1,5 @@
Coyote ships with a built-in mechanism for installing and sharing configurations (i.e. agents, roles, macros, tools, and MCP
servers) directly from any git repository. This makes it easy to:
Coyote ships with a built-in mechanism for installing and sharing configurations (i.e. agents, roles, macros, tools, and
MCP servers) directly from any git repository. This makes it easy to:
- Sync your Coyote setup across multiple machines.
- Share your work with teammates or the community.
@@ -36,6 +36,11 @@ or from inside the Coyote REPL:
That's it. Coyote clones the repo to a temp directory, scans for recognized asset categories, and installs each into the
matching subdirectory of your user config. The temp clone is removed on completion.
> ⚠️ **Heads up: Sandbox implications.** If you use [Sandbox mode](Sandboxes) and the bundle includes any
> `sbx-mixin.yaml` files, installing it grants those mixins network access and install privileges inside your
> sandboxes the next time you `coyote --sandbox`. See [Sandbox Implications](#sandbox-implications) at the bottom of
> this page before installing bundles from sources you don't fully trust.
---
## Expected repository layout
@@ -344,3 +349,51 @@ Add them via `coyote --add-secret <NAME>` (CLI) or `.vault add <NAME>` (REPL). S
### `git` binary not found
Coyote shells out to the system `git` binary. Install git for your platform and ensure it's on your `PATH`.
---
## Sandbox Implications
If you use Coyote's [Sandbox mode](Sandboxes), installing a shared bundle that contains any `sbx-mixin.yaml`
files is a **privilege escalation event**. Those mixins are auto-discovered and applied silently on your next
`coyote --sandbox`, granting:
- **Network access** to every domain listed in the mixin's `network.allowedDomains`.
- **Install commands** run with passwordless sudo inside the sandbox (the agent user has full sudo).
- **Environment variables** set in `environment.variables`.
This is fine for bundles you trust (your own configs, your team's shared repo). It can be dangerous for bundles from
unknown sources.
### Before installing a bundle from a source you don't fully trust
1. **Grep the repo for `sbx-mixin.yaml` files** and read each one:
```sh
git clone --depth 1 https://github.com/<source>/<repo> /tmp/audit
find /tmp/audit -name 'sbx-mixin.yaml' -exec cat {} +
```
2. **Look specifically at**:
- `network.allowedDomains`: What domains are being added to the sandbox's allowlist?
- `commands.install`: What commands run with passwordless sudo? Do they pull from a recognized source
(apt repo, official installer, signed release) or arbitrary URLs?
- `environment.variables`: Do any look like credential exfiltration vectors (`*_API_URL` pointing at unknown
hosts, `LD_PRELOAD`, etc.)?
3. **First-run with `--no-mixins` to verify the bundle's non-sandbox features work** without granting any sandbox
elevation:
```sh
coyote --sandbox skeptical-test --fresh --no-mixins
```
Then re-launch without `--no-mixins` once you've reviewed and accepted the mixins.
### Runtime audit
Every `coyote --sandbox` prints a [verbose mixin log](Sandboxes#verbose-mixin-log) listing each mixin about to be
applied along with its install/domain counts. Skim it the first time a bundle's mixin is applied. If something looks
off, abort with Ctrl-C (only safe before `sbx create` starts running install commands; once installs begin, use
`sbx rm <name>` after to clean up).
### When sharing your own bundle
If you publish a config repo, **document any `sbx-mixin.yaml` files in your `README.md`**. Explain what they add to
the sandbox and why. Users who land on your repo will trust you more if you make the privilege grants explicit
instead of letting them discover via grep.
+27 -1
@@ -14,7 +14,7 @@ The following tools are built-in to Coyote by default, and their default enabled
be enabled/disabled can be found in the [Configuration](#configuration) section below.
| Tool | Description | Enabled/Disabled |
|-------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------|
|--------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------|
| [`demo_py.py`](https://github.com/Dark-Alex-17/coyote/blob/main/assets/functions/tools/demo_py.py) | Demonstrates how to create a tool using Python and how to use comments. | 🔴 |
| [`demo_sh.sh`](https://github.com/Dark-Alex-17/coyote/blob/main/assets/functions/tools/demo_sh.sh) | Demonstrate how to create a tool using Bash and how to use comment tags. | 🔴 |
| [`demo_ts.ts`](https://github.com/Dark-Alex-17/coyote/blob/main/assets/functions/tools/demo_ts.ts) | Demonstrates how to create a tool using TypeScript and how to use JSDoc comments. | 🔴 |
@@ -52,6 +52,32 @@ preserved across Coyote updates. To discard local changes and reinstall the buil
run `coyote --install functions` (or `.install functions` in the REPL). Tools you created yourself are not affected, and
your `mcp.json` is left untouched. Use `coyote --install mcp_config` to reset that separately.
## Sandbox Compatibility
If you run Coyote inside a [Sandbox](Sandboxes), tools that need extra binaries or network access only work when
they're declared in an [`sbx-mixin.yaml`](Sandboxes#extending-the-sandbox-auto-discovered-mixins) somewhere on the discovery path. Coyote ships two relevant pre-built
mixins:
- **Base kit prerequisites:** The sandbox base kit already installs `jq`, `curl`, `git`, `uv`, `pandoc`, `bzip2`, and
`usql` for you. Built-in tools that depend on these need no further setup.
- **`assets/functions/sbx-mixin.yaml`:** When projected into your config via `coyote --install functions`, this mixin
allowlists the network domains the built-in tools and default MCP servers reach: Wikipedia, arxiv, jina, wttr,
WolframAlpha, Perplexity, Tavily, Twilio, github MCP, atlassian MCP, ddg-search MCP, npm registry, and common Docker
registries.
For tools you wrote yourself, see [Custom Tools — Sandbox Support](Custom-Tools#sandbox-support).
### Sandbox caveats
- Built-in tools that hit external services (e.g. `fetch_url_via_curl.sh`, the search tools, the weather tools) will
fail silently inside a sandbox unless their domains are declared in some mixin. The bundled global tools mixin
covers the built-in defaults; custom tools must extend.
- Tool failures inside the sandbox often look like generic command failures or DNS errors. Check the [sandbox proxy
log](Sandboxes#troubleshooting) before assuming the tool itself is broken.
- If a tool needs a binary that's not in the base kit _and_ not in any mixin, it will fail with `command not found`
even though it works fine on your host. Add the install step to the appropriate mixin and re-run
`coyote --sandbox <name>` after `sbx rm <name>`.
# Configuration
Tools can be used in a handful of contexts:
* Inside a session
+83
@@ -123,6 +123,89 @@ Once the provider is set up, the wizard continues with your LLM/API provider sel
If you set up Coyote with one provider and later want to switch, just edit your `config.yaml` to change (or add) the
`secrets_provider` block.
# Vault in Sandboxes
If you run Coyote inside a [Docker Sandbox](Sandboxes), your vault provider determines what (if anything) you need to do
to make it usable in the sandbox.
## Local provider
Nothing is required. Coyote auto-copies your vault password file into the sandbox at the matching location during
`coyote --sandbox`. Your existing config and encrypted vault.yml come along for the ride. See
[Sandboxes — Vault Behavior](Sandboxes#vault-behavior) for the exact path-rewriting rules.
If your `vault_password_file` (or `secrets_provider.password_file`) is set to a path under your *host's* home
directory (e.g. `/home/atusa/.config/coyote/.password`, `/Users/atusa/...` on macOS, or `C:\Users\atusa\...`
on Windows), Coyote inside the sandbox automatically retranslates that path to the corresponding
`/home/agent/...` location at vault initialization time. So your existing config doesn't need to be edited,
and a `coyote --info` inside the sandbox will still show the original path you configured. You'll see an
`INFO`-level log line noting the translation if it happens. This means custom password-file locations work
transparently in the sandbox.
### Windows-host limitation
On Windows hosts, the password file must live under `%USERPROFILE%` (i.e. `C:\Users\<you>\...`). If your
configured `vault_password_file` points anywhere outside the user profile (e.g. `C:\Program Files\Coyote\vault.txt`),
`coyote --sandbox` will refuse to start with a clear error directing you to move the file. The Linux sandbox
has no way to faithfully represent paths under `C:\Program Files\` or other Windows-only locations, so the file
copy can't proceed.
Linux and macOS hosts have no such restriction. Vault files outside the home directory are copied verbatim
into the sandbox at the same absolute path (e.g. `/etc/coyote/.password``/etc/coyote/.password` inside
the sandbox).
## Non-Local providers (one-time re-authentication needed)
For every other provider, Coyote auto-applies a built-in **sbx mixin** that installs the corresponding CLI inside the
sandbox. The mixin does **not** log you in! You must re-authenticate once per sandbox lifetime:
| Provider | First-time sandbox auth |
|-----------------------|--------------------------------------------------------------------------------------|
| `local` | Nothing (password file auto-copied) |
| `one_password` | `sbx exec <sandbox-name> op signin` |
| `azure_key_vault` | `sbx exec <sandbox-name> az login` |
| `gopass` | `sbx exec <sandbox-name> gopass clone <your-git-remote>` (or `gopass setup`) |
| `aws_secrets_manager` | `sbx exec <sandbox-name> aws configure sso` (or `aws sso login --profile <profile>`) |
| `gcp_secret_manager` | `sbx exec <sandbox-name> gcloud auth application-default login` |
Replace `<sandbox-name>` with whatever you passed to `coyote --sandbox <name>` (or the basename of your project
directory if you used the no-argument form).
### From inside the running Coyote REPL (alternative)
When `coyote --sandbox` is already running and you're at the REPL prompt inside the sandbox, you can run the auth
command directly with the [`!<command>` shell pass-through](REPL#command---run-an-arbitrary-shell-command) without opening a second terminal:
```
> !op signin
> !az login
> !gopass clone <your-git-remote>
> !aws configure sso
> !gcloud auth application-default login
```
The output streams to your REPL, Ctrl-C cancels, and you stay in the conversation. This is usually quicker than
exiting Coyote to run `sbx exec <sandbox-name> <command>` from your host shell. Use `sbx exec` when you're not
already inside the REPL, or when you want to launch an interactive sub-shell to step through a complex login flow.
## Things to know
- **Re-auth is per-sandbox, not per-Coyote-launch.** Once you authenticate, credentials persist inside the sandbox
filesystem until you `sbx rm <sandbox-name>`. Reopening the sandbox later does not re-prompt.
- **`sbx rm <sandbox-name>` wipes all cached credentials.** The next `coyote --sandbox <sandbox-name>` creates a fresh
sandbox that requires re-auth.
- **The built-in mixin installs the CLI but does not pre-configure it.** Users with multiple AWS profiles, Azure
subscriptions, GCP projects, or 1Password accounts must run the same `aws configure` / `az account set` /
`gcloud config set` / `op account add` they would on a fresh host.
- **gopass with self-hosted git remotes:** the built-in mixin only allows `github.com` and `gitlab.com` for cloning
your store. If you host the store elsewhere (Bitbucket, self-hosted GitLab, etc.), add the relevant host to your
own top-level `~/.config/coyote/sbx-mixin.yaml` under `network.allowedDomains`. Without this, `gopass clone` will
fail with a network error.
- **Manual credential transfer is supported as an alternative.** If you'd rather not re-authenticate inside the
sandbox, you can `sbx cp ~/.aws <sandbox-name>:/home/agent/.aws` (or equivalent) before starting Coyote. Coyote
intentionally does _not_ do this automatically. Re-auth inside the sandbox keeps host state out of the sandbox by
default. The choice is yours.
# Motivation
Coyote is intended to be highly configurable and adaptable to many different use cases. This means that users of Coyote
should be able to share configurations for agents, tools, roles, etc. with other users or even entire teams.
+5
@@ -65,4 +65,9 @@
## Configuration
- [Environment Variables](Environment-Variables)
- [Vault](Vault)
- [Sandboxes](Sandboxes)
- [Quick Start](Sandboxes#quick-start)
- [What `--sandbox` does](Sandboxes#what---sandbox-actually-does)
- [Vault Behavior](Sandboxes#vault-behavior)
- [Extending with Mixins](Sandboxes#extending-the-sandbox)
- [Themes](Themes)
Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB