docs: Updated documentation to reflect the new v2 sbx kit spec changes
+7
-6
@@ -898,16 +898,17 @@ A custom agent that uses `httpie` to call an internal API:
|
||||
|
||||
```yaml
|
||||
# <coyote-config-dir>/agents/internal-api-agent/sbx-mixin.yaml
|
||||
schemaVersion: "1"
|
||||
schemaVersion: "2"
|
||||
kind: mixin
|
||||
name: agent-internal-api
|
||||
description: Installs httpie and allows access to our internal API host
|
||||
|
||||
network:
|
||||
allowedDomains:
|
||||
- "internal-api.example.com:443"
|
||||
permissions:
|
||||
network:
|
||||
allow:
|
||||
- "internal-api.example.com"
|
||||
|
||||
commands:
|
||||
setup:
|
||||
install:
|
||||
- command: |
|
||||
sudo apt-get update
|
||||
@@ -931,4 +932,4 @@ path table and the [official sbx mixin reference](https://docs.docker.com/ai/san
|
||||
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.
|
||||
full base prereq list.
|
||||
+9
-7
@@ -61,16 +61,17 @@ Both are auto-discovered by Coyote on every `coyote --sandbox`. No flags or regi
|
||||
`sbx-mixin.yaml`:
|
||||
|
||||
```yaml
|
||||
schemaVersion: "1"
|
||||
schemaVersion: "2"
|
||||
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"
|
||||
permissions:
|
||||
network:
|
||||
allow:
|
||||
- "example-api.com"
|
||||
|
||||
commands:
|
||||
setup:
|
||||
install:
|
||||
- command: |
|
||||
sudo apt-get update
|
||||
@@ -86,7 +87,8 @@ path table and the [official sbx mixin reference](https://docs.docker.com/ai/san
|
||||
|
||||
- **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.
|
||||
- **Custom tools that hit external APIs fail on DNS resolution** if the domain isn't in any network allow list
|
||||
(`permissions.network.allow`; v1: `network.allowedDomains`).
|
||||
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.
|
||||
@@ -394,4 +396,4 @@ export function run(name: string): string {
|
||||
```
|
||||
|
||||
This is useful for pinning a specific Python version, using an alternative TypeScript runtime like
|
||||
[Bun](https://bun.sh/) or [Deno](https://deno.com/), or working with virtual environments.
|
||||
[Bun](https://bun.sh/) or [Deno](https://deno.com/), or working with virtual environments.
|
||||
+22
-13
@@ -436,25 +436,32 @@ For a full example configuration for an agent, see the [Agent Configuration Exam
|
||||
|
||||
# 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.
|
||||
If you run Coyote inside a [Sandbox](Sandboxes), the sandbox's network proxy denies all unlisted domains. Two
|
||||
mechanisms cover most MCP needs automatically:
|
||||
|
||||
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.
|
||||
- **Remote servers are auto-allowed.** At launch, Coyote generates a `coyote-mcp` mixin that adds every remote
|
||||
server `url` from your `mcp.json` to the sandbox's network allow list and declares your MCP secrets as sbx
|
||||
credentials, which are proxy-injected into request headers where possible, exposed as `COYOTE_SECRET_<NAME>`
|
||||
env vars otherwise. Any number of secrets per server is supported, and your `mcp.json` works unmodified inside
|
||||
the sandbox. See [Sandboxes: The generated coyote-mcp mixin](Sandboxes#the-generated-coyote-mcp-mixin).
|
||||
- **The bundled `functions/sbx-mixin.yaml`** (installed via `coyote --install functions`) allowlists what the
|
||||
built-in `mcp.json` needs beyond its URLs: the npm registry and Docker registries that npx/uvx/docker-based
|
||||
MCP servers pull from, the ddg-search endpoints, plus the github (`api.githubcopilot.com`) and atlassian
|
||||
(`mcp.atlassian.com`) MCP hosts.
|
||||
|
||||
For your own MCP servers, add the relevant domains to a user-level `sbx-mixin.yaml`. Brief example:
|
||||
You only need your own mixin for domains Coyote can't derive from `mcp.json`: OAuth authorization hosts,
|
||||
registries for custom containerized servers, or URLs hidden inside `args` (e.g. `mcp-remote <url>`). Brief example:
|
||||
|
||||
```yaml
|
||||
# ~/.config/coyote/sbx-mixin.yaml
|
||||
schemaVersion: "1"
|
||||
schemaVersion: "2"
|
||||
kind: mixin
|
||||
name: my-mcp-domains
|
||||
network:
|
||||
allowedDomains:
|
||||
- "api.example-mcp.com:443"
|
||||
- "auth.example-mcp.com:443"
|
||||
permissions:
|
||||
network:
|
||||
allow:
|
||||
- "auth.example-mcp.com"
|
||||
- "sso.example-corp.com"
|
||||
```
|
||||
|
||||
See [Sandboxes: Extending the Sandbox](Sandboxes#extending-the-sandbox-auto-discovered-mixins) for the full discovery
|
||||
@@ -462,7 +469,9 @@ path table.
|
||||
|
||||
## Sandbox caveats
|
||||
|
||||
- **Containerized MCP servers (`docker run -i ...`) require their image registry to be in `allowedDomains`.** The
|
||||
- **Servers whose URL lives in `args` are not auto-allowed** (e.g. `npx mcp-remote https://…`). Coyote only derives
|
||||
egress from each server's `url` field; add such domains to a user mixin.
|
||||
- **Containerized MCP servers (`docker run -i ...`) require their image registry in the network allow list.** 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.
|
||||
|
||||
+123
-24
@@ -19,7 +19,7 @@ coyote --sandbox
|
||||
# Or with an explicit name
|
||||
coyote --sandbox my-project
|
||||
|
||||
# Start fresh — no copied config or MCP secrets; LLM credentials still injected via proxy
|
||||
# Start fresh with no copied config, MCP secrets, or generated MCP mixin; LLM credentials still injected via proxy
|
||||
coyote --sandbox throwaway --fresh
|
||||
```
|
||||
|
||||
@@ -71,24 +71,30 @@ coyote --info | grep -i "sbx_kit_dir" | awk '{print $2}'
|
||||
# 4. Inject your LLM API credentials into the sbx global secret store (host-side).
|
||||
# Coyote reads your host config, finds any {{SECRET_NAME}} placeholder in your
|
||||
# client's api_key, decrypts it from your host vault, and registers it:
|
||||
sbx secret set -g <llm-provider> # e.g. anthropic, openai, google
|
||||
sbx secret set <llm-provider> # e.g. anthropic, openai, gemini
|
||||
# (skipped silently if already registered; see "Updating a registered secret")
|
||||
|
||||
# 5. Inject MCP secrets into the sbx global secret store (host-side, skipped if --fresh).
|
||||
# 5. Inject MCP secrets and generate the coyote-mcp mixin (host-side, skipped if --fresh).
|
||||
# Coyote scans your mcp.json for {{SECRET_NAME}} placeholders anywhere in each server
|
||||
# config (env, headers, or any nested field), decrypts on the host, and registers each:
|
||||
sbx secret set -g <mcp-service>
|
||||
# config (env, headers, args, URLs, every occurrence of every secret; any number of
|
||||
# secrets per server), decrypts each distinct secret on the host, and registers it
|
||||
# under its kebab-cased secret name:
|
||||
sbx secret set <secret-name> # e.g. {{GITHUB_PAT}} registers as github-pat
|
||||
# (each secret skipped silently if already registered)
|
||||
# Coyote then renders a "coyote-mcp" mixin kit that declares each credential
|
||||
# and allows egress to every remote MCP server. See "The generated coyote-mcp mixin".
|
||||
|
||||
# 6. Check if a sandbox with this name already exists
|
||||
sbx ls
|
||||
|
||||
# 7. If not, create it with the base kit + every discovered mixin layered on
|
||||
# 7. If not, create it with the base kit + every discovered mixin + the generated
|
||||
# coyote-mcp mixin layered on
|
||||
sbx create \
|
||||
--name <NAME> \
|
||||
--kit <cache>/sbx-kit/ \
|
||||
--kit <user-mixin-1> \
|
||||
--kit <user-mixin-N> \
|
||||
--kit <cache>/sbx-mixin-kits/<hash>/ \
|
||||
coyote .
|
||||
|
||||
# 8. Copy your host config into the sandbox (skipped if --fresh). vault.yml is
|
||||
@@ -145,10 +151,16 @@ Run `sbx --help` for the full surface. None of these are reimplemented in Coyote
|
||||
|
||||
## Credentials & Secrets
|
||||
|
||||
The vault is **not used inside a sandbox**. Instead, Coyote injects your LLM API credentials and MCP secrets directly
|
||||
into the sbx global secret store **on your host** before the sandbox starts. Inside the sandbox, the sbx network proxy
|
||||
intercepts outbound HTTP requests and supplies the registered credentials automatically. Your API provider receives the
|
||||
correct key without Coyote ever decrypting anything inside the VM.
|
||||
The vault is **not used inside a sandbox**. Instead, Coyote registers your LLM API credentials and MCP secrets with
|
||||
the sbx secret store **on your host** before the sandbox starts. Inside the sandbox, each credential is supplied one
|
||||
of two ways:
|
||||
|
||||
- **Proxy-injected.** The sbx network proxy intercepts outbound HTTPS requests and writes the real credential into
|
||||
the request header at the network edge. The environment inside the VM only ever holds the literal string
|
||||
`proxy-managed`. Used for LLM providers, and for any MCP secret whose every occurrence is a request-header value.
|
||||
- **Environment-injected.** Secrets that can't be expressed as a proxy header rule are exposed inside the sandbox as
|
||||
`COYOTE_SECRET_<NAME>` environment variables, and Coyote resolves their `{{SECRET_NAME}}` placeholders from those
|
||||
at startup. Either way, your `mcp.json` works unmodified inside the sandbox.
|
||||
|
||||
### How injection works
|
||||
|
||||
@@ -156,8 +168,11 @@ Before `sbx create`, Coyote:
|
||||
|
||||
1. Reads your host `config.yaml` and locates any `{{SECRET_NAME}}` placeholder in your configured client's `api_key` field.
|
||||
2. Decrypts that secret from your host vault.
|
||||
3. Registers it globally with `sbx secret set -g <service>` (e.g. `anthropic`, `openai`, `google`).
|
||||
4. Unless `--fresh` is passed, repeats the same process for every `{{SECRET_NAME}}` placeholder found anywhere in your `mcp.json` server configurations (including `env`, `headers`, and any other nested fields).
|
||||
3. Registers it with `sbx secret set <service>` (e.g. `anthropic`, `openai`, `gemini`).
|
||||
4. Unless `--fresh` is passed, repeats the same process for **every** `{{SECRET_NAME}}` placeholder found anywhere in
|
||||
your `mcp.json` server configurations; i.e. `env`, `headers`, `args`, URLs, any nested field. There is no limit on the
|
||||
number of secrets per server, and one secret may be shared by several servers. Each **distinct secret** is
|
||||
registered once, under its kebab-cased secret name: `{{GITHUB_PAT}}` registers as `github-pat`.
|
||||
|
||||
Secrets already registered with `sbx` are silently skipped to keep re-attaches fast. See
|
||||
[Updating a registered secret](#updating-a-registered-secret) if you've rotated a credential.
|
||||
@@ -165,6 +180,41 @@ Secrets already registered with `sbx` are silently skipped to keep re-attaches f
|
||||
If your LLM client uses **OAuth** instead of an API key (`auth: oauth` in your config), no secret is injected. The
|
||||
sbx proxy handles OAuth natively without a stored key.
|
||||
|
||||
### The generated `coyote-mcp` mixin
|
||||
|
||||
Alongside secret registration, every non-`--fresh` launch renders a mixin kit named `coyote-mcp` and
|
||||
passes it to `sbx create` as an extra `--kit`. It carries two things:
|
||||
|
||||
- **Network egress for your MCP servers.** Every remote server `url` in `mcp.json` contributes an entry to the
|
||||
sandbox's network allow list (HTTPS on the default port as a bare host, anything else as `host:port`). You don't
|
||||
need to hand-maintain a mixin just to reach the MCP servers you've already configured.
|
||||
- **One credential declaration per distinct secret.** Each entry names the sbx service (`github-pat`), the
|
||||
in-sandbox env var (`COYOTE_SECRET_GITHUB_PAT`), and, when the secret qualifies, the proxy inject rules that
|
||||
let sbx write the real value into request headers at the network edge (`proxyManaged: true`).
|
||||
|
||||
When sbx asks you to approve credential bindings on the first interactive run, each entry carries a description
|
||||
naming the vault secret and the MCP server(s) that use it, so you can tell exactly what you're approving.
|
||||
|
||||
A secret is proxy-managed only when **every** occurrence of it across all servers is a single-placeholder HTTPS
|
||||
header value. It falls back to environment injection (`COYOTE_SECRET_<NAME>` holds the real value) when any of
|
||||
these apply:
|
||||
|
||||
- It appears anywhere other than a request header: `env`, `args`, a URL, or any nested field.
|
||||
- Its header value holds more than one placeholder (e.g. `Basic {{USER}}:{{PASS}}`) or a literal `%`.
|
||||
- The server URL isn't HTTPS. (Non-default ports are fine: the inject rule targets `host:port`, mirroring the allow-list format.)
|
||||
- **Two different secrets target the same header on the same domain**; e.g. two `mcp.json` entries pointing at
|
||||
the same host with different `Authorization` bearer tokens. The proxy injects per domain + header and cannot
|
||||
tell which credential a given request needs, so Coyote demotes *all* the conflicting secrets to environment
|
||||
injection and warns on stderr at launch. Different secrets in different headers on one domain are fine and stay
|
||||
proxy-managed.
|
||||
|
||||
Demoted or not, every server's domain stays in the generated allow list, so connectivity is never affected, and only
|
||||
*where* the secret value lives changes.
|
||||
|
||||
> **Not covered:** servers whose URL is buried in `args` rather than the `url` field (e.g. `npx mcp-remote
|
||||
> https://…`) contribute no allow entry, because Coyote only derives egress from `url`. Add those domains to a
|
||||
> [user mixin](#extending-the-sandbox-auto-discovered-mixins).
|
||||
|
||||
### First-run wizard inside the sandbox
|
||||
|
||||
If the sandbox has no Coyote config (e.g. after `--fresh` or on a brand-new sandbox), Coyote runs a lightweight
|
||||
@@ -185,8 +235,11 @@ rotated a credential, update it manually on your host:
|
||||
# See what's currently registered
|
||||
sbx secret ls
|
||||
|
||||
# Overwrite a specific secret (supply the new value via stdin)
|
||||
echo "sk-new-value" | sbx secret set -g --force anthropic
|
||||
# Overwrite an LLM provider credential (supply the new value via stdin)
|
||||
echo "sk-new-value" | sbx secret set --force anthropic
|
||||
|
||||
# Overwrite an MCP secret, keyed by its kebab-cased secret name
|
||||
echo "ghp-new-value" | sbx secret set --force github-pat
|
||||
```
|
||||
|
||||
The updated credential takes effect immediately; you do not need to restart the sandbox.
|
||||
@@ -225,17 +278,18 @@ sandbox:
|
||||
|
||||
```yaml
|
||||
# <config-dir>/sbx-mixin.yaml
|
||||
schemaVersion: "1"
|
||||
schemaVersion: "2"
|
||||
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"
|
||||
permissions:
|
||||
network:
|
||||
allow:
|
||||
- "files.pythonhosted.org"
|
||||
- "pypi.org"
|
||||
|
||||
commands:
|
||||
setup:
|
||||
install:
|
||||
- command: "uv tool install ruff"
|
||||
user: "1000"
|
||||
@@ -245,6 +299,45 @@ commands:
|
||||
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.
|
||||
|
||||
### What a mixin can declare
|
||||
|
||||
A mixin can carry everything a full kit can **except** `sandbox` (base image/entrypoint), `extends`, and
|
||||
`mixins`, as those belong to the base kit. The full authoring surface:
|
||||
|
||||
| Section | Purpose |
|
||||
|---------|---------|
|
||||
| `permissions.network.allow` | Extra egress domains. Bare host = HTTPS on 443; use `host:port` for anything else. `*.host` matches **exactly one** subdomain label. It covers `api.host` but not `a.b.host`, and not the bare `host` itself. There is no allow-all `'*'`. |
|
||||
| `setup.install` | One-time install commands, run once when the sandbox is created. `command` is a shell **string** (run via `sh -c`); set `user: "0"` for root or `"1000"` for the agent user. |
|
||||
| `setup.startup` | Commands run on **every** sandbox start. `command` is an **argv list** (e.g. `['sh', '-c', '...']`); add `background: true` for daemons. |
|
||||
| `setup.files` | Files written into the sandbox: `path`, `content`, `mode`. Only `${WORKDIR}` is expanded inside `content`. Other `${...}` sequences are rejected (unbraced `$VAR` in scripts is fine). |
|
||||
| `environment.variables` | Static environment variables. |
|
||||
| `credentials` | Secret declarations: each entry names an sbx service and an env var, and may add `apiKey.inject` rules so the sandbox proxy writes the real value into request headers at the network edge (`proxyManaged: true`). Bind values with `sbx secret set <service>`. Every inject `domain` **must** also appear in your `permissions.network.allow`. |
|
||||
| `agentInstructions.content` | Instructions for the agent running inside the sandbox, written to the sandbox's `kits-memory/<mixin-name>.md` and progressively disclosed. (A mixin's `filename` field is ignored, and only the base kit controls the primary instructions file.) |
|
||||
|
||||
Composition across the base kit and all mixins is additive: allow lists concatenate, `setup` entries append in kit
|
||||
order, and environment variables merge with later kits overriding earlier ones on key collisions. Kit names must be
|
||||
unique across everything passed to one sandbox.
|
||||
|
||||
The `credentials` section deserves a call-out: it means a custom tool's mixin can ship real proxy-managed auth, not
|
||||
just open domains. For example, a tool hitting an internal API could declare:
|
||||
|
||||
```yaml
|
||||
credentials:
|
||||
- service: internal-api
|
||||
description: Internal API token for the my-report tool
|
||||
apiKey:
|
||||
name: INTERNAL_API_TOKEN
|
||||
proxyManaged: true
|
||||
inject:
|
||||
- domain: internal-api.example.com
|
||||
scheme: bearer
|
||||
```
|
||||
|
||||
Inside the sandbox, `INTERNAL_API_TOKEN` holds the literal string `proxy-managed`; the real value (bound once via
|
||||
`sbx secret set internal-api`) never enters the VM. Requests to `internal-api.example.com` get the
|
||||
`Authorization: Bearer <token>` header added at the proxy. Use `header:` + `format: "%s"` instead of
|
||||
`scheme: bearer` for non-standard headers (exactly one `%s` required, and `format`/`scheme` are mutually exclusive).
|
||||
|
||||
### 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:
|
||||
@@ -252,6 +345,10 @@ Coyote already ships mixins for the most common needs. They get auto-applied whe
|
||||
- **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.
|
||||
- **`coyote-mcp` mixin** (auto-generated on every non-`--fresh` launch; not a file on disk). Declares your MCP
|
||||
credentials and allowlists every remote MCP server in your `mcp.json`. See
|
||||
[The generated coyote-mcp mixin](#the-generated-coyote-mcp-mixin). It does not appear in the verbose mixin log,
|
||||
which only lists discovered mixin *files*.
|
||||
|
||||
You can list everything that's about to be applied via the [verbose mixin log](#verbose-mixin-log) on every launch.
|
||||
|
||||
@@ -284,13 +381,13 @@ If something looks wrong, these are your three debugging flags:
|
||||
|
||||
| Flag | Effect | Use when… |
|
||||
|----------------------|------------------------------------------------------------------------------------------|---------------------------------------------------------------------------|
|
||||
| `--fresh` | Skips host config copy and MCP secret injection; LLM credentials still injected via proxy | You want a clean-slate sandbox with no copied state |
|
||||
| `--fresh` | Skips host config copy, MCP secret injection, and the generated `coyote-mcp` mixin (so MCP servers get no auto egress); LLM credentials still injected via proxy | You want a clean-slate sandbox with no copied state |
|
||||
| `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.** 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. Temporarily move the file aside to confirm.
|
||||
it failed. Open that mixin file and inspect its `setup.install` (v1: `commands.install`) block. Temporarily move the file aside to confirm.
|
||||
- **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.
|
||||
- **An API request from inside the sandbox fails with an auth error.** The secret may not have been registered or may
|
||||
@@ -298,8 +395,10 @@ If something looks wrong, these are your three debugging flags:
|
||||
[Updating a registered secret](#updating-a-registered-secret).
|
||||
- **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.
|
||||
- **A network request from inside the sandbox hangs or fails.** The domain isn't in any allow list
|
||||
(`permissions.network.allow`; v1: `network.allowedDomains`). Remote MCP servers from `mcp.json` are auto-allowed
|
||||
via the generated [`coyote-mcp` mixin](#the-generated-coyote-mcp-mixin); anything else needs a user mixin. The
|
||||
sandbox's proxy denies all unlisted traffic silently.
|
||||
|
||||
## When not to use sandbox mode
|
||||
|
||||
|
||||
@@ -358,7 +358,7 @@ If you use Coyote's [Sandbox mode](Sandboxes), installing a shared bundle that c
|
||||
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`.
|
||||
- **Network access** to every domain listed in the mixin's `permissions.network.allow` (v1: `network.allowedDomains`).
|
||||
- **Install commands** run with passwordless sudo inside the sandbox (the agent user has full sudo).
|
||||
- **Environment variables** set in `environment.variables`.
|
||||
|
||||
@@ -373,8 +373,8 @@ unknown sources.
|
||||
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
|
||||
- `permissions.network.allow` (v1: `network.allowedDomains`): What domains are being added to the sandbox's allowlist?
|
||||
- `setup.install` (v1: `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.)?
|
||||
@@ -396,4 +396,4 @@ off, abort with Ctrl-C (only safe before `sbx create` starts running install com
|
||||
|
||||
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.
|
||||
instead of letting them discover via grep.
|
||||
+6
-4
@@ -125,10 +125,12 @@ If you set up Coyote with one provider and later want to switch, just edit your
|
||||
|
||||
# Vault in Sandboxes
|
||||
|
||||
The vault is **not active** inside a [Docker Sandbox](Sandboxes). Coyote injects your LLM API credentials and MCP
|
||||
secrets directly into the sbx global secret store **on your host** before the sandbox starts, using
|
||||
`sbx secret set -g <service>`. Inside the sandbox, the sbx network proxy intercepts outbound HTTP requests and
|
||||
supplies the registered credentials automatically; no vault password file is copied, and no vault provider CLI is
|
||||
The vault is **not active** inside a [Docker Sandbox](Sandboxes). Coyote registers your LLM API credentials and MCP
|
||||
secrets with the sbx secret store **on your host** before the sandbox starts. LLM providers under their service
|
||||
name (`sbx secret set anthropic`) and each distinct MCP secret under its kebab-cased secret name
|
||||
(`{{GITHUB_PAT}}` → `sbx secret set github-pat`; any number of secrets per server). Inside the sandbox,
|
||||
header-only secrets are injected into requests by the sbx network proxy, and everything else is exposed as
|
||||
`COYOTE_SECRET_<NAME>` environment variables; no vault password file is copied, and no vault provider CLI is
|
||||
installed or configured inside the VM.
|
||||
|
||||
This means:
|
||||
|
||||
Reference in New Issue
Block a user