Metadata-Version: 2.1
Name: yolo-jail
Version: 0.8.0
Summary: Secure container jail for AI coding agents — run Claude Code, Copilot, opencode, pi, or Codex in YOLO mode safely
Author: Matt Schulkind
License: Apache-2.0
Home-page: https://github.com/mschulkind-oss/yolo-jail
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# YOLO Jail

[![CI](https://github.com/mschulkind-oss/yolo-jail/actions/workflows/ci.yml/badge.svg)](https://github.com/mschulkind-oss/yolo-jail/actions/workflows/ci.yml)
[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)

A secure, isolated container environment for AI coding agents (Claude Code, Copilot, opencode, pi, Codex, Antigravity) to safely modify codebases without compromising host security or identity. Agents are selected with the `packs` config key — see [Agents](#agents). Runs on **Linux and macOS** (Apple Silicon and Intel) with Podman or Apple Container.

## Why?

AI coding agents like Claude Code, GitHub Copilot, and OpenAI Codex have a `--yolo` mode that lets them run shell commands without confirmation. This is powerful but dangerous — agents can access your SSH keys, cloud credentials, git identity, and anything else on your machine.

**YOLO Jail** lets you run agents in YOLO mode safely by isolating them in a container with:
- ❌ No access to `~/.ssh/`, `~/.gitconfig`, or cloud credentials
- ✅ Separate auth (`gh auth login`, `codex login`, etc. inside the jail)
- ✅ Your codebase mounted read-write at `/workspace`
- ✅ Persistent tool state across restarts
- ✅ Pre-configured MCP servers, LSP servers, and modern CLI tools

## Features

- **Isolated:** Runs in a podman or Apple Container container with no access to host credentials
- **Optimized:** Pre-installed with modern, fast tools (`rg`, `fd`, `bat`, `eza`, `jq`, `delta`, `fzf`)
- **Restricted:** Blocked tools return clear errors with suggestions (e.g., `rg` instead of `grep`)
- **Reproducible:** Defined entirely via Nix Flakes
- **Agent-Ready:** MCP presets (Chrome DevTools, Sequential Thinking) and LSP servers (Pyright, TypeScript) — enable by name
- **Configurable:** Per-project config via `yolo-jail.jsonc`, user defaults via `~/.config/yolo-jail/config.jsonc`
- **Container Reuse:** Same workspace reuses the same container via `exec`
- **Runtime Flexible:** Works with podman (Linux/macOS) and Apple Container (macOS native)
- **Cross-Platform:** Full support for Linux and macOS (Apple Silicon and Intel)

## Prerequisites

Core requirements (both platforms):

- **[Nix](https://nixos.org/download/)** (with flakes enabled)
- A container runtime — one of:
  - **[Podman](https://podman.io/)** (preferred on Linux; Podman Machine on macOS)
  - **[Apple Container](https://github.com/apple/container)** (native macOS, `brew install container`)

Additionally, to [install from source](#from-source):

- **[Go](https://go.dev/dl/)** (see `go.mod` for the required version)
- **[just](https://github.com/casey/just)**

Platform specifics (in priority order):

- **Linux / x86_64** — any modern distribution with Podman. No extra setup. The primary target.
- **macOS / Apple Silicon** — via a native **arm64** Linux container (Apple Container or Podman Machine); no emulation. See [docs/guides/macos.md](docs/guides/macos.md).
- **Linux / arm64 (aarch64-linux)** — supported and CI-tested (image built + integration-tested natively on `ubuntu-24.04-arm`); same nix image as x86_64, no arch switch.
- **macOS / Intel** — also supported (x86_64 Linux container).

No builder is needed on macOS — the standard image builds entirely from the NixOS binary cache. If you add a package that isn't cached, the from-source Linux build is offloaded automatically to a tiny throwaway container on whichever container runtime is already up (Podman or Apple Container); no VM, no `sudo`, no setup.

## Install

Four channels, all shipping the same single `yolo` binary. Pick whichever fits.

### Homebrew (easiest, macOS and Linux)

```bash
brew tap mschulkind-oss/tap
brew install mschulkind-oss/tap/yolo-jail
```

Works on macOS and Linuxbrew. Single command, auto-upgrades with `brew upgrade`. No source checkout, no `just` required.

### Go

```bash
go install github.com/mschulkind-oss/yolo-jail/cmd/yolo@latest
```

Builds straight from the module. Needs Go on the host; puts `yolo` in `$GOBIN` (or `$(go env GOPATH)/bin`).

### pipx / uvx

```bash
pipx install yolo-jail
# or, to run without installing:
uvx yolo-jail
```

The PyPI distribution is per-platform wheels wrapping the same prebuilt Go binary — there is no Python code and no Python runtime dependency beyond the installer itself. It exists so the pre-Go audience keeps a working upgrade path.

### From source

For hacking on yolo-jail itself, or running an unreleased working tree. Identical on Linux and macOS:

```bash
git clone https://github.com/mschulkind-oss/yolo-jail.git
cd yolo-jail
just setup             # pinned toolchain (mise) + Go module deps
just deploy            # builds + installs the yolo CLI
```

To upgrade later: `cd yolo-jail && git pull && just deploy`

#### Upgrading from the Python version

yolo-jail used to ship as a Python package installed with `uv tool install`. `just deploy` retires that install for you — it uninstalls the `yolo-jail` uv tool and clears the console scripts it left in `$GOBIN` (`yolo`, `yolo-ps`, `yolo-host-processes`, `yolo-claude-oauth-broker-host`), which otherwise make `go install` fail with `build output "…/yolo" already exists and is not an object file`.

Nothing is deleted that cannot be positively identified as part of that old install. If something unrecognized is sitting at `$GOBIN/yolo`, the migration stops and asks you to look at it rather than guessing. `uv` itself is no longer a prerequisite.

### Optional — User-level defaults

```bash
yolo init-user-config
# Edit: ~/.config/yolo-jail/config.jsonc
```

**Platform-specific runtime setup** (one-time, needed for both install options):

```bash
# Linux — Podman
sudo pacman -S podman                   # or apt/dnf/pacman for your distro

# macOS — Apple Container (native, recommended)
brew install container skopeo
container system start

# macOS — Podman Machine
brew install podman
podman machine init --cpus 4 --memory 8192 --disk-size 50
podman machine start
```

On macOS, image builds use the NixOS binary cache by default — no builder to set up. If you add packages that aren't in the cache (or build offline), the from-source Linux build is offloaded automatically to a throwaway container on the container runtime you already have running. See [docs/guides/macos.md](docs/guides/macos.md).

For development, see [CONTRIBUTING.md](https://github.com/mschulkind-oss/.github/blob/main/CONTRIBUTING.md).

## Quick Start

Works identically on Linux and macOS:

```bash
# Navigate to any repository
cd ~/code/my-project

# Start an interactive shell in the jail
yolo

# Or run a command directly (agent installation is being reworked; see the Agents section)
yolo -- claude           # Claude Code in YOLO mode
yolo -- copilot          # Copilot with --yolo auto-injected
yolo -- opencode         # opencode.ai agent (auto-approve)
yolo -- pi               # pi.dev coding agent (auto-approve)
yolo -- codex            # OpenAI Codex CLI (auto-approve, sandbox off)

# Force a new container
yolo --new -- bash

# ALWAYS run this after every yolo-jail.jsonc edit, before restarting
yolo check

# Check your setup
yolo doctor

# List running jails
yolo ps

# Show full configuration reference
yolo config-ref
```

On macOS, `yolo doctor` additionally checks the VM backend (Podman Machine or Apple Container `system status`) — confirming the runtime is up, so that an uncached build can offload to a throwaway container on it.

### First Run

On first run, YOLO Jail will:
1. Build the Linux container image via `nix build` (takes a few minutes — both Linux and macOS download from the NixOS binary cache; on macOS, any non-cached package is built by offloading to an ephemeral container on the running runtime — no VM, no `sudo`, no first-boot)
2. Load the image into your container runtime
3. Install MCP servers, LSP servers, and utilities
4. Start your command

Subsequent runs are fast — tools are cached in persistent storage on both platforms.

### Auth Setup (One-Time)

Inside the jail, authenticate with your tools:

```bash
gh auth login          # GitHub CLI
# Claude Code authenticates via /login on first run
# codex login / opencode auth login / pi's /login work the same way
```

Each coding agent authenticates itself inside the jail — see the per-agent
auth column in [Agents](#agents). Agents that take a provider API key
(opencode, pi, codex) can instead read it from [`env_sources`](#configuration).

These tokens are stored in `~/.local/share/yolo-jail/home/` (same path on Linux and macOS) and persist across jail restarts. On both platforms, a host-side systemd timer (installed by `just deploy`) periodically refreshes the shared Claude OAuth token so jails never race the refresh flow.

## Configuration

Create a per-project config in `yolo-jail.jsonc`:

```jsonc
{
  "runtime": "podman",              // or "container" (Apple Container)
  "packages": ["strace", "htop"],   // extra nix packages
  "mounts": ["/path/to/ref-repo"],  // extra read-only mounts
  "network": {
    "mode": "bridge",               // or "host" for host networking
    "ports": ["8000:8000"]          // publish ports in bridge mode
  },
  "security": {
    "blocked_tools": ["curl", "wget"]
  }
  // "cache_relocations" exists too, but NOT here — user scope only, see below
}
```

Workspace config merges over user defaults (`~/.config/yolo-jail/config.jsonc`), and a sibling `yolo-jail.local.jsonc` — meant to be gitignored for per-machine overrides — auto-merges over the workspace config. Lists merge and dedupe, scalars override.

**Two keys opt out of that merge, and both for the same reason: a workspace config
lives inside the jail's writable mount, so an agent could otherwise grant itself
something.** `packs` and `cache_relocations` are read straight from
`~/.config/yolo-jail/config.jsonc` and nowhere else; `yolo check` errors if either
appears in `yolo-jail.jsonc`.

```jsonc
// ~/.config/yolo-jail/config.jsonc — never yolo-jail.jsonc
{
  // Everything a jail has beyond a bare shell. A bare NAME selects a pack that
  // ships with yolo; an address brings one from elsewhere. Nothing is on by
  // default, so with no entries here a jail has no coding agent.
  "packs": [
    "claude",                                    // a shipped agent pack
    "file:///home/me/code/my-skills-pack",       // a local pack of your own
    "git+ssh://git@github.com/org/repo//packs/team?ref=main"
  ]
}
```

A pack delivers a coding agent (its CLI, config files, skills and briefing), or
your own shared skills and house rules, or both. An EMBEDDED pack — one shipped
with yolo — may read a host file, which is how `claude` and `pi` compose your own
`settings.json` into the jail. A FETCHED pack never can: installing a
third-party pack approves distributing content, not handing that repository your
host config. Run `yolo pack --help` for authoring and `yolo pack install` to fetch.

**On `cache_relocations` specifically:** it moves a subdir of the jail cache onto other storage, bind-mounted **read-write** — which is the read-write host mount an agent must not be able to grant itself. Podman only.

```jsonc
// ~/.config/yolo-jail/config.jsonc — never yolo-jail.jsonc
{
  "cache_relocations": {
    // cache subdir name → absolute host path (the parent must already exist)
    "huggingface": "/data/relocated/yolo-jail/cache/huggingface"
  }
}
```

Moving an existing cache needs a stop-copy-configure-restart dance — see [Storage & Persistence](docs/guides/USER_GUIDE.md#relocating-a-cache-subdir-to-other-storage) in the user guide.

Run `yolo check` after **every** edit to `yolo-jail.jsonc` to validate the merged config, dry-run the generated jail agent configs, and preflight the image build before restarting into the jail. Inside a running jail, `yolo check --no-build` is the fast way to validate config changes mid-session before asking for a restart.

Run `yolo config-ref` for the full configuration reference.

## Agents

> [!IMPORTANT]
> **The `agents` config key has been REMOVED. An agent arrives as a `packs`
> entry.** A config still carrying `agents` is rejected with an error.
>
> Name the pack you want, in your USER config
> (`~/.config/yolo-jail/config.jsonc` — a workspace config cannot name one):
>
> ```jsonc
> { "packs": ["claude"] }   // also: copilot, codex, opencode, pi, agy
> ```
>
> **Nothing is on by default**, so a jail with no `packs` really has no coding
> agent, and says so at launch and in `yolo check`.

YOLO Jail is a **library of coding agents**. Which ones a jail gets follows from
the packs you configure, and nothing in the core knows what an agent is — the six
below are pack files (`packs/*/pack.json`), not Go code.

- **No rebuild:** agents install lazily on first use, so changing `packs` never
  rebuilds the image — just restart the jail.

Each agent is launched with its autonomous/YOLO mode auto-enabled (the jail
container is the security boundary), and authenticates itself **inside the
jail** — host credentials never cross the boundary.

| Agent | pack name | Run | Install | Auth (inside the jail) |
|---|---|---|---|---|
| **Claude Code** | `claude` | `yolo -- claude` | native installer | `/login` on first run |
| **GitHub Copilot** | `copilot` | `yolo -- copilot` | npm `@github/copilot` | `/login` (GitHub OAuth) |
| **opencode** | `opencode` | `yolo -- opencode` | npm `opencode-ai` | `opencode auth login`, or a provider key (e.g. `ANTHROPIC_API_KEY`/`OPENAI_API_KEY`) |
| **pi** ([pi.dev](https://pi.dev)) | `pi` | `yolo -- pi` | npm `@earendil-works/pi-coding-agent` | `pi` `/login`, or a provider key |
| **OpenAI Codex** | `codex` | `yolo -- codex` | npm `@openai/codex` | `codex login` (ChatGPT), or `OPENAI_API_KEY` |
| **Antigravity** | `agy` | `yolo -- agy` | native installer | Google sign-in on first run |

Provider API keys are easiest to supply via [`env_sources`](#configuration)
(a gitignored dotenv file) so they reach the agent inside the jail without
living in your committed config. MCP servers you configure (`mcp_presets` /
`mcp_servers`) are wired into every selected agent that supports MCP —
claude, copilot, opencode, codex, and agy (pi has no native MCP).

## Isolation backends

The `runtime` config picks how the agent is isolated:

- **`podman`** (Linux, default) / **`container`** (macOS, Apple Container) —
  the agent runs in a Linux container. Strongest boundary (kernel/VM
  isolation, resource caps). On macOS this means a lightweight Linux VM —
  **native arm64 on Apple Silicon (no emulation)**; see [docs/guides/macos.md](docs/guides/macos.md).

## Security

- **Strict Isolation**: No access to host `~/.ssh/`, `~/.gitconfig`, or cloud credentials
- **Separate Auth**: Run `gh auth login`, `codex login`, etc. inside the jail once
- **User Mapping**: Files created in the jail are owned by your host user (matching UID/GID)
- **Blocked Tools**: Configurable list of tools that return clear error messages
- **Config Safety**: Changes to `yolo-jail.jsonc` require human confirmation at next startup — agents cannot silently modify the jail environment. See [docs/design/config-safety.md](docs/design/config-safety.md).
- **Read-Only Mounts**: Extra mounts are read-only by default

## Troubleshooting

Run `yolo doctor` to diagnose common setup issues:

```bash
yolo doctor
```

This checks your container runtime, Nix installation, configuration files, image status, and running containers.

Run `yolo check` after **every** config edit, especially when handing work from an outside agent into the jail or when an in-jail agent edits `yolo-jail.jsonc` mid-session and needs to verify the restart will succeed.

## Contributing

See [CONTRIBUTING.md](https://github.com/mschulkind-oss/.github/blob/main/CONTRIBUTING.md) for development setup and guidelines.

## Documentation

- [User Guide](docs/guides/USER_GUIDE.md) — Detailed setup, configuration, and troubleshooting
- [macOS Setup](docs/guides/macos.md) — macOS-specific installation and setup guide
- [Platform Comparison](docs/research/platform-comparison.md) — Feature matrix: Linux vs macOS
- [Config Safety](docs/design/config-safety.md) — How config change approval works
- [Storage & Config](docs/design/storage-and-config.md) — Storage hierarchy and mount layout
- [Happy-path principle](docs/design/happy-path-principle.md) — fill the matrix, support one tool per capability

## License

[Apache License 2.0](LICENSE)

