diff --git a/Sandboxes.md b/Sandboxes.md index 531e610..b1fff8d 100644 --- a/Sandboxes.md +++ b/Sandboxes.md @@ -5,7 +5,8 @@ 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. +config in, inject your LLM and MCP credentials into the sbx secret store, attach you to a running session) and then get +out of the way. ## Quick Start @@ -18,14 +19,8 @@ coyote --sandbox # Or with an explicit name coyote --sandbox my-project -# Create without copying your host config (clean-slate sandbox) +# Start fresh — no copied config or MCP secrets; LLM credentials still injected via proxy 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 pulls the pre-built Coyote sandbox image from Docker Hub (`darkalex17/coyote`) and creates the sandbox. @@ -34,7 +29,7 @@ Attaches to an existing sandbox are instant: your config, vault state, sessions, 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 sandbox creation). +creating a fresh one. `--fresh` is ignored on re-attach (it only affects sandbox creation). ### Multiple sandboxes per workspace @@ -69,41 +64,44 @@ 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// # 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 +# 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 # e.g. anthropic, openai, google +# (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). +# 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 +# (each secret skipped silently if already registered) + +# 6. 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 +# 7. If not, create it with the base kit + every discovered mixin layered on sbx create \ --name \ --kit /sbx-kit/ \ - --kit \ --kit \ --kit \ coyote . -# 6. Copy your host config into the sandbox (skipped if --fresh). Each top-level -# entry is copied individually to sidestep a macOS `docker cp` quirk that -# silently drops files carrying `com.apple.provenance` xattrs when they're -# tarred as part of a recursive directory copy. +# 8. Copy your host config into the sandbox (skipped if --fresh). vault.yml is +# excluded. The vault is not used inside the sandbox. Each top-level entry is +# copied individually to sidestep a macOS `docker cp` quirk that silently drops +# files carrying `com.apple.provenance` xattrs when tarred as a recursive copy. sbx exec sh -c "sudo mkdir -p /home/agent/.config/coyote && sudo chown agent:agent /home/agent/.config/coyote" for entry in ~/.config/coyote/*; do sbx cp "$entry" :/home/agent/.config/coyote/ done sbx exec sh -c "sudo chown -R agent:agent /home/agent/.config/coyote" -# 7. Copy your vault password file, if a local provider is configured (skipped if --fresh) -sbx exec sh -c "sudo mkdir -p && sudo chown agent:agent " -sbx cp : -sbx exec sh -c "sudo chown -R agent:agent " - -# 8. Hand control to sbx (Coyote's process is replaced). `--kit` is re-passed +# 9. Hand control to sbx (Coyote's process is replaced). `--kit` is re-passed # on reattach because sbx expects it even when the sandbox already exists. exec sbx run --name --kit /sbx-kit/ ``` @@ -111,21 +109,19 @@ exec sbx run --name --kit /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. +> Coyote handles steps 1–5, 8, and the `--name` argument of step 7. 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): - (adds: 1 install, 6 domains) +Applying 2 sbx mixin(s): ~/.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. +If zero mixins were discovered, you'll see `No sbx mixins discovered.` in the log (no terminal noise). 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. @@ -147,71 +143,59 @@ Coyote intentionally does not wrap sandbox lifecycle commands. Use `sbx` directl Run `sbx --help` for the full surface. None of these are reimplemented in Coyote. -## Vault Behavior +## Credentials & Secrets -Coyote's sandbox bootstrap is aware of your [vault](Vault) configuration and behaves differently depending on which provider you've chosen: +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. -### Local provider (default) +### How injection works -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. +Before `sbx create`, Coyote: -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//...`, `/Users//...`, or `C:\Users\\...`) 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. +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 ` (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). -#### Host OS support for the password file copy +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. -| Host OS | Password file under `$HOME`/`%USERPROFILE%` | Password file outside `$HOME`/`%USERPROFILE%` | -|---------|--------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Linux | Copied to `/home/agent/` | Copied verbatim (e.g. `/etc/coyote/.password` -> `/etc/coyote/.password`) | -| macOS | Copied to `/home/agent/` | Copied verbatim | -| Windows | Copied to `/home/agent/` (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\\...`). | +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. -### Non-Local provider (auto-installed CLI mixin) +### First-run wizard inside the sandbox -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: +If the sandbox has no Coyote config (e.g. after `--fresh` or on a brand-new sandbox), Coyote runs a lightweight +first-run wizard when it starts inside the sandbox: -| Provider | CLI installed | Auto-applied mixin | -|-----------------------|---------------|-----------------------------------------| -| `one_password` | `op` | `` | -| `azure_key_vault` | `az` | `` | -| `gopass` | `gopass` | `` | -| `aws_secrets_manager` | `aws` (v2) | `` | -| `gcp_secret_manager` | `gcloud` | `` | +1. Pick your API provider. +2. For providers that support OAuth (Claude, OpenAI, Gemini, and any openai-compatible provider with bundled OAuth), optionally choose OAuth instead of an API key. +3. Pick a model. -The mixin installs the CLI but **does not log you in** — see the next section. +The resulting `config.yaml` contains no secrets. Credentials are still managed entirely by the sbx proxy. -### Re-Authenticating Your Vault in a Sandbox +### Updating a registered secret -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 `: - -| Provider | First-time sandbox auth | -|-----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `local` | Nothing (password file auto-copied) | -| `one_password` |
  • **Command Passthrough:** `> !op signin`
  • **Docker Sbx CLI:** `sbx exec op signin`
| -| `azure_key_vault` |
  • **Command Passthrough:** `> !az login`
  • **Docker Sbx CLI:** `sbx exec az login`
| -| `gopass` |
  • **Command Passthrough:** `> !gopass clone ` (or `!gopass setup`)
  • **Docker Sbx CLI:** `sbx exec gopass clone ` (or `gopass setup`)
| -| `aws_secrets_manager` |
  • **Command Passthrough:** `> !aws configure sso` (or `!aws sso login --profile `)
  • **Docker Sbx CLI:** `sbx exec aws configure sso` (or `aws sso login --profile `)
| -| `gcp_secret_manager` |
  • **Command Passthrough:** `> !gcloud auth application-default login`
  • **Docker Sbx CLI:** `sbx exec gcloud auth application-default login`
| - -These commands authenticate **once per sandbox**, meaning credentials persist on the sandbox filesystem until you -`sbx rm `. - -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: +Coyote skips secrets that are already registered to avoid unnecessary prompts on every sandbox launch. If you've +rotated a credential, update it manually on your host: ```bash -sbx cp ~/.aws :/home/agent/.aws -sbx cp ~/.config/gcloud :/home/agent/.config/gcloud +# 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 ``` -This is faster but bleeds host state into the sandbox. It's your call. Coyote intentionally doesn't do this automatically. +The updated credential takes effect immediately; you do not need to restart the sandbox. + +### Vault operations inside the sandbox + +Vault management commands (`coyote --add-secret`, `--update-secret`, `--delete-secret`, `--list-secrets`, and `.vault` +REPL commands) are **disabled inside the sandbox** and will return an error directing you to your host. Manage your +vault from your host machine. ## Extending the Sandbox: Auto-Discovered Mixins @@ -268,8 +252,6 @@ 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. -- **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. @@ -300,21 +282,20 @@ every Coyote sandbox, so running `coyote --sandbox` from within a sandbox is a n 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 | +| 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 | +| `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 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. - **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). +- **An API request from inside the sandbox fails with an auth error.** The secret may not have been registered or may + be stale. Run `sbx secret ls` on your host to check, then see + [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 @@ -333,6 +314,6 @@ If something looks wrong, these are your three debugging flags: ## See Also - [Docker Sandboxes: Official Docs](https://docs.docker.com/ai/sandboxes/) -- [Vault](Vault): How Coyote resolves the password file that gets copied in +- [Vault](Vault): How Coyote's vault works; vault management is disabled inside sandboxes - [Environment Variables](Environment-Variables): Including `COYOTE_SANDBOX_KIT` - [Clients](Clients#authentication): Auth flows you may need to redo inside the sandbox for OAuth providers diff --git a/Vault.md b/Vault.md index 6c7bdef..9dec3d8 100644 --- a/Vault.md +++ b/Vault.md @@ -125,86 +125,25 @@ If you set up Coyote with one provider and later want to switch, just edit your # 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. +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 `. 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 +installed or configured inside the VM. -## Local provider +This means: -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. +- **No vault password file is copied into the sandbox.** +- **No vault provider CLI (op, az, aws, gcloud, gopass) is installed or configured in the sandbox.** +- **Vault management commands are disabled inside the sandbox.** `coyote --add-secret`, `--update-secret`, + `--delete-secret`, `--list-secrets`, and `.vault` REPL commands all return an error. Manage your vault from your + host machine. +- **Secrets added to your vault after a sandbox was created are not automatically propagated.** See + [Updating a registered secret](Sandboxes#updating-a-registered-secret) for how to push a new value without + recreating the sandbox. -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\\...`). 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 op signin` | -| `azure_key_vault` | `sbx exec az login` | -| `gopass` | `sbx exec gopass clone ` (or `gopass setup`) | -| `aws_secrets_manager` | `sbx exec aws configure sso` (or `aws sso login --profile `) | -| `gcp_secret_manager` | `sbx exec gcloud auth application-default login` | - -Replace `` with whatever you passed to `coyote --sandbox ` (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 [`!` shell pass-through](REPL#command---run-an-arbitrary-shell-command) without opening a second terminal: - -``` -> !op signin -> !az login -> !gopass clone -> !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 ` 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 `. Reopening the sandbox later does not re-prompt. -- **`sbx rm ` wipes all cached credentials.** The next `coyote --sandbox ` 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 :/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. +For the full injection sequence and details on the sandbox first-run wizard, see +[Sandboxes: Credentials & Secrets](Sandboxes#credentials--secrets). # Motivation Coyote is intended to be highly configurable and adaptable to many different use cases. This means that users of Coyote