GitHub - ArmanJR/claudebox: Full Claude agent, completely contained, one command.

4 min read Original article ↗

Claude in a box

Gemini_Generated_Image_3bck2g3bck2g3bck

Why?

  • Use it however you want: Run Claude from your terminal as a CLI, or drop it into an existing Docker Compose stack as a service.
  • No API key, no extra billing: claudebox uses your existing Claude subscription and authenticates with your current Claude credentials, so personal use feels seamless.
  • Real agent, strong isolation: Claude gets its full toolset inside the container—file editing, shell access, code analysis, and more; without access to your host machine or the open internet beyond Anthropic’s APIs.

Prerequisites

  • DockerInstall Docker Desktop
  • Claude CLI — installed and authenticated (curl -fsSL https://claude.ai/install.sh | bash, then run claude once to log in)

claudebox uses your Claude subscription. It reads local credentials (Keychain on macOS, ~/.claude/.credentials.json on Linux) to authenticate inside the container. Credentials are resolved in order: CLAUDE_CODE_OAUTH_TOKEN env var, then platform credential store.

Use as a CLI tool

For: running prompts and agentic tasks in a sandboxed container from your terminal.

Install

curl -fsSL https://raw.githubusercontent.com/ArmanJR/claudebox/main/install.sh | bash

Usage

claudebox prompt "explain how DNS works"    # run a single prompt
claudebox prompt --json "explain DNS"       # full JSON output
claudebox prompt --verbose "explain DNS"    # container logs + output
claudebox server                            # start the HTTP API server
claudebox server --openai                   # start with OpenAI-compatible API
claudebox stop                              # stop the server
claudebox logs                              # view server logs
claudebox status                            # check if server is running
claudebox version                           # show CLI version
claudebox update                            # update the CLI

Works on macOS and Linux. Handles authentication automatically and refreshes expired tokens before launching the container.

Environment variables

Variable Default Purpose
CLAUDEBOX_PORT 3000 Host port
CLAUDEBOX_IMAGE ghcr.io/armanjr/claudebox:latest Docker image
CLAUDEBOX_NAME claudebox Container name
CLAUDE_CODE_OAUTH_TOKEN Skip auto-detection, use this token directly
CLAUDEBOX_API_KEY API key for /v1/* endpoints (used with --openai)

Use as a service

For: adding Claude as an agent alongside other services in your Docker Compose stack.

Add to your docker-compose.yml

First, extract your OAuth token (re-run when it expires):

curl -fsSL https://raw.githubusercontent.com/ArmanJR/claudebox/main/setup-auth.sh | bash
services:
  claudebox:
    image: ghcr.io/armanjr/claudebox:latest
    cap_add:
      - NET_ADMIN
    ports:
      - "3000:3000"
    env_file:
      - path: .env.claude
        required: true

Then docker compose up -d. Other services in the same network reach Claude at http://claudebox:3000.

HTTP API

POST /prompt

Send a prompt to Claude and get a JSON response.

{
  "prompt": "your prompt here",
  "options": {
    "model": "sonnet",
    "maxTurns": 10,
    "maxBudgetUsd": 1.0,
    "systemPrompt": "you are a helpful assistant",
    "appendSystemPrompt": "additional instructions",
    "allowedTools": ["Read", "Edit", "Bash"],
    "cwd": "/workspace"
  }
}

All fields in options are optional.

Response: Claude Code's JSON output (includes result, session_id, usage, total_cost_usd, etc.)

GET /health

Returns {"status": "ok", "activeRequests": 0}.

Server environment variables

Variable Default Purpose
PORT 3000 Server listen port
MAX_CONCURRENT 4 Max parallel Claude processes
OPENAI_COMPAT Set to 1 to enable /v1/* routes (set automatically by --openai)
CLAUDEBOX_API_KEY If set, all /v1/* requests require Authorization: Bearer <key>

OpenAI-compatible API

Start the server with --openai (CLI) or set OPENAI_COMPAT=1 (Docker) to expose /v1/chat/completions and /v1/models. This lets you use claudebox with any OpenAI-compatible client.

# CLI
claudebox server --openai

# Docker Compose — add to environment
OPENAI_COMPAT=1

POST /v1/chat/completions

Accepts the standard OpenAI chat completions request format and translates it to Claude Code invocations.

curl http://localhost:3000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "sonnet",
    "messages": [
      {"role": "system", "content": "You are a helpful assistant"},
      {"role": "user", "content": "Explain DNS in one sentence"}
    ]
  }'

Returns a standard OpenAI-shaped response:

{
  "id": "chatcmpl-...",
  "object": "chat.completion",
  "model": "sonnet",
  "choices": [{
    "index": 0,
    "message": {"role": "assistant", "content": "..."},
    "finish_reason": "stop"
  }],
  "usage": {"prompt_tokens": 100, "completion_tokens": 50, "total_tokens": 150}
}

Supported features:

Feature How it maps
messages[role=system] Concatenated into --system-prompt
Multi-turn conversation Serialized into structured prompt with history
model Passed through to Claude (sonnet, opus, haiku, or full model IDs)
response_format (json_schema / json_object) Injected into prompt + validated; retries once on failure
Base64 images (data:image/...;base64,...) Decoded to temp files, read by Claude's Read tool

Not supported: streaming, function calling / tools, external image URLs, temperature / top_p, n > 1.

GET /v1/models

Lists available Claude models.

Authentication

If CLAUDEBOX_API_KEY is set, all /v1/* requests must include Authorization: Bearer <key>. The existing /prompt and /health endpoints are unaffected.

How It Works

Network isolation — iptables firewall blocks all outbound traffic except Anthropic API domains (baked into the image). To allow additional domains, mount a custom allowlist:

-v /path/to/allowed-domains.txt:/etc/allowed-domains.txt:ro

Workspace/workspace is writable so Claude can create and edit files. Mount context files read-only at /workspace/context for reference (put your CLAUDE.md there).

Limitations

IP-based firewall — Domain allowlisting resolves IPs at container start. If Anthropic's CDN/load-balancer IPs rotate during the container's lifetime, connections will fail. Restart the container to re-resolve. This is an inherent limitation of iptables-based filtering.

Building Locally

docker compose up -d --build

To pin a specific Claude Code version:

docker compose build --build-arg CLAUDE_CODE_VERSION=2.1.80

License

MIT