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

89 lines
3.7 KiB
Markdown

# 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
```bash
# 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
```bash
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/`](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.