diff --git a/README.md b/README.md index 879c4ca..4c7218b 100644 --- a/README.md +++ b/README.md @@ -47,8 +47,13 @@ subdirectory still finds the right one. 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 diff --git a/example/.safeclaude/README.md b/example/.safeclaude/README.md new file mode 100644 index 0000000..0add8b9 --- /dev/null +++ b/example/.safeclaude/README.md @@ -0,0 +1,35 @@ +# .safeclaude/ — this project's sandboxed environment + +This folder defines the container that `safeclaude` runs Claude in. The container +is built from these files, so changing the environment means editing them on the +host and rebuilding — not installing things inside the running container (which +is a non-root sandbox and gets reset each run). + +## What's here + +- `Dockerfile` — the container image: system packages and pinned language + versions (one Ruby, one Node, etc.). Built once, then cached. +- `hooks/*.sh` — scripts that run at every startup, with the project at `/code`. + Use these for setup that needs your code present or should run each launch + (installing dependencies, starting a service proxy). Keep them safe to re-run. +- `cache/` — scratch space on the host, gitignored. A good home for installed + dependencies, downloads, or "already did this" markers; survives rebuilds and + `docker volume` resets. +- `.env` — secrets passed into the container at runtime (gitignored; copy from + `.env.example`). +- `version` — the safeclaude version this config was created with. + +## How to change the environment + +The container runs as a non-root user with no sudo, so you can't install system +packages from inside it. Instead, edit these files on the host: + +- **Add a system package:** add it to `Dockerfile`, then run `safeclaude build`. +- **Add a language or tool:** install a specific version in `Dockerfile` — pin + it, since a project only needs one. See the repo's `example/` for a worked + Ruby + Node setup. +- **Run setup at startup:** add or edit a script in `hooks/` (no rebuild needed). +- **Add a secret:** put it in `.env` (see `.env.example`). + +After editing the `Dockerfile`, run `safeclaude build` to rebuild. Hook, `.env`, +and `cache/` changes take effect on the next launch with no rebuild. diff --git a/example/README.md b/example/README.md index 2095890..2bced8b 100644 --- a/example/README.md +++ b/example/README.md @@ -21,6 +21,7 @@ reference you can copy from when setting up your own project. | `hooks/30-pg-proxy.sh` | each launch | lets the app reach the host's Postgres at the usual `127.0.0.1:5432` | | `.env.example` | — | copy to `.env` for a private gem token (kept out of git) | | `version` | — | the safeclaude version this config was created with | +| `README.md` | — | how this environment works (also read by the sandboxed Claude) | A couple of things to take away: diff --git a/safeclaude b/safeclaude index c45adb3..837da8e 100755 --- a/safeclaude +++ b/safeclaude @@ -29,8 +29,16 @@ has a .safeclaude/ folder. EOF } -# Turn a folder name into something Docker is happy to use as a name. -proj_name() { basename "$1" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9_.-' '-'; } +# Turn a folder name into something Docker is happy to use as a name fragment: +# lowercase, only [a-z0-9_.-], and no leading/trailing separators (Docker rejects +# those). We capture basename into a variable first so the trailing newline is +# gone before tr runs — otherwise tr -c turns that newline into a stray '-'. +proj_name() { + local n; n="$(basename "$1")" + n="$(printf '%s' "$n" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9_.-' '-')" + n="$(printf '%s' "$n" | sed -e 's/^[-_.]*//' -e 's/[-_.]*$//')" + printf '%s' "${n:-project}" +} # Look in this folder, then its parents, for a project (one that has a # .safeclaude/) — so you can launch from a subdirectory and still find it. @@ -97,6 +105,7 @@ cmd_run() { - This project is mounted at /code. On the host it lives at: ${proj} - You run as the non-root user 'coder'. You cannot use sudo or install system packages. - The .safeclaude/ directory configures this container. NEVER edit anything under .safeclaude/ yourself — instead, tell the developer exactly what to change and have them do it on the host. +- How this environment is set up and how to change it is documented in /code/.safeclaude/README.md — read it before advising on environment changes. - To add a system package: ask the developer to add it to .safeclaude/Dockerfile and run 'safeclaude build' on the host. - To add setup that runs at container startup: ask the developer to add a script to .safeclaude/hooks/. - /home/coder persists between runs. Edits under /code are real and shared with the host. Everything else is discarded when the container exits." diff --git a/skeleton/README.md b/skeleton/README.md new file mode 100644 index 0000000..0add8b9 --- /dev/null +++ b/skeleton/README.md @@ -0,0 +1,35 @@ +# .safeclaude/ — this project's sandboxed environment + +This folder defines the container that `safeclaude` runs Claude in. The container +is built from these files, so changing the environment means editing them on the +host and rebuilding — not installing things inside the running container (which +is a non-root sandbox and gets reset each run). + +## What's here + +- `Dockerfile` — the container image: system packages and pinned language + versions (one Ruby, one Node, etc.). Built once, then cached. +- `hooks/*.sh` — scripts that run at every startup, with the project at `/code`. + Use these for setup that needs your code present or should run each launch + (installing dependencies, starting a service proxy). Keep them safe to re-run. +- `cache/` — scratch space on the host, gitignored. A good home for installed + dependencies, downloads, or "already did this" markers; survives rebuilds and + `docker volume` resets. +- `.env` — secrets passed into the container at runtime (gitignored; copy from + `.env.example`). +- `version` — the safeclaude version this config was created with. + +## How to change the environment + +The container runs as a non-root user with no sudo, so you can't install system +packages from inside it. Instead, edit these files on the host: + +- **Add a system package:** add it to `Dockerfile`, then run `safeclaude build`. +- **Add a language or tool:** install a specific version in `Dockerfile` — pin + it, since a project only needs one. See the repo's `example/` for a worked + Ruby + Node setup. +- **Run setup at startup:** add or edit a script in `hooks/` (no rebuild needed). +- **Add a secret:** put it in `.env` (see `.env.example`). + +After editing the `Dockerfile`, run `safeclaude build` to rebuild. Hook, `.env`, +and `cache/` changes take effect on the next launch with no rebuild.