# Configuration Reference Sandcage uses a layered configuration system. Each layer overrides the previous one: 1. **Compiled defaults** — all fields unset 2. **Global config** (`~/.sandcage/config.toml`) — user-wide preferences (TOML format) 3. **Project config** (`.sandcage.yml`) — per-project settings, checked into version control (YAML format) 4. **Local config** (`.sandcage.local.yml`) — personal overrides, gitignored (YAML format) Later layers win. Fields not set in a layer are inherited from the layer below. --- ## Fields ### env Environment variables passed into the container at runtime. - **Type:** Map of string keys to string values - **Valid in:** Global, Project, Local - **Behavior:** Passed as `-e KEY=VALUE` flags to `docker compose run` ```yaml # .sandcage.yml env: RUST_LOG: debug DATABASE_URL: "postgres://localhost:5432/dev" ``` ```toml # ~/.sandcage/config.toml [env] EDITOR = "vim" ``` --- ### packages System packages to install in the container image (apt packages). - **Type:** List of strings - **Valid in:** Global, Project, Local ```yaml packages: - ripgrep - fd-find - postgresql-client ``` --- ### toolchains Language toolchains to install, keyed by language name and version. - **Type:** Map of string keys to string values - **Valid in:** Global, Project, Local ```yaml toolchains: rust: "1.78" node: "20" python: "3.12" ``` --- ### mounts Additional volume mounts passed to the container. Each entry must contain a `:` separator. - **Type:** List of strings in Docker mount format (`:[:]`) - **Valid in:** Global, Project, Local - **Behavior:** Tilde (`~`) in the source path is expanded to the host home directory. Entries without `:` are skipped with a warning. ```yaml mounts: - /data/models:/models:ro - ~/.gitconfig:/home/agent/.gitconfig:ro - /tmp/cache:/cache ``` --- ### shell Default shell inside the container. - **Type:** String - **Valid in:** Global, Project, Local ```yaml shell: zsh ``` --- ### justfile Path to a justfile to make available in the container. - **Type:** File path (string) - **Valid in:** Global, Project, Local ```yaml justfile: ./project.justfile ``` --- ### dockerfiles Custom Dockerfile overrides for image builds. Keyed by image short name. Values can be a path to a Dockerfile or a directory containing one. - **Type:** Map of string keys to file paths - **Valid in:** Global, Project (project overrides require the project to be in `trusted_projects`) - **Behavior:** Project-level Dockerfile overrides are ignored unless the project directory is listed in `trusted_projects` in the global config. ```yaml # .sandcage.yml (only applied if project is trusted) dockerfiles: sandcage: ./docker/Dockerfile.custom ``` ```toml # ~/.sandcage/config.toml [dockerfiles] sandcage = "/home/user/my-dockerfiles/sandcage/" ``` --- ### trusted_projects List of project directories allowed to use Dockerfile overrides. - **Type:** List of file paths - **Valid in:** Global only (a project cannot self-trust) - **Behavior:** Read exclusively from `~/.sandcage/config.toml`. Projects with `dockerfiles` set are ignored unless their path (or a parent) appears here. ```toml # ~/.sandcage/config.toml trusted_projects = [ "/home/user/projects/my-trusted-project", "/home/user/work", ] ``` --- ### container_workspace Override the workspace path inside the container. Must be an absolute path (starting with `/`). Relative paths are ignored with a warning. - **Type:** String (absolute path) - **Valid in:** Global, Project, Local - **Default:** `/workspace/` (derived from the host workspace folder name) ```yaml container_workspace: /workspace/my-app ``` --- ### agent_args Default arguments passed to agents on every invocation. Keyed by service name. These are appended after the service name but before any extra CLI arguments. - **Type:** Map of service name to list of strings - **Valid in:** Global, Project, Local - **Behavior:** Only the args for the invoked service are used. When running with `--shell`, agent_args are skipped entirely. ```yaml agent_args: claude: - "--dangerously-skip-permissions" codex: - "--full-auto" ``` ```toml # ~/.sandcage/config.toml [agent_args] claude = ["--dangerously-skip-permissions"] ``` --- ### ssh_mode Controls how SSH keys are made available inside the container. - **Type:** String — one of `"volume"`, `"bind"`, or `"none"` - **Valid in:** Global, Project, Local - **Default:** No SSH mount (equivalent to `"none"`) | Value | Behavior | |----------|----------| | `volume` | Mounts a named Docker volume (`sandcage-ssh`) at `/home/agent/.ssh` (read-only). Populate it with `sandcage setup ssh`. | | `bind` | Bind-mounts `~/.ssh` from the host at `/home/agent/.ssh` (read-only). | | `none` | No SSH directory is mounted. | ```yaml ssh_mode: volume ``` When using `volume` mode, run `sandcage setup ssh --refresh` after changing keys on the host. --- ### ssh_keys Declares which SSH keys to copy into the volume when using `sandcage setup ssh`. Each entry specifies a host and identity file. - **Type:** List of objects with `host` (string) and `identity_file` (string) fields - **Valid in:** Global, Project, Local ```yaml ssh_keys: - host: github.com identity_file: ~/.ssh/id_ed25519 - host: gitea.internal identity_file: ~/.ssh/work_gitea ``` --- ### services Override the enabled state of built-in services. Keyed by service name. - **Type:** Map of service name to object with optional `enabled` (boolean) field - **Valid in:** Global, Project, Local - **Behavior:** Disabled services cannot be started with `sandcage ` and are excluded from generated compose files. ```yaml services: gemini: enabled: false codex: enabled: false ``` ```toml # ~/.sandcage/config.toml [services.codex] enabled = false ``` --- ### default_services Controls which services `sandcage build` prepares by default (when no service names are given on the CLI). - **Type:** List of strings (service names) - **Valid in:** Global, Project, Local ```yaml default_services: - claude - shell ``` --- ## Services Sandcage ships with four built-in services, all enabled by default: | Service | Description | Config directory | |----------|-----------------------|------------------| | `claude` | Claude Code agent | `.claude` | | `codex` | Codex agent | `.codex` | | `gemini` | Gemini CLI agent | `.gemini` | | `shell` | Interactive zsh shell | (none) | Agent services are installed on first run into the persistent home directory (`~/.sandcage/`). They auto-update on subsequent runs. To disable a service you do not use: ```yaml services: codex: enabled: false gemini: enabled: false ``` Disabled services are excluded from Docker compose generation and cannot be invoked. --- ## Examples ### Minimal project config ```yaml # .sandcage.yml toolchains: node: "20" packages: - ripgrep ``` ### Full-featured project config ```yaml # .sandcage.yml env: RUST_LOG: debug NODE_ENV: development packages: - ripgrep - fd-find - postgresql-client toolchains: rust: stable node: "20" mounts: - /data/models:/models:ro shell: zsh agent_args: claude: - "--dangerously-skip-permissions" services: gemini: enabled: false ``` ### Local overrides (gitignored) ```yaml # .sandcage.local.yml ssh_mode: volume ssh_keys: - host: github.com identity_file: ~/.ssh/id_ed25519 env: SECRET_API_KEY: "sk-local-dev-key" mounts: - ~/.aws:/home/agent/.aws:ro ``` ### Global config ```toml # ~/.sandcage/config.toml shell = "zsh" [env] EDITOR = "vim" [agent_args] claude = ["--dangerously-skip-permissions"] [services.codex] enabled = false trusted_projects = [ "/home/user/work/trusted-project", ] ```