Skip to content

feat: dockerCompose() sandbox provider (#471)#580

Open
OdinIversen wants to merge 1 commit into
mattpocock:mainfrom
OdinIversen:feat/docker-compose-provider
Open

feat: dockerCompose() sandbox provider (#471)#580
OdinIversen wants to merge 1 commit into
mattpocock:mainfrom
OdinIversen:feat/docker-compose-provider

Conversation

@OdinIversen
Copy link
Copy Markdown

Closes #471.

Adds a dockerCompose() sandbox provider that delegates container
configuration to a user-managed docker-compose.yml. Sandcastle invokes
docker compose run -d against a service (default agent) and injects
only the per-run worktree bind mount, workdir, and env vars. Image,
networks, GPU reservations, resource limits, and dependent services live
in the compose file — meaning every Docker option is reachable today
without sandcastle adding a flag for each.

import { dockerCompose } from "@ai-hero/sandcastle/sandboxes/docker-compose";

await run({
  agent: claudeCode("claude-opus-4-6"),
  sandbox: dockerCompose({ composeFile: ".sandcastle/docker-compose.yml" }),
  prompt: "...",
});

Notes for review

  • --deps not passed. The issue body suggested docker compose run --deps …; in compose v2 dependent services run by default and only --no-deps opts out, so the flag is omitted.
  • serviceName shipped. The issue listed an explicit service-name option as a "natural follow-up". Including it now keeps the API symmetric with docker() and avoids a follow-up release; happy to drop if you'd rather defer.
  • projectName shipped as the lower-level primitive. The issue's isolateDeps: true follow-up can be built on top of this. Per-session isolation itself is left for that follow-up.
  • No UID pre-flight. Compose users own UID via build args, so the docker provider's pre-flight check is intentionally skipped.
  • Compose file path verified at construction. Misconfiguration fails fast, before a worktree is created.

Tests

  • src/DockerComposeLifecycle.test.ts (13 tests) covers the docker-compose CLI shape: argv layout, -v mounts (incl. Windows drive-letter colons), :ro suffix, -f/-p/--project-directory/--workdir/-e flags, no --user, pre-existing-container guard, best-effort cleanup.
  • src/sandboxes/docker-compose.test.ts (18 tests) covers dockerCompose(): provider tag/name, mount validation, compose-file existence check, worktree volume, default workdir, serviceName override, projectName/projectDirectory/composeFile propagation, copyFileIn/copyFileOut.
  • npm run typecheck and npm run build both green.
  • End-to-end smoke run against a real compose file with NVIDIA GPU reservation: docker compose run -d started a real container, nvidia-smi -L from handle.exec() listed the host GPU inside the container, workdir and close() cleanup behaved as expected.

Out of scope (per issue)

  • sandcastle init scaffolding for a starter compose file
  • dockerCompose({ isolateDeps: true }) per-session project isolation

Changeset attached.

Adds a `dockerCompose()` provider that delegates container configuration
to a user-managed `docker-compose.yml`. Sandcastle invokes
`docker compose run -d` against a service (default `agent`) and injects
only the per-run worktree bind mount, workdir, and env vars — image,
networks, GPU reservations, resource limits, and dependent services live
in the compose file.

Closes the long-running ask in mattpocock#471: every Docker option (networks,
GPUs, ulimits, depends_on, healthchecks) is now reachable via the compose
file, without sandcastle having to add a flag for each.

Notes for review:
- `--deps` (mentioned in the issue body) is not passed: in compose v2
  dependent services run by default, only `--no-deps` opts out.
- `serviceName` option is included so users can target a non-default
  service; the issue listed this as a "natural follow-up", but shipping
  it now keeps the API symmetric with the existing `docker()` provider
  and avoids a follow-up release.
- `projectName` is exposed as the lower-level primitive that the issue's
  follow-up `isolateDeps: true` would build on; per-session isolation
  itself is left as a future story.
- UID pre-flight is intentionally skipped — compose users own UID via
  build args.
- Compose file path is verified at construction time so misconfiguration
  fails fast, before a worktree is created.

Validated end-to-end: real container started against a compose file with
`devices: [{ driver: nvidia, count: all, capabilities: [gpu] }]`,
`nvidia-smi -L` listed the host GPU inside the container, workdir and
cleanup behaved as expected.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 6, 2026

Someone is attempting to deploy a commit to the Matt Pocock's projects Team on Vercel.

A member of the Team first needs to authorize it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add docker-compose sandbox provider

1 participant