Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cuc — Claude Unleashed Container

build-image

A polyglot dev container (like a VS Code dev container) that runs a project and hosts a Claude Code instance inside it, driveable from the host by sax.

Toolchain baked in: Node 22, Go 1.22, Rust (rustup), plus git, ripgrep, fd, jq, build-essential, python3.

Pull the prebuilt image

CI publishes a multi-arch image (amd64 + arm64/Apple Silicon) to GHCR:

docker pull ghcr.io/asctheone/cuc:latest

Run it directly (mount a project + your Claude creds, keep it alive for exec):

docker run -d --name cuc-dev \
  -v "$PWD:/workspace" \
  -v "$HOME/.claude:/host-claude:ro" \
  -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
  ghcr.io/asctheone/cuc:latest sleep infinity

docker exec -it -u dev cuc-dev bash -lc 'cd /workspace && claude'

To use the published image via compose instead of building locally, set image: ghcr.io/asctheone/cuc:latest and drop the build: block in docker-compose.yml.

Design decisions

Question Choice Why
How to run Claude as root? Non-root dev user by default, claude-unleashed as opt-in fallback claude-unleashed patches the CLI's JS source, but the current CLI (v2.1.102+) is a compiled binary — the patches no longer apply. Running as a normal user sidesteps the root check entirely and works today.
Where does sax live? Host, driving docker exec Keeps sax's daemon + MCP server on your machine; the container stays lean.
Auth Mount ~/.claude read-only, copy creds into the container; ANTHROPIC_API_KEY fallback No secrets baked into the image.

⚠️ macOS auth caveat. On macOS the Claude OAuth token lives in the Keychain, not in a mountable ~/.claude/.credentials.json. scripts/cuc-up.sh auto-runs scripts/cuc-login-sync.sh, which extracts the Keychain token into the container and sets the account + onboarding flag (needed by the interactive TUI, not just -p). The login persists in a named volume (cuc-claude) across container removal/rebuild — re-run login-sync only to refresh an expired token, or set ANTHROPIC_API_KEY in .env instead.

Layout

.devcontainer/
  Dockerfile          polyglot image; INSTALL_UNLEASHED build arg
  devcontainer.json   VS Code "Reopen in Container" path
  post-create.sh      seeds creds + symlinks ~/.claude.json into the volume
docker-compose.yml    host-driven path; named volume cuc-claude persists auth
.env.example          CUC_PROJECT / INSTALL_UNLEASHED / ANTHROPIC_API_KEY
scripts/
  cuc-up.sh           start (+ --build) + chown volume + seed + login-sync
  cuc-claude.sh       docker exec a Claude instance (non-root, default)
  cuc-login-sync.sh   transpose host Keychain login into the container
  cuc-unleashed.sh    opt-in root/bypass fallback
  cuc-down.sh         stop
sax/
  sax-claude.sh       run the container's Claude as a sax session (detach/reattach)
  services.example.json  merge into ~/.claude/services.json

Quick start (host-driven)

cp .env.example .env          # set CUC_PROJECT + ANTHROPIC_API_KEY if needed
scripts/cuc-up.sh             # build image, start cuc-dev, seed creds
scripts/cuc-claude.sh         # launch Claude inside the container
scripts/cuc-down.sh           # stop when done

Mount a different project (not this repo) by setting CUC_PROJECT to its host path in .env — it lands at /workspace inside the container.

VS Code path

Open this folder in VS Code → Reopen in Container. devcontainer.json builds the same image, mounts your creds, and runs post-create.sh.

sax integration (the end goal)

sax runs the container's Claude as a first-class host session — so you get detach/reattach and the full scripting + MCP surface:

sax/sax-claude.sh                    # create + attach "cuc-claude"
sax --tail cuc-claude 30             # peek at output
sax --send cuc-claude $'/help\n'     # drive it

From a Claude agent on the host, the same via MCP:

sax_exec(name="cuc-claude", command="docker exec -it -u dev cuc-dev bash -lc 'cd /workspace && claude'")
sax_tail(name="cuc-claude", lines=30)

Merge sax/services.example.json into ~/.claude/services.json so "start the cuc claude instance" resolves without rediscovery.

The unleashed (root) fallback

Only if you specifically need root and are on a legacy/pinned JS CLI:

INSTALL_UNLEASHED=true docker compose build
scripts/cuc-unleashed.sh

Expect this to fail on the current compiled CLI — that's why non-root is the default.

About

Polyglot dev container (Node/Go/Rust) that hosts a Claude Code instance, driven from the host by sax

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages