Open source · Docker isolation · One command
Give the agent a cage, not your keys.
Unattended, not unguarded.
Run Claude Code with full autonomous power, safely sandboxed in Docker.
$ curl -fsSL https://cleat.sh/install | bash macOS, Linux, WSL
One command
Run it. It's already contained.
~ $ cleat
✔ Image ready (cached)
✔ Container started
✔ Auth shared
✔ Claude launched
Container: cleat-backend-a1b2c3d4
Project: ~/backend (same path, sandboxed)
Caps:
mount: git, ssh, env, hooks, gh
sandbox: docker (breaks isolation)
Claude is analyzing your project...
❯ ▊
A real recording: Claude Code hunts for keys and finds nothing, then rm -rf's the box's own OS. The host doesn't notice.
They say an agent bricked someone's Mac overnight. The truth is scarier: it can leak your keys and wipe your work, no reboot required. So I built Cleat.
Amplify the agent. Contain the blast.
| Without isolation | With Cleat | |
|---|---|---|
| Same power | ||
| Edit project files | ✓ | ✓ |
| Clipboard to host | ✓ | ✓ bridged |
| Paste an image (ctrl+v) | ✓ | ✓ restored |
| Browser auth (OAuth) | ✓ | ✓ bridged |
| Install packages | ⚠ your system | ✓ contained |
| Run any command | ⚠ your system | ✓ contained |
| Test Docker apps | ⚠ your system | ✓ opt-in via --cap docker |
| Only in the cage | ||
| Parallel agents on one repo | one shared copy | ✓ isolated fork each |
| Planner + worker team | wire it by hand | ✓ one command cleat kit |
| Start the Docker daemon | start it yourself | ✓ autopilot |
| Smaller blast radius | ||
| Access other projects | ⚠ exposed | ✓ blocked |
| Modify your system | ⚠ exposed | ✓ blocked |
| Your machine stays clean | ⚠ global installs pile up | ✓ deps stay in the box |
| Read ~/.ssh, credentials | ⚠ exposed | ✓ blocked |
| Claude config & approvals | ⚠ shared, corruptible | ✓ per-project |
| Safe to leave overnight | ⚠ risky | ✓ yes |
Architecture
Your machine. Docker container. Clean boundary.
/home/coder/.claude
/workspace
caps:
mount: git, ssh, env, hooks, gh
sandbox: docker (breaks isolation)
Claude Code (full permissions)
Can: read/write project, install packages, run cmds
Cannot: touch host or other projects (by default)
Everything else is untouched.
Why not just write a Dockerfile? You could. But then you handle UID/GID mapping, clipboard bridging, browser auth, hook forwarding, host connectivity, session persistence, per-project container naming and a dozen edge cases. Cleat handles all of it in one command.
Runs where you work
- Host
- macOS on Apple Silicon and Intel, Linux, Windows via WSL2
- Engine
- Docker Desktop, OrbStack, Colima, Rancher Desktop, Lima, rootful and rootless Linux
- Shell
- bash 3.2 (the macOS default) through bash 5
- Arch
- arm64 and amd64, prebuilt images for both
The whole matrix runs in CI on every push: real macOS, Linux, WSL2 and arm64 runners, real Docker engines, the real bash 3.2. The docker capability is engine-aware, so a caged agent reaches your daemon wherever it runs, rootless and Colima included.
Features
One command. Everything wired up.
$ cleat
✔ Docker tuned for Cleat (24 GB VM, room for many parallel sessions)
✔ Image ready (cached)
▸ New Claude Code available: 2.1.226 → 2.1.227 (latest)
Update the image before starting? (downloads the new build) [Y/n] y
✔ Claude Code upgraded (2.1.226 → 2.1.227)
✔ Container started
✔ Auth shared
✔ Claude launched
Container: cleat-myapp-a1b2c3d4
Project: ~/projects/myapp → /workspace
Caps: git, ssh
┌──────────────────────────┐
│ 8 hours later │
│ 47 files changed │
│ All tests passing │
│ Host system: untouched ✔ │
└──────────────────────────┘
One command. Always the latest Claude. Walk away.
$ cleat fork start feat-a
✔ Workspace copied
✔ Container started
✔ Auth shared
✔ Claude launched
Container: cleat-api-a1b2c3d4-feat-a
Project: ~/projects/api (not mounted, this box works on a copy)
Fork: ~/.config/cleat/forks/cleat-api-a1b2c3d4-feat-a → /workspace (copied just now)
Caps: git, ssh
Several agents on one repo, each in its own copy.
Your working tree is never in the room.
$ cleat ps
Cleat containers:
● cleat-api-a1b2c3d4
Up 2 hours
/Users/marcin/projects/api
● cleat-web-e5f6a7b8
Up 45 minutes
/Users/marcin/projects/web
● cleat-docs-c9d0e1f2
Exited (0) 5 hours ago
/Users/marcin/projects/docs
Resume with: cd <project-dir> && cleat resume
$ cleat storage
Docker storage one shared store, used by every container
▸ Disk ███████████████████████░░░ 53 / 60 GB · 88% full
On disk (all projects) size reclaimable
Images 36 GB 21 GB
Build cache (shared) 7.8 GB 5.4 GB
Cleat can reclaim
7.0 GB stale cleat images cleat prune
5.4 GB shared build cache cleat prune --cache
Other projects hold the rest. Cleat never deletes them.
$ cleat kit
Cleat Kits (curated Claude pre-configurations)
Box: main · other boxes: cleat kit <name> <box>
▸ ● plan-big-execute-small
○ none (your own config, no kit)
Run your session on Fable 5: it plans and reviews while
worker and scout subagents (Sonnet 5 by default) execute
and explore, each in its own context window so the main
session stays lean. Flagship judgment on the plan and every
review, the mechanical bulk billed at the worker model's
rate, so heavy work burns your rate limit far slower.
Adapted from Anthropic's coordinator-pattern cookbook.
↑/↓ navigate ⏎/→ select q cancel
A better place to run Claude, not just a safer
one: Fable 5 judgment on the plan and every
review, a Sonnet 5 crew on the legwork, per box.
$ cleat
✖ Docker isn't installed.
Install Docker now? (via Homebrew)
1) Docker Desktop (the standard; free for personal use, paid for larger companies)
2) OrbStack (fast, mac-native; free for personal use, paid commercially)
3) Colima (open source, CLI-only, no GUI)
n) no
Choice [1/2/3/n] 1
✔ Docker installed
✔ Container started
No Docker? Cleat asks first, then installs and
carries on. macOS, Linux and WSL2. Never a blind pipe.
$ cleat
! Docker isn't running. Starting Docker Desktop...
⠹ waiting for the Docker VM (14s)
✔ Docker ready
✔ Image ready (cached)
✔ Container started
✔ Auth shared
✔ Claude launched
Rebooted? Docker Desktop, OrbStack, Colima,
WSL2: cleat starts your Docker and carries on.
$ cleat stop
✔ Session ended. Resume with: cleat resume
$ cleat resume
✔ Session resumed
Back where you left off.
> copy the API key to clipboard
● Bash(echo -n "sk-…" | pbcopy)
⎿ (No output)
● Done. The API key is in your clipboard.
⏵⏵ bypass permissions on (shift+tab to cycle)
Bridged to host. No X11. Zero config.
$ cleat config
Cleat config (capabilities + resources)
Scope: global (~/.config/cleat/config)
Capabilities (space toggles host access)
[✔] git Mount ~/.gitconfig (read-only) for commit identity
[·] ssh Mount ~/.ssh (read-only) and forward SSH agent
[·] env Load env vars from ~/.config/cleat/env and .cleat.env
[·] hooks Run your Claude Code hooks on the host (global + project)
[·] gh GitHub CLI auth (persists across rm/nuke/rebuild)
[·] docker Host Docker socket (breaks sandbox) to test Docker apps
Resources (←/→ changes the value)
▸ memory ‹ 16g › (per-box RAM ceiling)
cpus all (core limit; all = no cap)
[·] Also write these to this project's .cleat
note: over half the 24 GB Docker VM. Two boxes at this ceiling can
oversubscribe it, so run one at a time.
↑/↓ move space toggle ←/→ change ⏎ save q cancel
$ cleat
▸ Project .cleat requests host access: git, docker (beyond the sandbox)
Trust this project's .cleat? (applies its caps; approve once, undo with cleat untrust) [y/N] _
A cloned repo can't grant itself your keys.
Approve once, per project. Default is deny.
$ cleat shell
▸ Container cleat-api-a1b2c3d4
coder@a1b2c3d4e5f6:/workspace$
Drop into bash. Debug anything.
Same container, same state.
Give the agent a cage, not your keys.
Start sandboxing Claude Code with one command.
Because you shouldn't need a Time Machine backup to use AI.
$ curl -fsSL https://cleat.sh/install | bash macOS, Linux, WSL