🛰️ export from upstream (aecf23a)

This commit is contained in:
2026-05-24 19:45:28 +02:00
parent 2fadce5490
commit 0cd116aff2
2 changed files with 375 additions and 0 deletions
Generated
+1
View File
@@ -798,6 +798,7 @@ dependencies = [
"assert_cmd", "assert_cmd",
"predicates", "predicates",
"sandcage", "sandcage",
"serde_yaml",
"tempfile", "tempfile",
] ]
+374
View File
@@ -0,0 +1,374 @@
# 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 (`<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.
```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/<project-directory-name>` (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 <service>` 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",
]
```