Files
safeclaude/README.md
2026-06-20 15:39:54 -04:00

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 a hooks/ 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 and docker volume resets 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.