3.7 KiB
safeclaude
Run Claude Code inside a locked-down container, one per project. Your code is shared with the container so Claude can edit it, but everything Claude runs stays boxed in — so a bad command can't touch the rest of your machine.
It works like a language version manager (think rvm/nvm/pyenv): this repo
holds only the shared plumbing, and each project keeps its own setup in a
small .safeclaude/ folder. When you launch, safeclaude reads that folder,
builds the container once, and reuses it after that.
Install
# Put the launcher on your PATH (any dir on your PATH works)
ln -s "$(readlink -f ./safeclaude)" ~/.local/bin/safeclaude
That's it — the first launch builds what it needs automatically.
Use
cd ~/my-project
safeclaude init # create a .safeclaude/ folder, then edit it for your project
safeclaude # launch Claude in this project's container
| Command | What it does |
|---|---|
safeclaude [claude-args...] |
Launch Claude for the project here. Anything extra is passed straight to claude. |
safeclaude build |
Rebuild the project's container from scratch. |
safeclaude init |
Create a starter .safeclaude/ in the current directory. |
safeclaude envs |
List the containers and stored data safeclaude has created. |
safeclaude version |
Print the safeclaude version. |
safeclaude always works on the project you're in — the current directory, or the
nearest parent that has a .safeclaude/ folder, so launching from a
subdirectory still finds the right one.
What goes in .safeclaude/
.safeclaude/
Dockerfile # which system packages / language versions this project needs
hooks/ # setup scripts that run each time the container starts
cache/ # scratch space on the host, gitignored (dependencies, downloads…)
.env # secrets, kept out of git (.env.example shows the format)
version # the safeclaude version this config was created with
README.md # how this environment works and how to change it
That README.md is also what the sandboxed Claude reads to learn how the
environment is set up — so it can tell you exactly what to change without
digging through your project.
A few things worth knowing:
- Two places setup can live, and the difference matters. Slow, one-time
installs (system packages, a pinned language version) go in the
Dockerfile— these get cached, so they don't repeat. Anything that needs your actual code present, or that should persist between runs (installing dependencies, starting a database proxy), goes in ahooks/script that runs at launch. - It only rebuilds when something changed. safeclaude remembers what it already built, so a normal launch starts right up with no waiting.
- Hooks are safe to run every time. They check before doing work, so a launch with nothing to do is near-instant.
cache/is your scratch space. It lives on the host and is gitignored, so it survives rebuilds anddocker volumeresets without ending up in your repo — a good home for installed dependencies, downloads, or "already did this" markers.
See example/ for a real, filled-in Ruby + Postgres setup you can
copy from.
Security
These are applied every launch, by safeclaude itself — a project's own config can't loosen them:
- Claude runs as a normal user, not root, so it can't make system-wide changes.
- The container is stripped of special privileges it doesn't need.
- It can't gain new privileges partway through.
- Each project gets its own private storage, so one project can't see or break another's setup.
Network access is otherwise normal. If you want to cut it off, that's a small change in the launcher.