Files
sandcage/docs/configuration.md
T
2026-05-24 19:45:28 +02:00

7.7 KiB

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
# .sandcage.yml
env:
  RUST_LOG: debug
  DATABASE_URL: "postgres://localhost:5432/dev"
# ~/.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
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
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 (<source>:<target>[:<options>])
  • 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.
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
shell: zsh

justfile

Path to a justfile to make available in the container.

  • Type: File path (string)
  • Valid in: Global, Project, Local
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.
# .sandcage.yml (only applied if project is trusted)
dockerfiles:
  sandcage: ./docker/Dockerfile.custom
# ~/.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.
# ~/.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/<project-directory-name> (derived from the host workspace folder name)
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.
agent_args:
  claude:
    - "--dangerously-skip-permissions"
  codex:
    - "--full-auto"
# ~/.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.
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
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 <service> and are excluded from generated compose files.
services:
  gemini:
    enabled: false
  codex:
    enabled: false
# ~/.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
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:

services:
  codex:
    enabled: false
  gemini:
    enabled: false

Disabled services are excluded from Docker compose generation and cannot be invoked.


Examples

Minimal project config

# .sandcage.yml
toolchains:
  node: "20"
packages:
  - ripgrep
# .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)

# .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

# ~/.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",
]