Sandboxed AI coding agents on macOS. Built to run Claude Code or Codex in filesystem-isolated Linux containers with a single command.
spawn build # build container images (once)
spawn # run Claude Code in current directory
spawn -- cargo test
spawn doctor # check local runtime readiness, images, config, and workspace detection
spawn doctor -C ~/code/project
spawn doctor --jsonspawn detects your project's language, picks the right container image, mounts your code, and launches the agent. Your files are read/write inside the container — everything else on your system is isolated.
spawn is a work in progress. PRs are welcome.
The goal is similar to Jai on Linux: a jail
for your agents that you'll actually use. On macOS we do not have the same
underlying isolation model, so spawn takes the pragmatic route and builds on
Apple's container and virtualization stack.
It's written in Swift so it can directly use Apple's Containerization and Virtualization frameworks when that becomes the right boundary, instead of being limited to a shell wrapper forever.
spawn wraps Apple's container CLI to launch AI coding agents in lightweight Linux VMs.
- Auto-detects toolchains — Rust, Go, C++, and JS/TS projects (Node, Bun, Deno), or falls back to a base image
- Safe mode by default — prompts before
git push, PR creation, and other remote-write operations - Uses explicit access profiles — default
minimal, with opt-ingitandtrustedhost auth exposure - Persists OAuth credentials across runs — authenticate once, not every session
- No API keys required — Pro/Max plan users authenticate via OAuth
- macOS 26+
- Apple's
containerCLI:brew install container - Swift 6.3+ (for building from source;
make testprefers Xcode when installed)
brew install container
brew install vmunix/tap/spawngit clone https://github.com/vmunix/spawn.git
cd spawn
make install # builds release and installs to ~/.local/binEnsure ~/.local/bin is in your PATH:
export PATH="$HOME/.local/bin:$PATH"# Build all container images (required once)
spawn build
# Or build just what you need
spawn build rust # also: base, cpp, go, js
# Run Claude Code in your project
spawn
# Run Codex instead
spawn codex
# Run an arbitrary command
spawn -- cargo test
# Run in another workspace
spawn -C ~/code/project
# Opt into git identity and gh auth without exposing SSH keys
spawn --access git
# Drop into a shell for debugging
spawn --shell
# Check local runtime readiness and the current workspace
spawn doctor
spawn doctor -C ~/code/project
spawn doctor --jsonspawn build uses an isolated temporary build context, so it does not depend on whatever files happen to be in your current working directory.
spawn [agent] [options]
spawn -- <command...>
spawn doctor [-C <dir>]Use spawn -- <command...> for passthrough commands. spawn cargo test is rejected on purpose so the root CLI stays unambiguous.
| Option | Description |
|---|---|
--yolo |
Skip permission gates (default: safe mode, prompts before git push) |
--shell |
Drop into a shell instead of running an agent |
-C, --cwd <dir> |
Directory to mount as workspace (default: current directory) |
--runtime <name> |
Runtime mode: auto, spawn, workspace-image |
--rebuild-workspace-image |
Force a rebuild when using --runtime workspace-image |
--access <name> |
Host access profile: minimal, git, trusted |
--toolchain <name> |
Override auto-detected toolchain: base, cpp, rust, go, js |
--image <name> |
Override auto-selected container image |
--mount <dir> |
Mount an additional directory (repeatable) |
--read-only <dir> |
Mount a directory read-only (repeatable) |
--cpus <n> |
CPU cores for the container (default: 4) |
--memory <size> |
Container memory (default: 8g) |
--env <KEY=VALUE> |
Set environment variable (repeatable) |
--env-file <path> |
Load environment variables from a file |
--verbose |
Show the container command being run |
Run an arbitrary command in the workspace container by passing it after --:
spawn -- cargo test
spawn -C ~/code/project -- swift testAccess profiles control host auth exposure:
minimalmounts only the workspace, requested extra mounts, and persisted agent stategitadditionally mounts git config andghCLI authtrustedadditionally mounts selected SSH config and standardid_*key material copied from~/.ssh
Runtime mode controls how spawn reacts when a workspace defines its own runtime:
autois the defaultspawnopts into spawn-managed images explicitlyworkspace-imagebuilds and runs the workspace-defined image directly
workspace-image reuses a cached workspace image when the tracked Dockerfile, optional .dockerignore, devcontainer config, and non-ignored build-context file contents and permissions have not changed.
Use --rebuild-workspace-image with --runtime workspace-image when you want to bypass the cache explicitly.
If your repo has a root Dockerfile / Containerfile, or a .devcontainer/devcontainer.json with build.dockerfile, spawn currently requires an explicit choice:
spawn --runtime workspace-image
spawn --runtime workspace-image --rebuild-workspace-image
spawn --runtime spawnspawn build [toolchain] [options]Omit the toolchain to build all images. Base is built first since other images depend on it.
| Option | Description |
|---|---|
--cpus <n> |
CPU cores for the builder container (default: 4) |
--memory <size> |
Builder container memory (default: 8g) |
spawn list # list running containers
spawn stop <id> # stop a container
spawn exec <id> <cmd> # run a command in a running container
spawn shell <id> # open /bin/bash in a running container
spawn doctor # check local runtime readiness, images, config, and workspace detection
spawn doctor -C ~/code/project
spawn doctor --json # same report in machine-readable formspawn image list # list spawn images
spawn image list --all # list all container images
spawn image rm <name> # remove a spawn imageBy default, spawn runs agents in safe mode. Remote-write operations require approval:
git pushgit remote add/set-urlgh pr create/merge/closegh issue create/closegh release,gh repo
Use --yolo to skip all permission gates.
Place a KEY=VALUE file at ~/.config/spawn/env to set environment variables for every run. Lines starting with # are comments. Values can be quoted.
Add a .spawn.toml to your repo root to set workspace defaults:
[workspace]
agent = "codex"
[toolchain]
base = "rust"Valid values:
workspace.agent:claude-code,codextoolchain.base:base,cpp,rust,go,js
Repo config can set the default agent and toolchain preference. Host access still requires an explicit --access ... at launch time, even if .spawn.toml contains an access value.
spawn also reads .devcontainer/devcontainer.json to infer toolchains from images and features. If a viable devcontainer config is present, spawn prefers that explicit signal over repo-file heuristics. This makes existing VS Code devcontainer projects work with zero extra setup.
spawn doctor also checks local runtime readiness: whether the container services are running, whether a default kernel is installed, and whether Rosetta is available on Apple Silicon hosts. When something is missing, it points you at the most common first-machine fixes.
If your project already uses .devcontainer/devcontainer.json, spawn treats that as the strongest project signal after .spawn.toml.
- devcontainer image/features are mapped to spawn toolchains
- the launch summary and
spawn doctorshow when.devcontainer/devcontainer.jsondrove the choice - this makes spawn a good fit for projects already set up for VS Code Dev Containers
If .devcontainer/devcontainer.json uses build.dockerfile, spawn --runtime workspace-image builds and runs that workspace-defined image directly and reuses it until the tracked build inputs change. That cache respects a context-root .dockerignore, so ignored files do not force rebuilds. spawn --runtime spawn remains available when you want to ignore the workspace runtime and use spawn-managed images instead.
For JS/TS repos, spawn-js:latest bundles Node.js 22 LTS, Corepack, Bun, and Deno so the common runtime and package-manager paths work out of the box.
For detailed guides on permissions, authentication, and architecture, see the documentation.
make build # Debug build
make test # Lint + tests (prefers Xcode's SwiftPM when available)
make smoke # End-to-end workspace-first and workspace-image tests
make install # Install to ~/.local/binMIT