claudebox: Claude Code in Docker, Jetzt Mit Sieben Wegen Deinen Produktionsserver Zu Zerlegen

Achtung, dieser Beitrag ist überholt. Seit v2.0.0 ist claudebox ein dünnes Kind-Image von aicodebox, der agentenagnostischen Basis, der inzwischen jede unten beschriebene Oberfläche gehört. Die API, der OpenAI-kompatible Endpoint, der MCP-Server, der Telegram-Bot und der Cron-Scheduler leben alle in der Basis und werden mit pibox und codexbox geteilt. claudebox selbst ist jetzt der Claude-Code-Adapter plus ein Wrapper auf der Host-Seite. Lies zuerst den aicodebox-Beitrag, dort steckt die Architektur wirklich. Was unten steht, funktioniert weiterhin und dokumentiert claudeboxs eigene Oberfläche, aber die eigentliche Geschichte ist jetzt die Basis.

MCP ist ein eigener Modus, kein Winkel der API

Das gehört klar gesagt, denn die Doku hat es lange als Endpoint innerhalb des API-Modus beschrieben, und das verkauft es unter Wert. MCP läuft auf zwei Arten:

Inside API mode   mounted at /mcp on the API server, no extra process   port 8080
Standalone        its own uvicorn process, spawned as a sidecar        port 8081

Die eigenständige Variante koexistiert mit jedem anderen Modus, statt sie zu ersetzen, und sie nimmt sich ihr eigenes Token, CLAUDEBOX_MCP_MODE_TOKEN, das nicht auf das API-Token zurückfällt. Lass es leer und die MCP-Oberfläche hat überhaupt keine Auth, was ein anderer und deutlich schlechterer Default ist als „erbt, was die API benutzt“. Setz es bewusst.
Fünf Tools: run_prompt plus vier Datei-Tools, list_files, read_file, write_file, delete_file, von denen jedes seinen path unterhalb der Workspace-Wurzel auflöst und alles zurückweist, was herausklettert. Nimm lieber die, als eine Nutzlast in prompt zu stopfen: der Agent kann den Workspace selbst lesen, also schlägt „schreib die Eingabe in eine Datei und sag ihm, welche“ einen 50-KB-Prompt.


Ich benutze Claude Code für alles. Code schreiben, Scheiß debuggen, Infrastruktur ausrollen, Repos verwalten, Artikel für genau dieses Blog schreiben und sogar spontan Browser-Sessions automatisieren. Es ist zum Rückgrat meiner Arbeitsweise geworden. Aber einem KI-Agenten vollen Zugriff auf dein System zu geben ist scheiße beängstigend, nicht weil Claude böswillig wäre, sondern weil er mit --permission-mode bypassPermissions läuft und die Macht hat, zu tun, was er will. Ein falscher Befehl und dein Host ist hinüber. Heutzutage ist der Container, in dem er läuft, nicht mal mehr claudebox-spezifisch, es ist aicodebox mit einem Claude-förmigen Adapter drangeschraubt.
Die offensichtliche Antwort lautet: steck ihn in einen Container. Aber das richtig hinzubekommen ist ein ganz anderes Problem. Ich habe claudebox gebaut, um es zu lösen. Was als schlichter containerisierter Claude-Code-Wrapper begann, ist zu sieben verschiedenen Arten gewachsen, Claude laufen zu lassen, jede davon tatsächlich nützlich, keine davon Füllmaterial.

Die Umbenennung

Das hieß früher docker-claude-code, Image psyb0t/claude-code, Binary claude. Jetzt heißt es claudebox, Image psyb0t/claudebox, Binary claudebox. Die SSH-Schlüssel sind von ~/.ssh/claude-code nach ~/.ssh/claudebox umgezogen.
Falls du aktualisierst: altes Binary deinstallieren, neues Image ziehen, Installationsskript erneut ausführen. Dein Config-Verzeichnis ~/.claude und der Sitzungsverlauf überstehen die Umbenennung unangetastet.

v2.0.0, auf aicodebox rebased

Die größere Änderung kam nach der Umbenennung. claudebox ist jetzt ein dünnes Kind-Image von psyb0t/aicodebox, einer gemeinsamen, agentenagnostischen Basis, die jede Modus-Oberfläche übernimmt. Dasselbe Muster wie psyb0t/pibox und psyb0t/codexbox. Der API-Server, der Telegram-Bot, der Cron-Scheduler und der MCP-Endpoint leben jetzt alle in der Basis; claudebox steuert einen Adapter bei, der weiß, wie man speziell mit Claude Code spricht. Korrekturen in der Basis erreichen jedes Kind-Image kostenlos.
Das ist ein vollständiger architektonischer Rebase, also hat er Dinge zerlegt. Alles ist per Alias oder Symlink nach vorne geführt, damit bestehende Konfigurationen weiterlaufen, aber die kanonischen Namen haben sich geändert:

  • Endpoints: aus POST /run/cancel?runId=… wurde DELETE /run/{run_id}. Aus GET /health wurde GET /healthz.
  • MCP-Tool: aus claude_run wurde run_prompt. Aktualisiere deine MCP-Client-Konfigurationen.
  • Umgebungsvariablen: aus CLAUDEBOX_MODE_API wurde CLAUDEBOX_API_MODE, aus CLAUDEBOX_MODE_CRON_FILE wurde CLAUDEBOX_CRON_MODE_FILE, und so weiter die Liste hinunter. Der Entrypoint führt die alten Namen per Alias nach vorne.
  • Pfade: aus der Workspace-Wurzel /workspaces wurde /workspace (Singular). Aus dem Container-Home /home/claude/.claude wurde /home/aicode/.aicodebox. Kompatibilitäts-Symlinks halten alte Bind-Mounts auflösbar.
  • Cron: nur noch croniter mit sechs Feldern. Setz jedem Fünf-Feld-Zeitplan, den du benutzt hast, ein 0 voran.
  • Weg: der Telegram-Befehl /bash. Bau ihn clientseitig nach, wenn du ihn brauchst.
  • Full-Variante: make build-full legt sich jetzt auf das minimale Image, statt ein separates Multi-Stage-Target zu sein.

Der Adapter kommt mit 32 pytest-Unittests, plus 9 containerisierten Smoke-Tests, die gegen ein Claude-Dummy-Binary laufen, healthz, die OpenAI-Modellliste, die init.d-Marker, die Kompatibilitäts-Symlinks, das Env-Aliasing, die Always-Skills-Injektion, die Extra-Argumente und den Standard-Permission-Modus.

Sieben Schnittstellen, Ein Container

claudebox ist nicht mehr nur ein Wrapper. Es sind sieben verschiedene Schnittstellen zu Claude Code, laufend innerhalb von Docker:

  • Interaktive CLI: persistenter Container, Sitzungsfortsetzung, der ursprüngliche Modus
  • Programmatische CLI: nicht interaktiv, funktioniert aus Skripten und CI, mit eigenem dediziertem Container
  • HTTP-API-Server: REST-API mit Workspace-Verwaltung, Dateioperationen, synchronen und asynchronen Runs
  • OpenAI-kompatibler Endpoint: direkter Ersatz unter /openai/v1/chat/completions mit Streaming
  • MCP-Server: fünf Tools, die Claude aus anderen Agenten über das Model Context Protocol nutzen kann
  • Telegram-Bot: Workspaces pro Chat, Dateifreigabe, Shell-Befehle vom Handy aus
  • Cron-Scheduler: in YAML definierte geplante Jobs mit Auflösung unterhalb einer Minute, Historie pro Job und optionalen Telegram-Benachrichtigungen

Installation

curl -fsSL https://raw.githubusercontent.com/psyb0t/docker-claudebox/master/install.sh | bash

Das erzeugt SSH-Schlüssel unter ~/.ssh/claudebox, zieht das Image (immer, den Installer erneut laufen zu lassen ist der unterstützte Weg zum Upgrade) und legt das claudebox-Binary unter /usr/local/bin/claudebox ab. Wenn du dem Installer Umgebungsvariablen mitgeben musst, exportiere sie zuerst in einer eigenen Zeile, denn VAR=x curl ... | bash durch eine Pipe reicht die Variable nicht an das Skript weiter:

export CLAUDEBOX_INSTALL_DIR=/usr/local/bin
curl -fsSL https://raw.githubusercontent.com/psyb0t/docker-claudebox/master/install.sh | bash

Dann:

claudebox

Der erste Lauf fragt nach Authentifizierung. Danach funktioniert es einfach. Der Wrapper übernimmt den kompletten Container-Lebenszyklus, er erzeugt einen neuen, wenn für das aktuelle Verzeichnis keiner existiert, und startet und hängt sich wieder an, wenn schon einer da ist.

Image-Varianten

Full: psyb0t/claudebox:latest-full
Ubuntu-Basis, vollgeladen mit allem, was ein Entwickler wirklich braucht: Go mit der vollen Toolchain (golangci-lint, gopls, delve), Python 3.14 über pyenv (flake8, black, mypy, pyright, vulture, pytest, poetry), Node.js 24 LTS mit dem üblichen Ökosystem, C/C++-Toolchain, Docker CE mit Compose, Terraform, kubectl, helm, GitHub CLI, Datenbankclients für SQLite/PostgreSQL/MySQL/Redis und ein Haufen Werkzeuge (jq, ripgrep, fd-find, bat, shellcheck, shfmt, httpie). Der Container erzeugt automatisch eine CLAUDE.md, die jedes verfügbare Werkzeug auflistet, damit Claude weiß, womit er arbeiten kann.
Minimal: psyb0t/claudebox:latest
Nur das Nötigste auf der aicodebox-Basis: Ubuntu 24.04, git/curl/wget/jq, Node.js 24 LTS mit npm, Python 3.14 mit uv, Docker CE. Kleineres Image, schnellerer Pull. Claude hat passwortloses sudo, installiert sich also im Flug, was er braucht. Beachte, dass sich die Benennung in v2 umgedreht hat: latest ist jetzt das minimale Image (vorher war es das volle), latest-full ist der Toolchain-Build, und das alte Opt-in CLAUDEBOX_MINIMAL=1 tut nichts mehr, weil minimal der Default ist. CLAUDEBOX_FULL=1 ist der Weg, in die andere Richtung zu optieren, und mit gesetzter Variable zu installieren backt diese Wahl in den Wrapper, damit sie hält. Nimm Init-Hooks, um dein Setup vorzubacken, damit du nicht bei jedem frischen Container auf Paketinstallationen wartest.
Claude Code selbst steckt in keinem der beiden Images mehr, und der Grund ist die Lizenz. Anthropics CLI ist proprietär ohne Weiterverteilungsrecht, ein Image mit eingebackenem Binary zu veröffentlichen hieße also, fremde Software auszuliefern. Stattdessen trägt das Image die festgenagelte Version in CLAUDEBOX_CLAUDE_VERSION, und der Entrypoint führt beim ersten Start eines frischen Containers npm install -g @anthropic-ai/claude-code@<version> aus. Nichts von Anthropic reist in den veröffentlichten Layern mit; jeder Container holt es sich selbst von npm. Der Preis ist, dass der erste Start eines neuen Containers Netzwerk und ein paar zusätzliche Sekunden braucht. Warme Neustarts überspringen das komplett. Du willst eine andere Version, setz CLAUDEBOX_CLAUDE_VERSION beim docker run.

Die Boxen können sich jetzt gegenseitig starten

Installier claudebox, codexbox und pibox in dasselbe Verzeichnis und jeder Wrapper mountet die anderen beiden schreibgeschützt unter /usr/local/bin/<name>. Das heißt, Claude kann aus seinem eigenen Container heraus codexbox oder pibox aufrufen und einen anderen Agenten ein Stück Arbeit erledigen lassen. Der naheliegende Einsatz ist eine zweite Meinung zu einem Diff, ohne die Sitzung zu verlassen.
Der Teil, der echtes Nachdenken gekostet hat, ist der Kontext. Ein verschachtelter Lauf muss wissen, wo die Dinge auf dem Host liegen, nicht innerhalb des Containers, der zufällig gerade aufruft, also gibt es dafür einen versionierten Block: AICODEBOX_LAUNCH_CONTEXT_VERSION=1 plus AICODEBOX_HOST_HOME, AICODEBOX_HOST_WORKSPACE, AICODEBOX_HOST_WRAPPER_DIR und pro Agent AICODEBOX_HOST_CLAUDE_HOME / CODEX_HOME / PI_HOME mit einem passenden *_WRAPPER für jeden. Versioniert, weil sich die Form ändern wird und ein verschachtelter Start das merken können sollte.
Verschachtelte Läufe sind außerdem bewusst wegwerfbar: sie überspringen die Auth-Dateischreibvorgänge, die ein Lauf auf oberster Ebene macht, ein Kind-Agent kann also nicht still die Zugangsdaten der Box überschreiben, die ihn aufgerufen hat.
Daneben reichen AICODEBOX_ENV_* und AICODEBOX_MOUNT_* Umgebung und Mounts an jede Box weiter, neben den bestehenden CLAUDEBOX_ENV_* und CLAUDEBOX_MOUNT_*, die pro Box gelten. Und AICODEBOX_MANAGED_INSTALL=1 ist eine nicht interaktive Installation für Provisioning-Skripte, die sich, wichtig, weigert, einen bereits vorhandenen SSH-Schlüssel zu überschreiben. CLAUDEBOX_INSTALL_DIR und CLAUDEBOX_BIN_NAME steuern, wo es landet und wie es heißt.
Ein Lieferketten-Detail, das Erwähnung verdient: der Installer zieht wrapper.sh jetzt vom passenden unveränderlichen Release-Tag statt von master, eine festgenagelte Version zu installieren liefert dir also tatsächlich den Wrapper dieser Version.

Interaktiver Modus

Führ claudebox aus einem beliebigen Verzeichnis aus und du bekommst eine lebendige Sitzung. Der Container bleibt zwischen Läufen bestehen, die Sitzung läuft dort weiter, wo du aufgehört hast. Jeder Workspace bekommt seinen eigenen Container, benannt nach dem Verzeichnispfad.
Hilfsbefehle:

claudebox --version        # show version
claudebox doctor           # health check
claudebox auth             # manage authentication
claudebox setup-token      # interactive OAuth token setup
claudebox stop             # stop the running container for this workspace
claudebox clear-session    # wipe session history, next run starts fresh
claudebox --update         # pull the latest image and reinstall

Sitzungskontinuität. claudebox startet Claude mit --continue, er nimmt also die letzte Unterhaltung aus dem aktuellen Verzeichnis wieder auf. Kill das Terminal, komm am nächsten Tag zurück, starte es erneut, und Claude macht genau dort weiter, wo er aufgehört hat. Keine Sitzung, kein Problem, er fängt frisch an.
Speicherlimit. Container sind standardmäßig auf 10g gedeckelt. Beim Start überschreiben mit CLAUDEBOX_MAX_MEM=16g claudebox (das alte CLAUDE_MAX_MEM funktioniert weiterhin).
UID/GID-Abgleich. Der Entrypoint erkennt den Eigentümer des Workspace und passt den Benutzer des Containers daran an. Im Container erzeugte Dateien haben auf dem Host die richtigen Besitzrechte. Kein chown -R-Gefrickel.

Programmatischer Modus

Übergib einen Prompt und claudebox läuft nicht interaktiv. Es benutzt einen dedizierten _prog-Container pro Workspace, getrennt vom interaktiven, ohne TTY, funktioniert aus Skripten, aus Cron, aus anderen Werkzeugen:

# basic run
claudebox "explain this codebase"
# pick a model
claudebox "explain this codebase" --model sonnet
claudebox "audit this" --model opus
# output formats
claudebox "list all TODOs" --output-format json
claudebox "list all TODOs" --output-format json-verbose | jq .
claudebox "list all TODOs" --output-format stream-json | jq .
# reasoning effort
claudebox "debug this complex issue" --effort high
claudebox "quick question" --effort low
# custom system prompt
claudebox "review this" --system-prompt "You are a security auditor"
claudebox "review this" --append-system-prompt "Focus on SQL injection"
# structured output
claudebox "extract author and title" --output-format json 
  --json-schema '{"type":"object","properties":{"author":{"type":"string"},"title":{"type":"string"}}}'
# session control
claudebox "start over" --no-continue
claudebox "keep going" --resume abc123-def456

Modell-Aliase: opus (Opus 4.6), sonnet (Sonnet 4.6), haiku (Haiku 4.5), opusplan (Opus fürs Planen plus Sonnet fürs Ausführen), sonnet[1m] (Sonnet mit 1M-Kontextfenster). Oder übergib einen vollen Modellnamen, um eine bestimmte Version festzunageln.
Ausgabeformate: text (Standard), json (ein einzelnes Ergebnisobjekt mit Kosten und Token-Aufschlüsselung), json-verbose (wie json, aber mit einem turns-Array, das jeden Tool-Aufruf, jedes Tool-Ergebnis und jede Assistenten-Nachricht zeigt, volle Sicht darauf, was Claude getan hat), stream-json (NDJSON, ein Event pro Zeile, System-Init, Assistenten-Antworten, Tool-Nutzung, Tool-Ergebnisse, Rate-Limit-Events, Endergebnis).

API-Modus

Setz CLAUDEBOX_API_MODE=1, um den Container als HTTP-API-Server zu betreiben. Häng ihn in einen docker-compose-Stack und andere Dienste können über HTTP mit Claude reden:

services:
  claudebox:
    image: psyb0t/claudebox:latest
    ports:
      - "8080:8080"
    environment:
      - CLAUDEBOX_API_MODE=1
      - CLAUDEBOX_API_MODE_TOKEN=your-secret-token
      - CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-xxx
    volumes:
      - ~/.claude:/home/aicode/.aicodebox
      - /your/projects:/workspace
      - /var/run/docker.sock:/var/run/docker.sock

Endpoints:

  • POST /run: schick einen Prompt, bekomm ein Ergebnis. Felder: prompt, workspace, model, system_prompt, append_system_prompt, json_schema, effort, no_continue, resume. Liefert 409, wenn der Workspace schon verarbeitet.
  • POST /run mit "async": true: liefert sofort eine runId. Frag GET /run/result?runId=X ab, bis es fertig ist. Abschicken und vergessen.
  • DELETE /run/{run_id}: killt einen laufenden Prozess (war vor v2 POST /run/cancel?runId=…)
  • GET /files/{path}: Verzeichnis auflisten oder Datei herunterladen
  • PUT /files/{path}: Datei hochladen (Elternverzeichnisse werden automatisch angelegt)
  • DELETE /files/{path}: Datei löschen
  • GET /healthz: Health-Check, ohne Auth (war vor v2 GET /health)
  • GET /status: zeigt, welche Workspaces gerade beschäftigt sind

Alle Pfade sind relativ zu /workspace. Auth per Bearer-Token im Authorization-Header, setz CLAUDEBOX_API_MODE_TOKEN, um sie zu aktivieren. Die Verfolgung beschäftigter Workspaces liefert 409 Conflict, damit du nicht versehentlich überlappende Läufe auf demselben Workspace aufstaust.

Strukturierte Ausgabe und die vollständige Aufzeichnung sind jetzt getrennte Regler

Zwei Dinge haben sich in der API geändert, zwei Dinge, die vorher ineinander verhakt waren. jsonSchema läuft jetzt über Claude Codes eigenes --json-schema-Flag, statt obendrauf geschraubt zu sein, und die Event-Aufbewahrung ist ihr eigener Regler: eventMode: "full" schaltet das Streaming von Teilnachrichten, Subagenten-Text und Hook-Events ein und gibt dir jede native stream-json-Aufzeichnung zurück, ohne Turns zusammenzufalten oder Tool-Ergebnisse abzuschneiden. Das ganze Transkript zu wollen verlangt nicht mehr, so zu tun, als wolltest du ein Schema, und nach einem Schema zu fragen zwingt dir nicht mehr den ganzen Feuerwehrschlauch auf. Du wählst beides einzeln.

OpenAI-Kompatibler Endpoint

POST /openai/v1/chat/completions, ein OpenAI-Adapter als direkter Ersatz, der Anfragen an Claude Code im Container weiterleitet. Funktioniert mit allem, was die OpenAI-API spricht: LiteLLM, Open WebUI, eigene Clients, was auch immer.

curl https://ciprian.51k.eu80/openai/v1/chat/completions 
  -H "Authorization: Bearer your-secret-token" 
  -H "Content-Type: application/json" 
  -d '{
    "model": "sonnet",
    "messages": [{"role": "user", "content": "explain this codebase"}],
    "stream": true
  }'

Streaming läuft über Server-Sent Events. Unterhaltungen über mehrere Turns funktionieren, du übergibst die volle Nachrichtenhistorie und Claude hält den Kontext. Multimodal funktioniert auch, du schickst Bildinhalt als base64 in der Nachricht und Claude kann ihn sehen.
Eigene Header zur Verhaltenssteuerung:

  • X-Claude-Workspace: in welchem Workspace gelaufen wird
  • X-Claude-Continue: ob die vorige Sitzung fortgesetzt wird
  • X-Claude-Append-System-Prompt: hängt zusätzliche Anweisungen an den System-Prompt

Für LiteLLM richtest du es auf http://your-host:8080/openai/v1 als eigenen OpenAI-Provider und es funktioniert ohne jede Sonderkonfiguration.
Härtungsrunde. Der OpenAI-Adapter hat eine echte Testsuite und ein Sicherheitsaudit bekommen: ein SSRF-Wächter weist Anfragen ab, die interne URLs durch Workspace-Felder schmuggeln wollen, finish_reason-Werte werden richtig gemappt, damit OpenAI-Clients stop/length/tool_calls sehen statt Müll, Unterhaltungen über mehrere Turns bleiben bei Nachfragen korrekt im selben Workspace, und nicht unterstützte Anfragefelder liefern jetzt 400, statt still verworfen zu werden. Abgesichert durch 24 Unittests und 3 Integrationstests, damit die Oberfläche beim Wachsen ehrlich bleibt.

MCP-Server

Aktivier den MCP-Server unter /mcp/, damit andere Agenten und Werkzeuge über das Model Context Protocol in deinen Claude-Container hineinrufen können. Fünf Tools werden bereitgestellt:

  • run_prompt: führt einen Prompt in einem Workspace aus und gibt das Ergebnis zurück (in v2.0.0 von claude_run umbenannt)
  • list_files: listet die Dateien in einem Workspace-Verzeichnis
  • read_file: liest eine Datei aus einem Workspace
  • write_file: schreibt eine Datei in einen Workspace
  • delete_file: löscht eine Datei aus einem Workspace

Das heißt, andere Claude-Instanzen, eigene Agenten oder jeder MCP-kompatible Client können deine claudebox-Instanz als Werkzeug benutzen und Arbeit an eine frische Claude-Sitzung mit vollem Dateizugriff delegieren.

Telegram-Modus

Setz CLAUDEBOX_TELEGRAM_MODE=1 und du bekommst einen Telegram-Bot, der mit Claude spricht. Jeder Chat bekommt seinen eigenen Workspace und eigene Einstellungen. Schick Text, Dateien, Fotos, Videos, Sprachnachrichten. Hol Dateien mit /fetch zurück.
Die Konfiguration liegt in einer YAML-Datei mit Modell, Effort, Workspace, System-Prompt und Budget pro Chat:

# ~/.claude/telegram.yml
allowed_chats:
  - 123456789
  - -987654321
default:
  model: sonnet
  effort: high
  continue: true
chats:
  123456789:
    workspace: my-project
    model: opus
    effort: max
    system_prompt: "You are a senior engineer"
    max_budget_usd: 5.00
  -987654321:
    workspace: team-stuff
    model: sonnet
    allowed_users:
      - 123456789
services:
  claudebox-telegram:
    image: psyb0t/claudebox:latest
    environment:
      - CLAUDEBOX_TELEGRAM_MODE=1
      - CLAUDEBOX_TELEGRAM_MODE_TOKEN=123456:ABC-DEF
      - CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-xxx
    volumes:
      - ~/.claude:/home/aicode/.aicodebox
      - ~/telegram-workspaces:/workspace
      - /var/run/docker.sock:/var/run/docker.sock

Bot-Befehle:

  • beliebiger Text → als Prompt an Claude geschickt
  • schick eine Datei/ein Foto/ein Video/eine Sprachnachricht → im Workspace gespeichert, die Bildunterschrift wird zum Prompt
  • /model [name]: zeigt das aktuelle Modell mit auswählbaren Buttons oder setzt es direkt: haiku, sonnet, opus, opusplan, reset
  • /effort [level]: zeigt oder wählt den Effort: low, medium, high, xhigh, max, reset
  • /system_prompt [text]: zeigt, setzt oder löscht die System-Prompt-Überschreibung für diesen Chat
  • /append_system_prompt [text]: dasselbe für den angehängten System-Prompt
  • /fetch <path>: schickt eine Workspace-Datei als Telegram-Anhang zurück
  • /cancel: killt den laufenden Claude-Prozess für diesen Chat
  • /status: zeigt, welche Chats gerade laufende Prozesse haben
  • /config: zeigt die aktuelle Konfiguration dieses Chats
  • /reload: lädt die YAML-Konfiguration heiß neu, ohne den Container neu zu starten

Claude kann Dateien zurückschieben, indem er [SEND_FILE: path] in seine Antwort setzt. Bilder kommen als Fotos an, Videos als Videos, alles andere als Dokumente. Lange Antworten werden automatisch auf mehrere Nachrichten aufgeteilt.
Markdown-Rendering. Der Bot übersetzt Claudes Markdown-Ausgabe vor dem Senden in Telegrams HTML-Dialekt, und Fett, Kursiv, Inline-Code, Codeblöcke, Zitate, Überschriften, Listen und Links werden alle nativ im Chat gerendert. Schluss mit rohen **Sternchen und Backticks, die deine Nachrichten verschmutzen. NUL-Bytes in Tool-Ausgaben werden auf einen Platzhalter aus dem Private-Use-Bereich gemappt, damit sie den Hin- und Rückweg zu Telegram überstehen, ohne die Nachricht abzuschneiden.

Cron-Modus

Setz CLAUDEBOX_CRON_MODE=1 und richte CLAUDEBOX_CRON_MODE_FILE auf eine YAML-Datei, um geplante Claude-Jobs laufen zu lassen. Seit v2.0.0 nur noch croniter mit sechs Feldern, */30 * * * * * feuert alle 30 Sekunden. Konfigurationen von vor v2 mit Fünf-Feld-Einträgen brauchen ein vorangestelltes 0 .

model: haiku                    # default model for all jobs
append_system_prompt: |
  The current date and time is {system_datetime}.
telegram_chat_id: -1001234567890  # optional: post results to this Telegram chat
jobs:
  - name: hourly_check
    schedule: "0 0 * * * *"
    instruction: |
      Look at the git log for the last hour. Summarize commits.
  - name: every_30_seconds
    schedule: "*/30 * * * * *"  # sub-minute
    model: sonnet
    instruction: Write the current UTC timestamp to ./status.txt.
  - name: nightly_cleanup
    schedule: "0 0 3 * * *"
    model: opus
    system_prompt: |
      You are a cleanup agent. Current time: {system_datetime}.
    instruction: |
      Find files older than 7 days under ./tmp and delete them.

In instruction, system_prompt und append_system_prompt verfügbare Template-Variablen: {system_datetime} (aktuelles UTC-Datum mit Uhrzeit) und {job_name} (das Namensfeld des Jobs). model, system_prompt und append_system_prompt pro Job überschreiben die Vorgaben aus der Wurzel.

services:
  claudebox-cron:
    image: psyb0t/claudebox:latest
    environment:
      - CLAUDEBOX_CRON_MODE=1
      - CLAUDEBOX_CRON_MODE_FILE=/home/aicode/.aicodebox/cron.yaml
      - CLAUDEBOX_WORKSPACE=/workspace
      - CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-xxx
    volumes:
      - ./cron.yaml:/home/aicode/.aicodebox/cron.yaml:ro
      - ./workspace:/workspace
      - ~/.claude:/home/aicode/.aicodebox
      - /var/run/docker.sock:/var/run/docker.sock

Der Scheduler läuft im Vordergrund, und docker logs zeigt jeden Tick. Läuft ein Job noch, wenn der nächste Tick feuert, wird dieser Tick übersprungen. Die Job-Historie strömt nach ~/.claude/cron/history/<workspace-slug>/<timestamp>-<job-name>/ als activity.jsonl, stderr.log und meta.json.
Setz telegram_chat_id (in der Wurzel oder pro Job) plus CLAUDEBOX_TELEGRAM_MODE_TOKEN, damit Claudes Ergebnis nach jedem Job auf Telegram landet. Der Telegram-Bot muss nicht laufen, der Cron benutzt das Token direkt.
Denkaufwand pro Job. Setz effort in der Wurzel als Vorgabe und überschreib pro Job, dieselbe Skala wie in der CLI (low, medium, high, xhigh, max). Billige Modelle für billige Jobs, maximaler Aufwand für das fiese nächtliche Audit.
Historie früherer Läufe, automatisch injiziert. Jeder Cron-Tick hängt jetzt einen Systemblock an, der Claude sagt, wo seine vorigen Läufe liegen, die Historien-Wurzel, das Historienverzeichnis des Workspace und das Historienverzeichnis pro Job (~/.claude/cron/history/<workspace-slug>/*-<job-name>/). Claude liest sie nicht gierig; er bekommt die Pfade und entscheidet, ob er Glob oder Read benutzt, wenn der Job tatsächlich nach Trendanalyse oder Regressionserkennung verlangt. Der allererste Lauf überspringt den Hinweis, weil es noch nichts gibt. Das schaltet „vergleich mit letzter Woche“, „ist diese Kennzahl regrediert“, „was hat sich seit dem gestrigen Lauf geändert“ frei, ohne dass du irgendetwas davon pro Job verdrahtest. Kombiniert mit telegram_chat_id bekommst du einen täglichen Digest-Agenten, der tatsächlich weiß, was er gestern gesagt hat.

Anpassung

Immer Aktive Skills

Leg SKILL.md-Dateien in ~/.claude/.always-skills/ ab und sie werden automatisch in jeden claudebox-Aufruf injiziert, interaktiv, programmatisch, API, Telegram, Cron, in alle. Dauerhafter Kontext, der Claude in jede Sitzung folgt, ohne die CLAUDE.md-Dateien einzelner Projekte anzufassen.

Init-Hooks

Skripte in ~/.claude/init.d/*.sh laufen einmalig beim ersten Erzeugen des Containers, als root, bevor auf den claude-Benutzer heruntergeschaltet wird. Bei späteren docker start laufen sie nicht erneut, nur bei frischen Containern. Nimm das für zusätzliche Paketinstallationen oder einmaliges Setup:

mkdir -p ~/.claude/init.d
cat > ~/.claude/init.d/setup.sh << 'EOF'
#!/bin/bash
apt-get update && apt-get install -y some-package
pip install some-library
EOF
chmod +x ~/.claude/init.d/setup.sh

Eigene Skripte

Leg ausführbare Dateien in ~/.claude/bin/ auf dem Host ab und sie liegen in jedem Container im PATH. Bleibt über alle Sitzungen und alle Workspaces hinweg bestehen.

Weiterreichen von Umgebungsvariablen

Nimm das Präfix CLAUDEBOX_ENV_, um beliebige Umgebungsvariablen in den Container zu geben, das Präfix wird abgeschnitten:

CLAUDEBOX_ENV_GITHUB_TOKEN=xxx CLAUDEBOX_ENV_MY_VAR=hello claudebox "do stuff"

Zusätzliche Volume-Mounts

Das Präfix CLAUDEBOX_MOUNT_, um weitere Verzeichnisse zu mounten:

# Mount at same path on both sides
CLAUDEBOX_MOUNT_DATA=/data claudebox "process the data"
# Explicit source:dest
CLAUDEBOX_MOUNT_STUFF=/host/path:/container/path claudebox "do stuff"
# Read-only
CLAUDEBOX_MOUNT_RO=/data:/data:ro claudebox "read the data"

Das Workspace-Modell

claudebox erzeugt zwei Container pro Workspace: claude-<path> für interaktive Sitzungen und claude-<path>_prog für programmatische Läufe. Sie teilen keinen Zustand und können gleichzeitig laufen. Die interaktive Sitzung blockiert deine Skripte nicht. Die Skripte unterbrechen deine Sitzung nicht.
Das Verzeichnis ~/.claude wird in jeden Container gemountet, und Konfiguration, API-Schlüssel, Always-Skills, Init-Hooks und eigene Skripte sind alle über Workspaces hinweg geteilt. SSH-Schlüssel aus ~/.ssh/claudebox werden automatisch eingehängt. Die Isolation liegt auf Workspace-Ebene, die Identität ist gemeinsam.
Der Docker-Socket wird ebenfalls durchgereicht, damit Claude Images bauen, Compose-Stacks hochziehen und Container aus seinem eigenen Container heraus verwalten kann. Weil der Workspace unter seinem echten Host-Pfad gemountet ist ($PWD:$PWD), lösen sich Volume-Mounts aus Claude heraus auf dem Host korrekt auf. Claude schreibt eine docker-compose.yml, führt sie aus, und die Pfade stimmen.

Es Liefert Seinen Eigenen Skill und Sein Plugin Mit

Das Repo trägt .agents/skills/claudebox/, einen Agenten-Skill, der jeden Modus dokumentiert, den die Box bereitstellt, sodass ein Assistent, den du darauf richtest, die interaktive Shell, das Einmal-Exec, die REST-API, den OpenAI-kompatiblen Endpoint, den MCP-Server, den Telegram-Bot und den Cron-Scheduler bereits kennt, ohne dass du irgendetwas erklärst.
Daneben @psyb0t/claudebox unter .agents/plugins/claudebox/, eine MCP-Brücke stdio↔HTTP über mcp-remote, damit ein OpenClaw- oder MCP-Agent den /mcp-Endpoint einer laufenden Box direkt fahren kann. MIT-lizenziert. Die CI veröffentlicht beide bei Tag-Pushes auf ClawHub.

Sicherheitsmodell

Das Ding läuft mit --permission-mode bypassPermissions, dem modernen Äquivalent des alten --dangerously-skip-permissions. Claude hat passwortloses sudo im Container und kann tun, was er will.
Die Sicherheitsgrenze ist der Container. Claude kommt an dein Host-Dateisystem nicht heran, jenseits des gemounteten Workspace und der ~/.claude-Konfiguration. Wenn er Amok läuft, docker stop und docker rm, und weg ist er. Einen frischen ziehst du in Sekunden hoch.
Der Docker-Socket-Mount ist die Ausnahme, denn er gibt Zugriff auf den Docker-Daemon des Hosts. Mount ihn nicht, wenn dich das beunruhigt. Alles andere bleibt eingeschlossen.
Die SSH-Schlüssel liegen in ~/.ssh/claudebox, ein dediziertes Schlüsselpaar, das bei der Installation erzeugt wird. Deine persönlichen Schlüssel betreten den Container nie. Welcher Schlüssel benutzt wird, stellst du bei Bedarf über CLAUDEBOX_SSH_DIR ein.
Lieferkette. Das Basis-Image ist per @sha256:-Digest festgenagelt, nicht bloß per Tag, denn Tags sind veränderlich, ein Digest dagegen inhaltsadressiert, ein Neubau kann also nicht still andere Basis-Bytes ziehen. Im vollen Image wird das Go-Tarball auf die Platte geladen und vor dem Entpacken mit sha256sum -c gegen eine Prüfsumme je Architektur verifiziert, statt der alten unverifizierten Pipe curl … | tar. Ein manipulierter oder abgeschnittener Download lässt den Build scheitern, statt im Image zu landen.
Die Auth im API-Modus läuft per Bearer-Token im Authorization-Header. Setz CLAUDEBOX_API_MODE_TOKEN. Setzt du es nicht, läuft die API unauthentifiziert, was für den lokalen Gebrauch in Ordnung und in einem Netzwerk exponiert eine schlechte Idee ist.

Unterm Strich

Ich fahre inzwischen jede Claude-Sitzung innerhalb von claudebox. Sieben Modi, null Host-Verschmutzung. Die Isolation bedeutet, dass ich nicht zweimal überlege, ob ich ihn Pakete installieren, Konfigurationen umschreiben oder beliebige Befehle ausführen lasse, um die Aufgabe zu erledigen. Die Sitzungskontinuität bedeutet, dass ich mein Terminal schließe, Stunden später wiederkomme und genau dort weitermache, wo ich war. Die API und der OpenAI-Endpoint bedeuten, dass ich Claude an andere Dienste hängen kann, ohne Klebecode zu schreiben. Der Telegram-Bot bedeutet, dass ich eine Aufgabe vom Handy aus anstoßen kann, während ich nicht am Schreibtisch bin. Der Cron-Scheduler bedeutet, dass Claude arbeitet, während ich schlafe.
Hol es dir hier: github.com/psyb0t/docker-claudebox
Lizenziert unter WTFPL, denn das Einzige, was gefährlicher ist als eine KI mit Root-Zugriff, ist eine KI mit Root-Zugriff und einer restriktiven Lizenz.

Wie Du Es In Deinen Agenten Installierst

Claude Code, der Claude Code in einer Box laufen lässt, und jetzt aus Claude Code heraus installierbar, was ungefähr so rekursiv ist, wie ich es haben will. Alles unter .agents/ ist in einem einzigen Marktplatz katalogisiert, es sind also zwei Befehle:

claude plugin marketplace add psyb0t/agents
claude plugin install claudebox@psyb0t

Codex benutzt denselben Marktplatz mit einem anderen Verb, codex plugin add claudebox@psyb0t, weil es kein codex plugin install gibt. Es findet den Skill in einem Checkout des Repos auch von allein, da es .agents/skills/ nativ scannt, ganz ohne dass irgendetwas installiert ist. Es ist inzwischen auch in der offiziellen MCP Registry gelistet, ein Client, der Server von dort auflöst, kann es also finden, ohne eine URL in die Hand gedrückt zu bekommen.