111 lines
3.8 KiB
Markdown
111 lines
3.8 KiB
Markdown
# Docker Image
|
|
|
|
## Base image contents
|
|
|
|
The sandcage image is built on `debian:bookworm-slim` and includes:
|
|
|
|
- **Shell:** zsh (default), bash
|
|
- **VCS:** git, openssh-client
|
|
- **Search:** ripgrep, fd-find
|
|
- **Utilities:** jq, curl, sudo, ca-certificates, npm
|
|
- **Task runner:** just (installed from upstream binary)
|
|
- **Python tooling:** uv (installed per-user at `/home/agent/.local/bin`)
|
|
|
|
The image creates an `agent` user (UID 1000) with a home at `/home/agent`, zsh as the default shell, and passwordless sudo.
|
|
|
|
Pre-created directories: `/workspace`, `/home/agent/.claude`, `/home/agent/.codex`, `/home/agent/.gemini`.
|
|
|
|
## How agents are installed
|
|
|
|
AI coding agents (Claude Code, Codex, Gemini CLI) are **not** baked into the image. They are installed on first run via their respective entrypoint scripts. The agent home directories (`~/.claude`, `~/.codex`, `~/.gemini`) are persisted across runs through volume mounts to `~/.sandcage/` on the host, so installation only happens once.
|
|
|
|
Each service has its own entrypoint script installed at `/usr/local/bin/sandcage-<service>-entrypoint`.
|
|
|
|
## Building images
|
|
|
|
Build the sandcage image with:
|
|
|
|
```
|
|
sandcage build
|
|
```
|
|
|
|
### Force rebuild
|
|
|
|
To bypass the cache and rebuild unconditionally:
|
|
|
|
```
|
|
sandcage build --force
|
|
```
|
|
|
|
When `--force` is used, Docker's `--no-cache` flag is also passed to ensure layers are rebuilt from scratch.
|
|
|
|
### Service filter
|
|
|
|
Build only images required for specific services:
|
|
|
|
```
|
|
sandcage build claude shell
|
|
```
|
|
|
|
Unknown service names produce an error listing available services.
|
|
|
|
## Cache awareness
|
|
|
|
Sandcage uses hash-based rebuild detection to avoid unnecessary image builds.
|
|
|
|
On each build, the SHA-256 hash of the Dockerfile content (bundled or custom) is computed and compared against the stored hash in `~/.sandcage/.build-hashes`. If they match, the build is skipped with an "up to date" message.
|
|
|
|
After a successful build, the new hash is persisted. The hash file uses a simple `image:hash` line format.
|
|
|
|
This means:
|
|
|
|
- Editing the Dockerfile (or switching to/from a custom one) triggers a rebuild automatically.
|
|
- The `--force` flag bypasses hash comparison entirely.
|
|
|
|
## Custom Dockerfiles
|
|
|
|
You can override the bundled Dockerfile by specifying a path in configuration.
|
|
|
|
### Global config (`~/.sandcage/config.toml`)
|
|
|
|
```toml
|
|
[dockerfiles]
|
|
sandcage = "/path/to/my/Dockerfile"
|
|
```
|
|
|
|
### Project config (`.sandcage.yml`)
|
|
|
|
```yaml
|
|
dockerfiles:
|
|
sandcage: ./docker/Dockerfile.custom
|
|
```
|
|
|
|
The path can point to either:
|
|
|
|
- A **file** -- used as the Dockerfile with a temporary build context.
|
|
- A **directory** -- used as the full build context (must contain a `Dockerfile`).
|
|
|
|
### trusted_projects requirement
|
|
|
|
Project-level Dockerfile overrides are only applied if the project directory is listed in the global `trusted_projects` setting. This prevents untrusted repositories from injecting arbitrary build instructions.
|
|
|
|
```toml
|
|
# ~/.sandcage/config.toml
|
|
trusted_projects = [
|
|
"/home/user/projects/my-trusted-repo",
|
|
"/home/user/work",
|
|
]
|
|
```
|
|
|
|
A project is considered trusted if its canonical path starts with any entry in `trusted_projects` (prefix matching). If a project specifies `dockerfiles` but is not trusted, the override is silently ignored with a warning.
|
|
|
|
Global config overrides (`~/.sandcage/config.toml`) are always trusted.
|
|
|
|
## Cross-platform notes
|
|
|
|
### UID/GID handling
|
|
|
|
On **Linux**, sandcage queries the host user's UID and GID via `id -u` / `id -g` and passes them into the container as `SANDCAGE_UID` and `SANDCAGE_GID`. This ensures files created inside the container have correct ownership on bind-mounted host directories.
|
|
|
|
On **Windows**, UID/GID passthrough is not meaningful. Sandcage hardcodes both to `1000`, matching the `agent` user built into the image. File ownership on Windows bind mounts is handled by Docker Desktop's filesystem sharing layer.
|