Sandboxed Claude Code environment for autonomous agent work on the Orcha monorepo.
--dangerously-skip-permissions safely because it's sandboxed┌─────────────────────────────────────────────────────────────────────┐
│ sandbox-network │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ claude-sandbox │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌─────────────┐ │ │
│ │ │ Claude Code │───▶│ clj-nrepl-eval│───▶│ nREPL │ │ │
│ │ │ (TTY) │ │ │ │ :7888 │ │ │
│ │ └──────────────┘ └──────────────┘ └─────────────┘ │ │
│ │ │ │ │ │
│ │ │ Clojure app connects to: │ │ │
│ │ │ - postgres:5432 │ │ │
│ │ │ - localstack:4566 │ │ │
│ │ ▼ ▼ │ │
│ │ /workspace (mounted worktree) │ │
│ │ /root/.claude (host auth + sandbox projects) │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ postgres │ │ localstack │ │
│ │ :5432 │ │ :4566 │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
orcha/
├── sandbox/
│ ├── docker-compose.yml # Sandbox services
│ ├── Dockerfile # Claude + Clojure + Babashka image
│ └── entrypoint.sh # Starts nREPL, then Claude
├── .worktrees/
│ └── feature/
│ └── <name>/
│ ├── .claude-sandbox/
│ │ └── projects/ # Conversation persistence
│ ├── .nrepl-port # Port file for editor connection
│ └── <code>
└── .gitignore # Add .claude-sandbox/
sandbox/Dockerfile:
eclipse-temurin:21-jdk-alpinesandbox/docker-compose.yml:
orcha-sandbox-${FEATURE_NAME} (isolates volumes)${WORKTREE_PATH}:/workspace${HOME}/.claude:/root/.claude:ro (auth from host)${WORKTREE_PATH}/.claude-sandbox/projects:/root/.claude/projects (persistence)${HOME}/.m2:/root/.m2 (Maven deps)${HOME}/.gitlibs:/root/.gitlibs (Git deps)${HOME}/.clojure/.cpcache:/root/.clojure/.cpcache (classpath cache)${NREPL_HOST_PORT}:7888 (nREPL to host)Entrypoint starts nREPL with cider middleware:
clojure -Sdeps '{:deps {nrepl/nrepl {:mvn/version "1.3.1"}
cider/cider-nrepl {:mvn/version "0.56.0"}
refactor-nrepl/refactor-nrepl {:mvn/version "3.11.0"}}
:aliases {:cider/nrepl {:main-opts ["-m" "nrepl.cmdline"
"--middleware" "[refactor-nrepl.middleware/wrap-refactor,cider.nrepl/cider-middleware]"]}}}' \
-M:dev:test:cider/nrepl \
-p 7888 \
-b 0.0.0.0
User can start full Integrant system with (go) if needed.
hash(feature-name) % 1000 + 17000 → range 17000-17999bb sandbox:start foo --port 18000.worktrees/feature/<name>/.nrepl-portAdd :sandbox profile to config.edn:
:com.getorcha/aws
{:config {:endpoint #profile {:local-dev "http://localhost:4566"
:sandbox "http://localstack:4566"
:default nil}}}
:com.getorcha/db-credentials-json
#profile {:local-dev "{\"host\":\"localhost\",...}"
:sandbox "{\"host\":\"postgres\",...}"
:default #orcha/param "/v1-orcha/db-credentials"}
Profile activated via ENVIRON_NAME=sandbox in entrypoint.
bb sandbox:start <name> [--port PORT] # Create worktree, start containers, attach to Claude
bb sandbox:stop <name> # Stop containers
bb sandbox:list # List running sandboxes
bb sandbox:clean <name> [--worktree] # Remove containers/volumes, optionally worktree
Start:
$ bb sandbox:start booking-ocr
Creating worktree .worktrees/feature/booking-ocr from main...
nREPL will be exposed on port 17342
Starting sandbox containers...
claude>
Connect editor:
$ cat .worktrees/feature/booking-ocr/.nrepl-port
17342
# Connect to localhost:17342
Resume after exit:
$ bb sandbox:start booking-ocr
# Worktree exists, just starts containers
# Conversation history preserved in .claude-sandbox/projects/
Clean up:
$ bb sandbox:clean booking-ocr --worktree
~/.claude/ mounted read-only for subscription auth.claude-sandbox/projects/ overlays for conversation persistenceHost directories mounted read-write to avoid re-downloading:
~/.m2 - Maven deps~/.gitlibs - Git deps~/.clojure/.cpcache - Classpath cache