commit 243b84946fa923aa05d64581efbcc6241c8f1bb1 Author: Lexical Bits Date: Sat Apr 11 09:26:46 2026 -0400 v1 - basic functionality diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..493e885 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,25 @@ +FROM node:22-slim + +# Install runtime dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + curl \ + ca-certificates \ + git \ + ripgrep \ + bash \ + && rm -rf /var/lib/apt/lists/* + +# Create a non-root user with a real home directory +RUN useradd -m -s /bin/bash -u 1001 coder + +# Ensure ~/.local/bin is on PATH (where the native installer puts the binary) +ENV PATH="/home/coder/.local/bin:/home/coder/.claude/bin:${PATH}" + +# Copy entrypoint (as root, then lock down) +COPY --chmod=755 entrypoint.sh /usr/local/bin/entrypoint.sh + +USER coder +WORKDIR /code + +ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] +CMD ["bash"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..3b6bd72 --- /dev/null +++ b/README.md @@ -0,0 +1,76 @@ +# Claude Code — Dockerized + +A minimal, guardrailed container for running Claude Code. The home +directory and project folder are volumes, keeping your Claude install +and credentials separate from any specific project. + +## Setup + +```bash +# 1. Build the image +docker compose build + +# 2. Export your API key (or put it in a .env file) +export ANTHROPIC_API_KEY=sk-ant-... + +# 3. First run — installs Claude Code into the home volume, then drops you +# into an interactive shell inside the default ./code directory +docker compose run --rm claude-code +``` + +On first start the entrypoint runs the native installer and places the +binary in the `claude-home` named volume (under `/home/coder/.local/bin`). +Subsequent starts skip the install and launch immediately. + +## Switching projects + +Point `PROJECT_DIR` at any directory on your host: + +```bash +PROJECT_DIR=/path/to/myproject docker compose run --rm claude-code +``` + +Or set it in a `.env` file: + +``` +ANTHROPIC_API_KEY=sk-ant-... +PROJECT_DIR=/Users/me/projects/my-app +``` + +Then just: + +```bash +docker compose run --rm claude-code +``` + +## Starting Claude Code + +Once inside the container shell: + +```bash +claude # start an interactive session in the current directory +claude --help # show available options +claude doctor # diagnose installation issues +``` + +## Volumes + +| Volume | Purpose | +|---|---| +| `claude-home` (named) | Persists Claude Code binary, config, and auth credentials | +| `$PROJECT_DIR` (bind) | Your project code — swap freely between sessions | + +To wipe the Claude install and start fresh: + +```bash +docker compose down -v # removes the claude-home volume +``` + +## Security notes + +- Runs as a non-root user (`coder`, uid 1000) +- All Linux capabilities are dropped (`cap_drop: ALL`) +- Privilege escalation is disabled (`no-new-privileges`) +- The container has no network restrictions beyond what Docker provides — + add a custom network or `--network none` with `--add-host` if you want + to lock that down further diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..3db9b5c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,37 @@ +services: + claude-code: + build: . + image: claude-code:local + container_name: claude-code + + # Pass your Anthropic API key in from the host environment, + # or drop it into a .env file alongside this compose file. + # environment: + # - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:?Set ANTHROPIC_API_KEY in your environment or .env file} + + volumes: + # Fixed home volume — persists Claude Code install, config, and credentials + # across container restarts and image rebuilds. + - claude-home:/home/coder + + # Swappable project folder — override PROJECT_DIR to point at any directory: + # PROJECT_DIR=/path/to/myproject docker compose run --rm claude-code + - ${PROJECT_DIR:-./code}:/code + + # Drop all Linux capabilities and disable privilege escalation — + # Claude Code doesn't need any of them. + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + + # Interactive terminal so `claude` works properly + stdin_open: true + tty: true + + working_dir: /code + +volumes: + claude-home: + # Named volume — Docker manages it; survives `docker compose down` + # (use `docker compose down -v` to wipe it along with the install) diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..24de07e --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,15 @@ +#!/bin/bash +set -e + +# Install Claude Code if not already present in the home volume. +# Because the home directory is a volume, this install persists across +# container restarts and rebuilds. +if ! command -v claude &>/dev/null; then + echo "Claude Code not found — running installer..." + curl -fsSL https://claude.ai/install.sh | bash + echo "Claude Code installed successfully." +else + echo "Claude Code $(claude --version 2>/dev/null || echo '(version unknown)') ready." +fi + +exec "$@" diff --git a/safeclaude b/safeclaude new file mode 100755 index 0000000..5b28ef3 --- /dev/null +++ b/safeclaude @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ $# -lt 1 ]]; then + echo "Usage: $(basename "$0") " >&2 + exit 1 +fi + +PROJECT_DIR="$(cd "$1" && pwd)" # resolve to absolute path + +SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)" + +PROJECT_DIR="$PROJECT_DIR" docker compose -f "$SCRIPT_DIR/docker-compose.yml" run -w /code --rm claude-code claude