Attention, ce post a été dépassé. Depuis la v2.0.0, claudebox est une image enfant fine d’aicodebox, la base agnostique à l’agent qui possède désormais toutes les surfaces décrites plus bas. L’API, l’endpoint compatible OpenAI, le serveur MCP, le bot Telegram et le scheduler cron vivent tous dans la base et sont partagés avec pibox et codexbox. claudebox lui-même n’est plus que l’adaptateur Claude Code plus un wrapper côté host. Va lire le post sur aicodebox d’abord, c’est là que vit réellement l’architecture. Ce qui suit marche toujours et documente la surface propre à claudebox, mais la vraie histoire, c’est la base maintenant.
MCP est son propre mode, pas un recoin de l’API
Ça mérite d’être dit clairement, parce que la doc l’a longtemps décrit comme un endpoint à l’intérieur du mode API, et ça le sous-vend. MCP tourne de deux façons:
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 8081La version autonome cohabite avec tous les autres modes au lieu de les remplacer, et elle prend son propre token, CLAUDEBOX_MCP_MODE_TOKEN, qui ne retombe pas sur le token de l’API. Laisse-le vide et la surface MCP n’a aucune auth du tout, ce qui est un défaut différent et bien pire que « hérite de ce qu’utilise l’API ». Pose-le délibérément.
Cinq outils: run_prompt plus quatre outils de fichiers, list_files, read_file, write_file, delete_file, chacun résolvant son path sous la racine du workspace et rejetant tout ce qui en sort. Préfère-les à bourrer une charge utile dans prompt: l’agent sait lire le workspace tout seul, donc « écris l’entrée dans un fichier et dis-lui lequel » bat un prompt de 50 Ko.
J’utilise Claude Code pour tout. Écrire du code, débugger de la merde, déployer de l’infra, gérer des repos, écrire des articles pour ce blog même, et jusqu’à automatiser des sessions de navigateur à la volée. C’est devenu la colonne vertébrale de ma façon de travailler. Mais donner à un agent IA un accès complet à ton système, c’est foutrement terrifiant, pas parce que Claude serait malveillant, mais parce qu’il tourne avec
--permission-mode bypassPermissions et qu’il a le pouvoir de faire ce qu’il veut. Une mauvaise commande et ton host est cuit. Ces temps-ci le container dans lequel il tourne n’est même plus spécifique à claudebox, c’est aicodebox avec un adaptateur en forme de Claude boulonné dessus.La réponse évidente, c’est: mets-le dans un container. Mais le faire correctement, c’est un tout autre problème. J’ai construit claudebox pour le régler. Ce qui a commencé comme un simple wrapper containerisé autour de Claude Code est devenu sept façons différentes de faire tourner Claude, chacune réellement utile, aucune de remplissage.
Le Renommage
Ça s’appelait avant docker-claude-code, image psyb0t/claude-code, binaire claude. C’est maintenant claudebox, image psyb0t/claudebox, binaire claudebox. Les clés SSH sont passées de ~/.ssh/claude-code à ~/.ssh/claudebox.
Si tu fais une mise à jour: désinstalle l’ancien binaire, tire la nouvelle image, relance le script d’installation. Ton dossier de config ~/.claude et ton historique de sessions survivent au renommage sans y toucher.
v2.0.0, rebasé sur aicodebox
Le plus gros changement est venu après le renommage. claudebox est maintenant une image enfant fine de psyb0t/aicodebox, une base commune, agnostique à l’agent, qui gère toutes les surfaces de modes. Même schéma que psyb0t/pibox et psyb0t/codexbox. Le serveur d’API, le bot Telegram, le scheduler cron et l’endpoint MCP vivent tous dans la base maintenant; claudebox apporte un adaptateur qui sait parler spécifiquement à Claude Code. Les corrections dans la base atteignent gratuitement toutes les images enfants.
C’est un rebase architectural complet, donc ça a cassé des choses. Tout est aliasé ou lié en symbolique vers l’avant pour que les configs existantes continuent de marcher, mais les noms canoniques ont changé:
- Endpoints:
POST /run/cancel?runId=…est devenuDELETE /run/{run_id}.GET /healthest devenuGET /healthz. - Outil MCP:
claude_runest devenurun_prompt. Mets à jour tes configs de client MCP. - Variables d’environnement:
CLAUDEBOX_MODE_APIest devenuCLAUDEBOX_API_MODE,CLAUDEBOX_MODE_CRON_FILEest devenuCLAUDEBOX_CRON_MODE_FILE, et ainsi de suite sur toute la liste. L’entrypoint aliase les anciens noms vers l’avant. - Chemins: la racine de workspace
/workspacesest devenue/workspace(au singulier). Le home du container/home/claude/.claudeest devenu/home/aicode/.aicodebox. Des liens symboliques de compatibilité gardent les anciens bind mounts résolvables. - Cron: croniter à six champs uniquement. Préfixe d’un
0toute planification à cinq champs que tu utilisais. - Disparu: la commande Telegram
/bash. Réimplémente-la côté client si tu en as besoin. - Variante full:
make build-fullse pose maintenant par-dessus l’image minimale au lieu d’être une cible multi-stage séparée.
L’adaptateur est livré avec 32 tests unitaires pytest, plus 9 tests de fumée containerisés qui tournent contre un binaire claude bidon, healthz, la liste de modèles OpenAI, les marqueurs init.d, les liens de compatibilité, l’aliasing d’environnement, l’injection des always-skills, les arguments supplémentaires et le mode de permissions par défaut.
Sept Interfaces, Un Seul Container
claudebox n’est plus juste un wrapper. Ce sont sept interfaces différentes vers Claude Code tournant à l’intérieur de Docker:
- CLI interactif: container persistant, reprise de session, le mode d’origine
- CLI programmatique: non interactif, marche depuis des scripts et la CI, avec son propre container dédié
- Serveur HTTP API: API REST avec gestion de workspaces, opérations sur fichiers, runs synchrones et asynchrones
- Endpoint compatible OpenAI: remplacement direct sur
/openai/v1/chat/completionsavec streaming - Serveur MCP: cinq outils que Claude peut utiliser depuis d’autres agents via le Model Context Protocol
- Bot Telegram: workspaces par chat, partage de fichiers, commandes shell depuis ton téléphone
- Scheduler cron: jobs planifiés définis en YAML avec résolution sous la minute, historique par job et notifications Telegram optionnelles
Installation
curl -fsSL https://raw.githubusercontent.com/psyb0t/docker-claudebox/master/install.sh | bashÇa génère des clés SSH dans ~/.ssh/claudebox, tire l’image (toujours, relancer l’installeur est la façon supportée de mettre à jour) et pose le binaire claudebox dans /usr/local/bin/claudebox. S’il te faut passer des variables d’environnement à l’installeur, exporte-les d’abord sur une ligne séparée, parce que piper VAR=x curl ... | bash ne transmet pas la variable au script:
export CLAUDEBOX_INSTALL_DIR=/usr/local/bin
curl -fsSL https://raw.githubusercontent.com/psyb0t/docker-claudebox/master/install.sh | bashEnsuite:
claudeboxLe premier lancement demande une authentification. Après ça, ça marche tout seul. Le wrapper gère tout le cycle de vie du container, il en crée un nouveau s’il n’en existe pas pour le dossier courant, le redémarre et s’y rattache s’il en existe déjà un.
Variantes d’Image
Full: psyb0t/claudebox:latest-full
Base Ubuntu chargée avec tout ce dont un développeur a réellement besoin: Go avec la chaîne d’outils complète (golangci-lint, gopls, delve), Python 3.14 via pyenv (flake8, black, mypy, pyright, vulture, pytest, poetry), Node.js 24 LTS avec l’écosystème habituel, chaîne d’outils C/C++, Docker CE avec Compose, Terraform, kubectl, helm, GitHub CLI, clients de bases de données pour SQLite/PostgreSQL/MySQL/Redis, et un tas d’utilitaires (jq, ripgrep, fd-find, bat, shellcheck, shfmt, httpie). Le container génère automatiquement un CLAUDE.md listant chaque outil disponible pour que Claude sache avec quoi il travaille.
Minimale: psyb0t/claudebox:latest
Juste l’essentiel par-dessus la base aicodebox: Ubuntu 24.04, git/curl/wget/jq, Node.js 24 LTS avec npm, Python 3.14 avec uv, Docker CE. Image plus petite, pull plus rapide. Claude a sudo sans mot de passe, donc il installe à la volée ce dont il a besoin. Note que le nommage s’est inversé en v2: latest est maintenant l’image minimale (c’était la full avant), latest-full est le build avec la chaîne d’outils, et l’ancien opt-in CLAUDEBOX_MINIMAL=1 ne fait plus rien puisque la minimale est le défaut. CLAUDEBOX_FULL=1 est la façon d’opter dans l’autre sens, et installer avec cette variable posée cuit ce choix dans le wrapper pour qu’il tienne. Utilise les init hooks pour pré-cuire ton setup et ne pas attendre des installs de paquets à chaque container neuf.
Claude Code lui-même n’est plus dans aucune des deux images, et la raison est la licence. Le CLI d’Anthropic est propriétaire sans autorisation de redistribution, donc publier une image avec lui cuit dedans reviendrait à livrer le logiciel de quelqu’un d’autre. À la place l’image porte la version épinglée dans CLAUDEBOX_CLAUDE_VERSION et l’entrypoint lance npm install -g @anthropic-ai/claude-code@<version> la première fois qu’un container neuf démarre. Rien d’Anthropic ne voyage dans les couches publiées; chaque container va le chercher lui-même sur npm. Le coût, c’est que le premier démarrage d’un nouveau container a besoin du réseau et de quelques secondes de plus. Les redémarrages à chaud le sautent entièrement. Tu veux une autre version, pose CLAUDEBOX_CLAUDE_VERSION au docker run.
Les boîtes peuvent se lancer les unes les autres maintenant
Installe claudebox, codexbox et pibox dans le même dossier et chaque wrapper monte les deux autres en lecture seule sur /usr/local/bin/<name>. Ce qui veut dire que Claude, depuis son propre container, peut lancer codexbox ou pibox et faire faire un bout de travail par un autre agent. L’usage évident, c’est un deuxième avis sur un diff sans quitter la session.
La partie qui a demandé de la vraie réflexion, c’est le contexte. Un run imbriqué doit savoir où sont les choses sur le host, pas à l’intérieur du container qui se trouve appeler, d’où un bloc versionné pour ça: AICODEBOX_LAUNCH_CONTEXT_VERSION=1 plus AICODEBOX_HOST_HOME, AICODEBOX_HOST_WORKSPACE, AICODEBOX_HOST_WRAPPER_DIR, et par agent AICODEBOX_HOST_CLAUDE_HOME / CODEX_HOME / PI_HOME avec un *_WRAPPER correspondant pour chacun. Versionné parce que la forme va changer et qu’un lancement imbriqué devrait pouvoir s’en rendre compte.
Les runs imbriqués sont aussi délibérément jetables: ils sautent les écritures de fichiers d’auth que fait un run de premier niveau, donc un agent enfant ne peut pas discrètement réécrire les identifiants de la boîte qui l’a appelé.
À côté de ça, AICODEBOX_ENV_* et AICODEBOX_MOUNT_* transmettent l’environnement et les montages à toutes les boîtes, en plus des CLAUDEBOX_ENV_* et CLAUDEBOX_MOUNT_* existants qui sont par boîte. Et AICODEBOX_MANAGED_INSTALL=1 est une installation non interactive pour les scripts de provisioning qui, point important, refuse d’écraser une clé SSH déjà existante. CLAUDEBOX_INSTALL_DIR et CLAUDEBOX_BIN_NAME contrôlent où ça atterrit et comment ça s’appelle.
Un détail de chaîne d’approvisionnement qui vaut le coup d’être noté: l’installeur tire désormais wrapper.sh depuis le tag de release immuable correspondant plutôt que depuis master, donc installer une version épinglée te donne réellement le wrapper de cette version.
Mode Interactif
Lance claudebox depuis n’importe quel dossier et tu obtiens une session vivante. Le container persiste entre les lancements, la session continue là où tu l’avais laissée. Chaque workspace a son propre container, nommé d’après le chemin du dossier.
Commandes utilitaires:
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 reinstallContinuité de session. claudebox lance Claude avec --continue, donc il reprend la dernière conversation du dossier courant. Tue le terminal, reviens le lendemain, relance-le, et Claude reprend exactement où il s’était arrêté. Pas de session, pas de problème, il repart de zéro.
Limite mémoire. Les containers plafonnent à 10g par défaut. Tu surcharges au lancement avec CLAUDEBOX_MAX_MEM=16g claudebox (l’ancien CLAUDE_MAX_MEM marche encore).
Correspondance UID/GID. L’entrypoint détecte le propriétaire du workspace et ajuste l’utilisateur du container pour correspondre. Les fichiers créés dans le container ont le bon propriétaire sur le host. Pas de conneries de chown -R.
Mode Programmatique
Passe un prompt et claudebox tourne en non interactif. Il utilise un container _prog dédié par workspace, séparé de l’interactif, sans TTY, qui marche depuis des scripts, du cron, d’autres outils:
# 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-def456Alias de modèles: opus (Opus 4.6), sonnet (Sonnet 4.6), haiku (Haiku 4.5), opusplan (Opus pour la planification plus Sonnet pour l’exécution), sonnet[1m] (Sonnet avec une fenêtre de contexte de 1M). Ou passe un nom de modèle complet pour épingler une version précise.
Formats de sortie: text (par défaut), json (un seul objet résultat avec le coût et le détail des tokens), json-verbose (comme json mais avec un tableau turns montrant chaque appel d’outil, chaque résultat d’outil et chaque message de l’assistant, visibilité complète sur ce que Claude a fait), stream-json (NDJSON, un événement par ligne, init système, réponses de l’assistant, usage d’outils, résultats d’outils, événements de rate limit, résultat final).
Mode API
Pose CLAUDEBOX_API_MODE=1 pour faire tourner le container en serveur HTTP API. Branche-le dans une stack docker-compose et d’autres services peuvent parler à Claude en HTTP:
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.sockEndpoints:
- POST /run: envoie un prompt, reçois un résultat. Champs:
prompt,workspace,model,system_prompt,append_system_prompt,json_schema,effort,no_continue,resume. Renvoie 409 si le workspace est déjà en train de traiter. - POST /run avec
"async": true: renvoie immédiatement unrunId. Interroge GET /run/result?runId=X jusqu’à ce que ça finisse. Tu lances et tu oublies. - DELETE /run/{run_id}: tue un processus en cours (c’était
POST /run/cancel?runId=…avant la v2) - GET /files/{path}: liste un dossier ou télécharge un fichier
- PUT /files/{path}: envoie un fichier (les dossiers parents sont créés automatiquement)
- DELETE /files/{path}: supprime un fichier
- GET /healthz: vérification de santé, sans auth (c’était
GET /healthavant la v2) - GET /status: montre quels workspaces sont occupés en ce moment
Tous les chemins sont relatifs à /workspace. Auth par bearer token dans l’en-tête Authorization, pose CLAUDEBOX_API_MODE_TOKEN pour l’activer. Le suivi des workspaces occupés renvoie 409 Conflict pour que tu n’empiles pas par accident des runs qui se chevauchent sur le même workspace.
La sortie structurée et l’enregistrement complet sont des boutons séparés maintenant
Deux choses ont changé dans l’API, deux choses qui étaient emmêlées l’une dans l’autre. jsonSchema passe maintenant par le flag --json-schema propre à Claude Code plutôt que d’être greffé par-dessus, et la rétention d’événements est son propre contrôle: eventMode: "full" active le streaming des messages partiels, du texte des sous-agents et des événements de hook, et te rend chaque enregistrement stream-json natif sans fusionner les tours ni tronquer les résultats d’outils. Vouloir la transcription entière n’exige plus de faire semblant de vouloir un schéma, et demander un schéma ne t’impose plus la lance à incendie complète. Tu choisis chacun séparément.
Endpoint Compatible OpenAI
POST /openai/v1/chat/completions, un adaptateur OpenAI en remplacement direct qui route les requêtes vers Claude Code dans le container. Marche avec tout ce qui parle l’API OpenAI: LiteLLM, Open WebUI, des clients maison, n’importe quoi.
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
}'Le streaming marche via les Server-Sent Events. Les conversations multi-tours marchent, tu passes tout l’historique de messages et Claude garde le contexte. Le multimodal marche aussi, tu envoies du contenu d’image en base64 dans le message et Claude peut le voir.
En-têtes personnalisés pour contrôler le comportement:
X-Claude-Workspace: dans quel workspace tournerX-Claude-Continue: s’il faut continuer la session précédenteX-Claude-Append-System-Prompt: ajoute des instructions supplémentaires au prompt système
Pour LiteLLM, pointe-le sur http://your-host:8080/openai/v1 comme provider OpenAI personnalisé et ça marche sans configuration particulière.
Passe de durcissement. L’adaptateur OpenAI a reçu une vraie suite de tests et un audit de sécurité: un garde-fou anti-SSRF rejette les requêtes qui tentent de faire passer des URL internes par les champs de workspace, les valeurs de finish_reason sont correctement mappées pour que les clients OpenAI voient stop/length/tool_calls au lieu de n’importe quoi, les conversations multi-tours restent bien sur le même workspace au fil des relances, et les champs de requête non supportés renvoient maintenant 400 au lieu d’être silencieusement jetés. Adossé à 24 tests unitaires et 3 tests d’intégration pour que la surface reste honnête en grandissant.
Serveur MCP
Active le serveur MCP sur /mcp/ pour laisser d’autres agents et outils appeler dans ton container Claude via le Model Context Protocol. Cinq outils exposés:
- run_prompt: lance un prompt dans un workspace et récupère le résultat (renommé depuis
claude_runen v2.0.0) - list_files: liste les fichiers d’un dossier de workspace
- read_file: lit un fichier depuis un workspace
- write_file: écrit un fichier dans un workspace
- delete_file: supprime un fichier d’un workspace
Ça veut dire que d’autres instances de Claude, des agents maison ou n’importe quel client compatible MCP peuvent utiliser ton instance claudebox comme un outil, en déléguant du travail à une session Claude neuve avec un accès complet aux fichiers.
Mode Telegram
Pose CLAUDEBOX_TELEGRAM_MODE=1 et tu obtiens un bot Telegram qui parle à Claude. Chaque chat a son propre workspace et ses propres réglages. Envoie du texte, des fichiers, des photos, des vidéos, des messages vocaux. Récupère des fichiers avec /fetch.
La configuration vit dans un fichier YAML avec modèle, effort, workspace, prompt système et budget par 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:
- 123456789services:
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.sockCommandes du bot:
- n’importe quel texte → envoyé à Claude comme prompt
- envoie un fichier/photo/vidéo/vocal → enregistré dans le workspace, la légende devient le prompt
/model [name]: affiche le modèle courant avec des boutons sélectionnables, ou le pose directement:haiku,sonnet,opus,opusplan,reset/effort [level]: affiche ou sélectionne l’effort:low,medium,high,xhigh,max,reset/system_prompt [text]: affiche, pose ou réinitialise la surcharge de prompt système pour ce chat/append_system_prompt [text]: pareil pour le prompt système ajouté/fetch <path>: renvoie un fichier du workspace en pièce jointe Telegram/cancel: tue le processus Claude en cours pour ce chat/status: montre quels chats ont des processus en cours/config: affiche la configuration courante de ce chat/reload: recharge à chaud la config YAML sans redémarrer le container
Claude peut repousser des fichiers en mettant [SEND_FILE: path] dans sa réponse. Les images arrivent en photos, les vidéos en vidéos, tout le reste en documents. Les réponses longues sont automatiquement découpées sur plusieurs messages.
Rendu markdown. Le bot traduit la sortie markdown de Claude vers la variante HTML de Telegram avant l’envoi, et le gras, l’italique, le code en ligne, les blocs de code, les citations, les titres, les listes et les liens se rendent tous nativement dans le chat. Fini les **astérisques bruts et les accents graves qui polluent tes messages. Les octets NUL dans la sortie des outils sont mappés vers un caractère de substitution en zone d’usage privé pour qu’ils survivent à l’aller-retour vers Telegram sans tronquer le message.
Mode Cron
Pose CLAUDEBOX_CRON_MODE=1 et pointe CLAUDEBOX_CRON_MODE_FILE sur un fichier YAML pour lancer des jobs Claude planifiés. Croniter à six champs uniquement depuis la v2.0.0, */30 * * * * * se déclenche toutes les 30 secondes. Les configs d’avant la v2 écrites avec des entrées à cinq champs ont besoin d’un 0 devant.
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.Variables de template disponibles dans instruction, system_prompt et append_system_prompt: {system_datetime} (date et heure UTC courantes) et {job_name} (le champ nom du job). Les model, system_prompt et append_system_prompt par job surchargent les défauts de la racine.
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.sockLe scheduler tourne au premier plan, et docker logs montre chaque tic. Si un job tourne encore quand le tic suivant se déclenche, ce tic est sauté. L’historique des jobs s’écrit en flux vers ~/.claude/cron/history/<workspace-slug>/<timestamp>-<job-name>/ sous forme d’activity.jsonl, stderr.log et meta.json.
Pose telegram_chat_id (à la racine ou par job) plus CLAUDEBOX_TELEGRAM_MODE_TOKEN pour recevoir le résultat de Claude sur Telegram à la fin de chaque job. Le bot Telegram n’a pas besoin de tourner, le cron utilise le token directement.
Effort de raisonnement par job. Pose effort à la racine pour un défaut et surcharge par job, même échelle que le CLI (low, medium, high, xhigh, max). Des modèles pas chers pour les jobs pas chers, effort max pour l’audit nocturne bien gratiné.
Historique des runs précédents, injecté automatiquement. Chaque tic de cron ajoute maintenant un bloc système disant à Claude où vivent ses runs précédents, la racine d’historique, le dossier d’historique du workspace et le dossier d’historique par job (~/.claude/cron/history/<workspace-slug>/*-<job-name>/). Claude ne les lit pas avidement; il reçoit les chemins et décide s’il fait un Glob ou un Read quand le job appelle réellement une analyse de tendance ou une détection de régression. Le tout premier run saute l’indice parce qu’il n’y a encore rien. Ça débloque « compare à la semaine dernière », « est-ce que cette métrique a régressé », « qu’est-ce qui a changé depuis le run d’hier », sans câbler quoi que ce soit de tout ça par job. Combiné à telegram_chat_id, tu obtiens un agent de digest quotidien qui sait vraiment ce qu’il a dit hier.
Personnalisation
Skills Toujours Actifs
Dépose des fichiers SKILL.md dans ~/.claude/.always-skills/ et ils sont auto-injectés dans chaque invocation de claudebox, interactive, programmatique, API, Telegram, cron, toutes. Du contexte persistant qui suit Claude dans chaque session sans toucher aux fichiers CLAUDE.md des projets individuels.
Hooks d’Init
Les scripts dans ~/.claude/init.d/*.sh tournent une seule fois à la première création du container, en root, avant de redescendre à l’utilisateur claude. Ils ne retournent pas aux docker start suivants, seulement sur des containers neufs. Sers-t’en pour des installs de paquets supplémentaires ou du setup à faire une fois:
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.shScripts Personnels
Dépose des exécutables dans ~/.claude/bin/ sur le host et ils sont dans le PATH à l'intérieur de chaque container. Ça persiste sur toutes les sessions, tous les workspaces.
Transmission des Variables d'Environnement
Utilise le préfixe CLAUDEBOX_ENV_ pour passer des variables d'environnement arbitraires dans le container, le préfixe est retiré:
CLAUDEBOX_ENV_GITHUB_TOKEN=xxx CLAUDEBOX_ENV_MY_VAR=hello claudebox "do stuff"Montages de Volumes Supplémentaires
Le préfixe CLAUDEBOX_MOUNT_ pour monter des dossiers supplémentaires:
# 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"Le Modèle de Workspace
claudebox crée deux containers par workspace: claude-<path> pour les sessions interactives et claude-<path>_prog pour les runs programmatiques. Ils ne partagent pas d'état et peuvent tourner en même temps. La session interactive ne bloque pas tes scripts. Les scripts n'interrompent pas ta session.
Le dossier ~/.claude se monte dans chaque container, et la configuration, les clés d'API, les always-skills, les hooks d'init et les scripts personnels sont tous partagés entre workspaces. Les clés SSH de ~/.ssh/claudebox se montent automatiquement. L'isolation est au niveau du workspace, l'identité est commune.
Le socket Docker est monté lui aussi pour que Claude puisse construire des images, monter des stacks compose, gérer des containers depuis l'intérieur de son propre container. Comme le workspace est monté sur son vrai chemin host ($PWD:$PWD), les montages de volumes depuis l'intérieur de Claude se résolvent correctement sur le host. Claude écrit un docker-compose.yml, le lance, et les chemins marchent.
Il Livre Son Propre Skill et Son Plugin
Le repo porte .agents/skills/claudebox/, un skill d'agent documentant chaque mode que la boîte expose, si bien qu'un assistant que tu pointes dessus connaît déjà le shell interactif, l'exec one-shot, l'API REST, l'endpoint compatible OpenAI, le serveur MCP, le bot Telegram et le scheduler cron sans que tu lui expliques quoi que ce soit.
À côté, @psyb0t/claudebox dans .agents/plugins/claudebox/, un pont MCP stdio↔HTTP par-dessus mcp-remote, pour qu'un agent OpenClaw ou MCP puisse piloter directement l'endpoint /mcp d'une boîte qui tourne. Sous licence MIT. La CI publie les deux sur ClawHub aux pushs de tags.
Modèle de Sécurité
Ce truc tourne avec --permission-mode bypassPermissions, l'équivalent moderne de l'ancien --dangerously-skip-permissions. Claude a sudo sans mot de passe dans le container et peut faire ce qu'il veut.
La frontière de sécurité, c'est le container. Claude ne peut pas toucher au système de fichiers de ton host au-delà du workspace monté et de la config ~/.claude. S'il part en vrille, docker stop et docker rm et c'est fini. Tu en montes un neuf en quelques secondes.
Le montage du socket Docker est l'exception, parce qu'il donne accès au démon Docker du host. Ne le monte pas si ça t'inquiète. Tout le reste est contenu.
Les clés SSH vivent dans ~/.ssh/claudebox, une paire de clés dédiée générée pendant l'installation. Tes clés personnelles n'entrent jamais dans le container. Configure quelle clé est utilisée via CLAUDEBOX_SSH_DIR si besoin.
Chaîne d'approvisionnement. L'image de base est épinglée par digest @sha256:, pas juste par un tag, parce que les tags sont mutables alors qu'un digest est adressé par contenu, donc une reconstruction ne peut pas tirer silencieusement d'autres octets de base. Dans l'image full, l'archive Go est téléchargée sur disque et vérifiée avec sha256sum -c contre une somme de contrôle par architecture avant extraction, au lieu de l'ancien tuyau non vérifié curl … | tar. Un téléchargement trafiqué ou tronqué fait échouer le build au lieu d'atterrir dans l'image.
L'auth du mode API se fait par bearer token dans l'en-tête Authorization. Pose CLAUDEBOX_API_MODE_TOKEN. Si tu ne le poses pas, l'API tourne sans authentification, ce qui va pour un usage local et est une mauvaise idée exposée sur un réseau.
En Résumé
Je fais tourner chaque session Claude dans claudebox maintenant. Sept modes, zéro pollution du host. L'isolation fait que je n'hésite pas une seconde à le laisser installer des paquets, réécrire des configs ou lancer les commandes qu'il lui faut pour finir le boulot. La continuité de session fait que je ferme mon terminal, je reviens des heures plus tard et je reprends exactement où j'en étais. L'API et l'endpoint OpenAI font que je peux brancher Claude sur d'autres services sans écrire de code de colle. Le bot Telegram fait que je peux lancer une tâche depuis mon téléphone loin de mon bureau. Le scheduler cron fait que Claude bosse pendant que je dors.
Va le chercher: github.com/psyb0t/docker-claudebox
Sous licence WTFPL, parce que la seule chose plus dangereuse qu'une IA avec un accès root, c'est une IA avec un accès root et une licence restrictive.
L'Installer Dans Ton Agent
Claude Code faisant tourner Claude Code dans une boîte, et maintenant installable depuis Claude Code, ce qui est à peu près aussi récursif que je veux bien aller. Tout ce qui est sous .agents/ est catalogué dans un seul marketplace, donc ça fait deux commandes:
claude plugin marketplace add psyb0t/agents
claude plugin install claudebox@psyb0tCodex utilise le même marketplace avec un verbe différent, codex plugin add claudebox@psyb0t, parce qu'il n'existe pas de codex plugin install. Il trouve aussi le skill tout seul dans un checkout du repo, puisqu'il scanne .agents/skills/ nativement sans que rien ne soit installé. Il est aussi listé sur le MCP Registry officiel maintenant, donc un client qui résout ses serveurs depuis là peut le trouver sans qu'on lui donne une URL.