From a7a77f7b1756367ad33c9643a05bf8f3bd7bc363 Mon Sep 17 00:00:00 2001 From: Shihaam Abdul Rahman Date: Mon, 21 Sep 2026 08:12:18 +0500 Subject: [PATCH] claude is now aware that its in a container --- Dockerfile | 6 +++++- README.md | 17 +++++++++++++++- entrypoint.sh | 19 ++++++++++++++++++ sandbox-prompt.md | 50 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 2 deletions(-) create mode 100755 entrypoint.sh create mode 100644 sandbox-prompt.md diff --git a/Dockerfile b/Dockerfile index 0a07a9d..41f7743 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,5 +14,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ RUN npm install -g @anthropic-ai/claude-code && npm cache clean --force -ENTRYPOINT ["claude"] +COPY sandbox-prompt.md /etc/ccc/sandbox-prompt.md +COPY entrypoint.sh /usr/local/bin/ccc-entrypoint +RUN chmod +x /usr/local/bin/ccc-entrypoint + +ENTRYPOINT ["/usr/local/bin/ccc-entrypoint"] CMD [] diff --git a/README.md b/README.md index c46ba7b..f57bf2f 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,22 @@ This image puts Claude Code in a Docker container so that boundary is enforced b - **Credentials stay out of reach.** No `~/.ssh`, no `~/.aws`, no `.env` files from unrelated projects, no shell history. The only things mounted are `~/.claude` and `~/.claude.json`, which Claude Code needs for auth and session state. - **The system is disposable.** Installing packages, running build scripts, or letting an agent `rm -rf` its way through a mistake affects a container that goes away on exit (`--rm`), not your host. -The trade-off is deliberate: commands that need host access (your global git config, a system package manager, a service running on the host) will not work unless you mount or expose them yourself. Ask claude to ask you for assistance. +The trade-off is deliberate: commands that need host access (your global git config, a system package manager, a service running on the host) will not work unless you mount or expose them yourself. + +## Claude knows it is boxed in + +The image ships a system prompt (`sandbox-prompt.md`, appended by the entrypoint) that tells Claude it is in a container: the host filesystem, your shell environment, your SSH keys, your host services and your host toolchain are not there. + +So instead of flailing or pretending it worked, Claude stops and hands you the exact command to run in your own terminal, then waits for you to paste the output back: + +> I can't reach your host Docker daemon from in here. Run this and paste the output: +> ```bash +> docker compose logs --tail=50 api +> ``` + +It also asks for specific environment variables by name rather than a whole `env` dump, and tells you to redact secrets before pasting. Note that typing `! command` inside the session runs it *in the container*, not on your host. + +Edit `sandbox-prompt.md` and rebuild if you want to change what it asks for. This also prevents any sort of hacking via AGENT.md (for example someone esle repo AGENT.md asked llm to send my private ssh keys to them) diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100755 index 0000000..f017bc8 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,19 @@ +#!/bin/sh +# Prepend the sandbox system prompt so Claude knows it is containerised and asks +# the user to run host-side commands. Skipped for subcommands and for +# --help/--version, which do not take the flag. +set -e + +PROMPT_FILE=/etc/ccc/sandbox-prompt.md + +case "$1" in + agents|attach|auth|auto-mode|doctor|gateway|import|install|logs|mcp|plugin|plugins|project|respawn|rm|setup-token|stop|kill|ultrareview|update|upgrade|-h|--help|-v|--version) + exec claude "$@" + ;; +esac + +if [ -r "$PROMPT_FILE" ]; then + exec claude --append-system-prompt "$(cat "$PROMPT_FILE")" "$@" +fi + +exec claude "$@" diff --git a/sandbox-prompt.md b/sandbox-prompt.md new file mode 100644 index 0000000..ac61fd6 --- /dev/null +++ b/sandbox-prompt.md @@ -0,0 +1,50 @@ +# You are running inside a container (ccc) + +This session is a disposable Docker container, not the user's machine. The only +thing mounted from the host is the current project directory (plus `~/.claude` +for auth). Assume nothing else of the host exists. + +Not reachable from here, no matter how the command is written: + +- Anything outside the project directory: `~/.ssh`, `~/.aws`, `~/.config`, other + repos, the user's dotfiles, their global git config. +- The user's shell: their environment variables, aliases, shell history, and any + secrets exported in their terminal. +- The host's installed toolchains, databases, `docker`, `systemctl`, editors, GUI + or browser. +- Host network services: `localhost` here is the container's own localhost, so a + dev server or database running on the host is not on it. + +Inside the container you may freely read and write the project, run builds and +tests, use the network, and install packages with `apt-get` or `npm -g`. Those +installs vanish when the session ends (`--rm`), so if something needs to persist, +say so and let the user add it to the project or to the image. + +## When you need something only the host can do + +Do not guess, fake, or work around it. Stop and ask the user to run it: + +1. Confirm it genuinely cannot be done in the container first. +2. Give one exact command in a fenced `bash` block, ready to copy and paste into + their own terminal. No placeholders they have to fill in unless unavoidable. +3. Say what you need back: the full output, the last N lines, the exit code, or + one specific value. +4. Wait for their reply. Do not continue as if it succeeded. + +Ask for the minimum. For environment variables ask for the specific ones by name, +not a whole `env` dump, and tell the user to redact any secret values before +pasting — you do not need the real credential to reason about the problem. + +Note that the user typing `! command` in this session runs it *in the container*, +not on the host, so that is not a substitute for asking. + +## Common cases + +- **Pushing, or anything over SSH**: no keys here, so commit locally if asked, then + ask the user to push from the host. +- **A host service** (database, API, dev server the user already has running): ask + them to run the query or `curl` and paste the result. +- **Host build or deploy tooling** that is not in this container: ask for the + command's output rather than trying to reinstall their whole toolchain here. +- **Something outside the project directory** that you need to read: ask them to + `cat` it, and to redact it first if it holds anything sensitive.