Aviso, este post ha quedado superado. Desde la v2.0.0, claudebox es una imagen hija fina de aicodebox, la base agnóstica al agente que ahora es dueña de todas las superficies descritas abajo. La API, el endpoint compatible con OpenAI, el servidor MCP, el bot de Telegram y el scheduler de cron viven todos en la base y se comparten con pibox y codexbox. claudebox en sí es ahora el adaptador de Claude Code más un wrapper del lado del host. Lee primero el post de aicodebox, ahí es donde vive de verdad la arquitectura. Lo de abajo sigue funcionando y documenta la superficie propia de claudebox, pero la historia real ahora es la base.
MCP es su propio modo, no un rincón de la API
Merece decirse explícitamente, porque la documentación lo describió mucho tiempo como un endpoint dentro del modo API y eso lo infravalora. MCP corre de dos maneras:
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 versión autónoma convive con todos los demás modos en vez de reemplazarlos, y se lleva su propio token, CLAUDEBOX_MCP_MODE_TOKEN, que no cae de vuelta al token de la API. Déjalo vacío y la superficie MCP no tiene auth ninguna, que es un valor por defecto distinto y mucho peor que «hereda lo que esté usando la API». Ponlo deliberadamente.
Cinco herramientas: run_prompt más cuatro de ficheros, list_files, read_file, write_file, delete_file, cada una resolviendo su path bajo la raíz del workspace y rechazando cualquier cosa que se salga. Prefiérelas antes que embutir una carga en prompt: el agente sabe leer el workspace él solo, así que «escribe la entrada en un fichero y dile cuál» gana a un prompt de 50 KB.
Uso Claude Code para todo. Escribir código, depurar mierdas, desplegar infraestructura, gestionar repos, escribir artículos para este mismo blog e incluso automatizar sesiones de navegador sobre la marcha. Se ha convertido en la columna vertebral de cómo trabajo. Pero darle a un agente de IA acceso completo a tu sistema es acojonante, no porque Claude sea malintencionado, sino porque corre con
--permission-mode bypassPermissions y tiene poder para hacer lo que le dé la gana. Un comando malo y tu host está frito. Últimamente el container en el que corre ni siquiera es específico de claudebox, es aicodebox con un adaptador con forma de Claude atornillado encima.La respuesta obvia es: métete en un container. Pero hacerlo bien de verdad es otro problema entero. Construí claudebox para resolverlo. Lo que empezó como un simple wrapper containerizado de Claude Code ha crecido hasta siete formas distintas de ejecutar Claude, cada una realmente útil, ninguna de relleno.
El Cambio de Nombre
Esto se llamaba antes docker-claude-code, imagen psyb0t/claude-code, binario claude. Ahora es claudebox, imagen psyb0t/claudebox, binario claudebox. Las claves SSH se movieron de ~/.ssh/claude-code a ~/.ssh/claudebox.
Si estás actualizando: desinstala el binario viejo, baja la imagen nueva, vuelve a lanzar el script de instalación. Tu directorio de config ~/.claude y el historial de sesiones sobreviven al cambio de nombre intactos.
v2.0.0, rebasado sobre aicodebox
El cambio más gordo vino después del renombrado. claudebox es ahora una imagen hija fina de psyb0t/aicodebox, una base común, agnóstica al agente, que se encarga de todas las superficies de modo. El mismo patrón que psyb0t/pibox y psyb0t/codexbox. El servidor de API, el bot de Telegram, el scheduler de cron y el endpoint MCP viven todos en la base ahora; claudebox aporta un adaptador que sabe hablar específicamente con Claude Code. Los arreglos en la base llegan gratis a todas las imágenes hijas.
Eso es un rebase arquitectónico completo, así que rompió cosas. Una parte está aliasada o enlazada simbólicamente hacia delante, otra buena parte no, así que repasa esta lista antes de actualizar:
- Endpoints:
POST /run/cancel?runId=…pasó a serDELETE /run/{run_id}.GET /healthpasó a serGET /healthz. - Herramienta MCP:
claude_runpasó a serrun_prompt. Actualiza las configuraciones de tu cliente MCP. - Variables de entorno:
CLAUDEBOX_MODE_APIpasó a serCLAUDEBOX_API_MODE,CLAUDEBOX_MODE_CRON_FILEpasó a serCLAUDEBOX_CRON_MODE_FILE, y así toda la lista. Desde la v2.5.0 el entrypoint redirige a los nombres nuevos los seis nombres v1CLAUDEBOX_MODE_*(la API, su puerto y su token, Telegram, cron, el fichero de cron), además de los todavía más viejosCLAUDE_MODE_*,CLAUDE_WORKSPACEy los dosCLAUDE_TELEGRAM_*, y si tienes puestos los dos, gana el nombre canónico.CLAUDEBOX_TELEGRAM_BOT_TOKENyCLAUDEBOX_TELEGRAM_CONFIGde la v1 no están en esa lista, así que esos los renombras a mano. - Rutas: la raíz de workspace
/workspacespasó a ser/workspace(en singular). El home del container/home/claude/.claudepasó a ser/home/aicode/.aicodebox. Los enlaces simbólicos de compatibilidad cubren/workspacesy/home/aicode/.claude, pero ya nada apunta a/home/claude, así que cambia el destino de tus bind mounts viejos. - Cron: nada que renombrar, digan lo que digan las notas de la v2.0.0. Los horarios de cinco campos se siguen interpretando como un cron normal, al minuto. El sexto campo es opcional, va primero y cuenta segundos.
- Desaparecido: el comando
/bashde Telegram. Reimpleméntalo del lado del cliente si lo necesitas. - Variante full:
make build-fullya no es un target multi-stage aparte. Desde v2.3.12 construye directamente sobre la imagen full de aicodebox y solo le añade encima la capa de Claude.
El adaptador viene con 35 tests unitarios de pytest, más 9 tests de humo containerizados que corren contra un binario claude simulado, healthz, la lista de modelos de OpenAI, los marcadores de init.d, los enlaces de compatibilidad, el aliasado de entorno, la inyección de always-skills, los argumentos extra y el modo de permisos por defecto.
Siete Interfaces, Un Container
claudebox ya no es solo un wrapper. Son siete interfaces distintas a Claude Code corriendo dentro de Docker:
- CLI interactivo: container persistente, reanudación de sesión, el modo original
- CLI programático: no interactivo, funciona desde scripts y CI, con su propio container dedicado
- Servidor HTTP API: API REST con gestión de workspaces, operaciones de ficheros, ejecuciones síncronas y asíncronas
- Endpoint compatible con OpenAI: sustituto directo en
/openai/v1/chat/completionscon streaming - Servidor MCP: cinco herramientas que Claude puede usar desde otros agentes vía Model Context Protocol
- Bot de Telegram: workspaces por chat, compartición de ficheros, prompts a Claude desde el móvil
- Scheduler de cron: trabajos programados definidos en YAML con resolución por debajo del minuto, historial por trabajo y notificaciones opcionales de Telegram
Instalación
curl -fsSL https://raw.githubusercontent.com/psyb0t/docker-claudebox/master/install.sh | bashEsto genera claves SSH en ~/.ssh/claudebox, baja la imagen (siempre, relanzar el instalador es la forma soportada de actualizar) y deja el binario claudebox en /usr/local/bin/claudebox. Si necesitas pasar variables de entorno al instalador, expórtalas primero en una línea aparte, porque canalizar VAR=x curl ... | bash no reenvía la variable al script:
export CLAUDEBOX_INSTALL_DIR=/usr/local/bin
curl -fsSL https://raw.githubusercontent.com/psyb0t/docker-claudebox/master/install.sh | bashLuego:
claudeboxLa primera ejecución te pide autenticación. Después de eso funciona y ya. El wrapper se encarga de todo el ciclo de vida del container, crea uno nuevo si no existe para el directorio actual, lo reinicia y se reengancha si ya existe.
Variantes de Imagen
Full: psyb0t/claudebox:latest-full
Base Ubuntu cargada con todo lo que un desarrollador necesita de verdad: Go con el toolchain completo (golangci-lint, gopls, delve), Python 3.14 (flake8, black, mypy, pyright, vulture, pytest, poetry), Node.js 24 LTS con el ecosistema de siempre, toolchain de C/C++, Docker CE con Compose, Terraform, kubectl, helm, GitHub CLI, clientes de base de datos para SQLite/PostgreSQL/MySQL/Redis, y un montón de utilidades (jq, ripgrep, fd-find, bat, shellcheck, shfmt, httpie). Cada container nuevo te deja en el workspace un CLAUDE.md con el toolchain instalado para que Claude sepa con qué tiene que trabajar, y no toca el tuyo si el proyecto ya tiene uno.
Mínima: psyb0t/claudebox:latest
Solo lo esencial encima de la base aicodebox: Ubuntu 24.04, git/curl/wget/jq, Node.js 24 LTS con npm, Python 3.14 con uv, Docker CE. Imagen más pequeña, descarga más rápida. Claude tiene sudo sin contraseña, así que instala sobre la marcha lo que le hace falta. Ojo, que el nombrado se dio la vuelta en la v2: latest es ahora la imagen mínima (antes era la full), latest-full es la build con toolchain, y el viejo opt-in CLAUDEBOX_MINIMAL=1 ya no hace nada porque la mínima es la de por defecto. CLAUDEBOX_FULL=1 es como optas en el sentido contrario, e instalar con esa variable puesta hornea esa elección en el wrapper para que se quede. Usa init hooks para scriptear tu setup, así cada container nuevo se instala solito lo que necesitas en vez de que Claude lo haga a mano a mitad de sesión.
Claude Code en sí ya no está en ninguna de las dos imágenes, y la razón es la licencia. El CLI de Anthropic es propietario sin permiso de redistribución, así que publicar una imagen con él horneado dentro significaría distribuir software de otro. En su lugar la imagen lleva la versión fijada en CLAUDEBOX_CLAUDE_VERSION y el entrypoint ejecuta npm install -g @anthropic-ai/claude-code@<version> la primera vez que arranca un container nuevo. Nada de Anthropic viaja en las capas publicadas; cada container se lo baja él mismo de npm. El coste es que el primer arranque de un container nuevo necesita red y unos segundos extra. Los reinicios en caliente se lo saltan del todo. ¿Quieres otra versión? Pon CLAUDEBOX_CLAUDE_VERSION en el docker run.
Las cajas pueden lanzarse unas a otras ahora
Instala claudebox, codexbox y pibox en el mismo directorio y cada wrapper monta las tres en solo lectura, la suya incluida, en /usr/local/bin/<name>. Lo que significa que Claude, desde dentro de su propio container, puede tirar de codexbox o pibox y hacer que otro agente se ocupe de un trozo de trabajo. El uso obvio es una segunda opinión sobre un diff sin salir de la sesión.
La parte que requirió pensar de verdad es el contexto. Una ejecución anidada necesita saber dónde están las cosas en el host, no dentro del container que da la casualidad de que está llamando, así que hay un bloque versionado para eso: AICODEBOX_LAUNCH_CONTEXT_VERSION=1 más AICODEBOX_HOST_HOME, AICODEBOX_HOST_WORKSPACE, AICODEBOX_HOST_WRAPPER_DIR, y por agente AICODEBOX_HOST_CLAUDE_HOME / CODEX_HOME / PI_HOME con su *_WRAPPER correspondiente para cada uno. Versionado porque la forma va a cambiar y un lanzamiento anidado debería poder darse cuenta.
Las ejecuciones anidadas son además deliberadamente desechables: se saltan las escrituras de ficheros de auth que hace una ejecución de primer nivel, así que un agente hijo no puede reescribir en silencio las credenciales de la caja que lo llamó.
Junto a eso, AICODEBOX_ENV_* y AICODEBOX_MOUNT_* reenvían entorno y montajes a todas las cajas, al lado de los CLAUDEBOX_ENV_* y CLAUDEBOX_MOUNT_* existentes, que son por caja. Y AICODEBOX_MANAGED_INSTALL=1 es una instalación no interactiva para scripts de aprovisionamiento que, importante, se niega a pisar una clave SSH que ya exista. CLAUDEBOX_INSTALL_DIR y CLAUDEBOX_BIN_NAME controlan dónde aterriza y cómo se llama.
Un detalle de cadena de suministro que merece mención: el instalador ahora baja wrapper.sh del tag de release inmutable correspondiente y no de master, así que instalar una versión fijada te da de verdad el wrapper de esa versión.
Modo Interactivo
Ejecuta claudebox desde cualquier directorio y te sale una sesión viva. El container persiste entre ejecuciones, la sesión continúa donde la dejaste. Cada workspace tiene su propio container, nombrado según la ruta del directorio.
Comandos de utilidad:
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 # update Claude Code inside the container on this runContinuidad de sesión. claudebox ejecuta Claude con --continue, así que retoma la última conversación del directorio actual. Mata el terminal, vuelve al día siguiente, arráncalo otra vez y Claude sigue exactamente donde lo dejó. Sin sesión, ningún problema, empieza de cero.
Límite de memoria. Los containers topan en 10g por defecto. Lo sobreescribes al lanzar con CLAUDEBOX_MAX_MEM=16g claudebox (el antiguo CLAUDE_MAX_MEM sigue funcionando).
Coincidencia de UID/GID. El entrypoint detecta el dueño del workspace y ajusta el usuario del container para que coincida. Los ficheros creados dentro del container tienen el propietario correcto en el host. Sin gilipolleces de chown -R.
Modo Programático
Le pasas un prompt con -p y claudebox corre de forma no interactiva. El -p no es opcional: sin él, el wrapper te rebota el prompt como un comando desconocido. Usa un container _prog dedicado por workspace, separado del interactivo, sin TTY, funciona desde scripts, desde cron, desde otras herramientas:
# basic run
claudebox -p "explain this codebase"
# pick a model
claudebox -p "explain this codebase" --model sonnet
claudebox -p "audit this" --model opus
# output formats
claudebox -p "list all TODOs" --output-format json
claudebox -p "list all TODOs" --output-format stream-json | jq .
# reasoning effort
claudebox -p "debug this complex issue" --effort high
claudebox -p "quick question" --effort low
# custom system prompt
claudebox -p "review this" --system-prompt "You are a security auditor"
claudebox -p "review this" --append-system-prompt "Focus on SQL injection"
# structured output
claudebox -p "extract author and title" --output-format json \
--json-schema '{"type":"object","properties":{"author":{"type":"string"},"title":{"type":"string"}}}'
# session control
claudebox -p "start over" --no-continue
claudebox -p "keep going" --resume abc123-def456Alias de modelos: opus (Opus 4.6), sonnet (Sonnet 4.6), haiku (Haiku 4.5), opusplan (Opus para planificar más Sonnet para ejecutar), sonnet[1m] (Sonnet con ventana de contexto de 1M). O pasa un nombre de modelo completo para fijar una versión concreta.
Formatos de salida: text (por defecto), json (un único objeto resultado con el coste y el desglose de tokens), stream-json (NDJSON, un evento por línea, init de sistema, respuestas del asistente, uso de herramientas, resultados de herramientas, eventos de rate limit, resultado final). El viejo json-verbose está muerto: el wrapper de v2 lo rechaza y te manda al modo API, donde POST /run con "eventMode": "full" te devuelve cada registro en su lugar.
Modo API
Pon CLAUDEBOX_API_MODE=1 para ejecutar el container como servidor HTTP API. Enchúfalo a un stack de docker-compose y otros servicios pueden hablar con Claude por 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: mandas un prompt, recibes un resultado. Campos:
prompt,workspace,model,system_prompt,append_system_prompt,json_schema,no_continue,resume,thinking(el esfuerzo de razonamiento, que le llega a Claude Code como--effort). Devuelve 409 si el workspace ya está procesando. - POST /run con
"async": true: devuelve unrunIdal instante. Consultas GET /run/result?runId=X hasta que termine. Lanzar y olvidarse. - DELETE /run/{run_id}: mata un proceso en marcha (era
POST /run/cancel?runId=…antes de la v2) - GET /files/{path}: lista un directorio o descarga un fichero
- PUT /files/{path}: sube un fichero (los directorios padre se crean automáticamente)
- DELETE /files/{path}: borra un fichero
- GET /healthz: comprobación de salud, sin auth (era
GET /healthantes de la v2) - GET /status: muestra qué workspaces están ocupados ahora mismo
Todas las rutas son relativas a /workspace. Auth por bearer token en la cabecera Authorization, pon CLAUDEBOX_API_MODE_TOKEN para activarla. El seguimiento de workspaces ocupados devuelve 409 Conflict para que no encoles por accidente ejecuciones solapadas sobre el mismo workspace.
La salida estructurada y el registro completo son mandos separados ahora
Dos cosas cambiaron en la API, dos cosas que estaban enredadas la una con la otra. jsonSchema pasa ahora por el propio flag --json-schema de Claude Code en vez de ir atornillado encima, y la retención de eventos es su propio control: eventMode: "full" te devuelve cada registro stream-json nativo, mensajes parciales, texto de subagentes y eventos de hook incluidos, sin colapsar turnos ni truncar resultados de herramientas. El adaptador le pide ahora esos tres flujos a Claude Code en cada ejecución, así que el ajuste solo decide qué te llega de vuelta. Querer la transcripción entera ya no exige fingir que querías un esquema, y pedir un esquema ya no te impone la manguera completa. Eliges cada uno por su cuenta.
Endpoint Compatible con OpenAI
POST /openai/v1/chat/completions, un adaptador de OpenAI de sustitución directa que enruta las peticiones a Claude Code dentro del container. Funciona con cualquier cosa que hable la API de OpenAI: LiteLLM, Open WebUI, clientes propios, lo que sea.
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
}'El streaming funciona vía Server-Sent Events. Las conversaciones de varios turnos funcionan, pasas todo el historial de mensajes y Claude mantiene el contexto. Lo multimodal también funciona, mandas contenido de imagen en base64 en el mensaje y Claude lo puede ver. ¿Quieres también las tripas? Añade "stream_options": {"include_aicodebox_events": true} a una petición con streaming y te llegan eventos SSE con el nombre aicodebox.native junto a los chunks normales, con los registros en crudo de Claude (thinking, llamadas a herramientas, diagnósticos), mientras los chunks estándar y el [DONE] se quedan exactamente igual. Manda reasoning_effort y también le llega a Claude Code como --effort: minimal pasa a low, none deja el valor por defecto, y cualquier cosa fuera de la escala se lleva un error en vez de ignorarse a escondidas.
Cabeceras propias para controlar el comportamiento:
X-Claude-Workspace: en qué workspace ejecutarX-Claude-Continue: si continuar la sesión anteriorX-Claude-Append-System-Prompt: añade instrucciones extra al prompt de sistema
Para LiteLLM, apúntalo a http://your-host:8080/openai/v1 como proveedor OpenAI personalizado y funciona sin ninguna configuración especial.
Pasada de endurecimiento. El adaptador de OpenAI recibió una suite de tests de verdad y una auditoría de seguridad: una guarda anti-SSRF descarta las URLs de imagen que resuelven a direcciones privadas o de loopback, finish_reason vuelve como stop o tool_calls en vez de los motivos de parada en bruto de Claude (un stream que muere o se pasa de tiempo acaba en error o timeout), las conversaciones de varios turnos se quedan correctamente en el mismo workspace entre continuaciones, y un response_format o una cabecera de control mal formados se llevan un 400. Los campos de petición que no usa para nada, tipo temperature, se siguen ignorando en vez de rechazarse. Llegó con 24 tests unitarios y 3 tests de integración, pero los unitarios se escribieron contra el fichero de servidor de antes de la v2, que la v2 borró, así que ya ni siquiera se importan. La cobertura de verdad del endpoint vive ahora en la suite de tests de aicodebox, junto al código.
Servidor MCP
Activa el servidor MCP en /mcp/ para dejar que otros agentes y herramientas llamen a tu container de Claude vía Model Context Protocol. Cinco herramientas expuestas:
- run_prompt: ejecuta un prompt en un workspace y te devuelve el resultado (renombrada desde
claude_runen la v2.0.0) - list_files: lista los ficheros de un directorio de workspace
- read_file: lee un fichero de un workspace
- write_file: escribe un fichero en un workspace
- delete_file: borra un fichero de un workspace
Esto significa que otras instancias de Claude, agentes propios o cualquier cliente compatible con MCP pueden usar tu instancia de claudebox como herramienta, delegando trabajo a una sesión de Claude nueva con acceso completo a ficheros. En cuanto a rutas: en modo API la canónica es /mcp/, la variante sin barra /mcp cae en el mismo handler sin redirección, y el sidecar standalone del 8081 sirve directamente en la raíz del puerto, /. Una trampa: la protección contra DNS rebinding del SDK de MCP sigue activa y, de serie, solo acepta una cabecera Host con localhost, 127.0.0.1 o [::1], así que un cliente en la misma máquina va bien, pero otro contenedor, un reverse proxy o cualquier otra cosa que llame al endpoint por nombre de host se come un 421 en toda la cara (un cliente de navegador con un Origin que no está en la lista se lleva un 403). Desde v2.4.7 se arregla con dos variables de entorno, CLAUDEBOX_MCP_MODE_ALLOWED_HOSTS y CLAUDEBOX_MCP_MODE_ALLOWED_ORIGINS, con valores exactos separados por comas. Definir una sustituye el valor por defecto de loopback en vez de sumarse a él, así que vuelve a meter localhost y compañía en la lista si todavía los necesitas.
Modo Telegram
Pon CLAUDEBOX_TELEGRAM_MODE=1 y te sale un bot de Telegram que habla con Claude. Cada chat tiene su propio workspace y sus ajustes. Manda texto, ficheros, fotos, vídeos, mensajes de voz. Recupera ficheros con /fetch.
La configuración vive en un fichero YAML con modelo, effort, workspace y prompt de sistema por 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"
-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.sockComandos del bot:
- cualquier texto → enviado a Claude como prompt
- manda un fichero/foto/vídeo/voz → guardado en el workspace, el pie de foto se convierte en el prompt
/model [name]: muestra el modelo actual con botones seleccionables, o lo pone directamente:haiku,sonnet,opus,opusplan,reset/effort [level]: muestra o selecciona el effort:off,low,medium,high,xhigh,max,reset. La elección se queda guardada por chat y le llega a Claude Code como--effort, así que un chat puesto enmaxpiensa más de verdad, joder, no de cara a la galería./system_prompt [text]: muestra, pone o resetea la sobreescritura de prompt de sistema para este chat/append_system_prompt [text]: igual para el prompt de sistema añadido/fetch <path>: devuelve un fichero del workspace como adjunto de Telegram/cancel: mata el proceso de Claude en marcha para este chat/status: muestra qué chats tienen procesos en marcha ahora/config: muestra la configuración actual de este chat/reload: recarga en caliente la config YAML sin reiniciar el container
Claude puede empujar ficheros de vuelta poniendo [SEND_FILE: path] en su respuesta. Las imágenes llegan como fotos, los vídeos como vídeos, todo lo demás como documentos. Las respuestas largas se parten automáticamente en varios mensajes.
Renderizado de markdown. El bot traduce la salida markdown de Claude al dialecto HTML de Telegram antes de enviarla, y negrita, cursiva, código en línea, bloques de código, citas, títulos, listas y enlaces se renderizan todos de forma nativa en el chat. Se acabaron los **asteriscos en crudo y las comillas invertidas contaminando tus mensajes.
Modo Cron
Pon CLAUDEBOX_CRON_MODE=1 y apunta CLAUDEBOX_CRON_MODE_FILE a un fichero YAML para ejecutar trabajos de Claude programados. Para bajar del minuto añades un sexto campo, el primero, que cuenta segundos: */30 * * * * * se dispara cada 30 segundos. Las entradas normales de cinco campos se siguen interpretando como un cron de toda la vida, al minuto, así que las configuraciones de antes de la v2 funcionan tal cual.
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 plantilla disponibles en instruction, system_prompt y append_system_prompt: {system_datetime} (fecha y hora UTC actuales) y {job_name} (el campo de nombre del trabajo). Por trabajo, model, system_prompt, append_system_prompt, effort, thinking y telegram_chat_id sobreescriben los valores por defecto de la raíz, y telegram_chat_id: 0 saca a un trabajo concreto del chat de la raíz, para esa mierda que corre cada 30 segundos y que nadie quiere oír sonando en el móvil. Dos campos solo existen por trabajo: workspace lo ejecuta en un subdirectorio relativo de CLAUDEBOX_WORKSPACE (las rutas absolutas y las fugas con .. se rechazan), y no_continue: true lo arranca de cero en vez de retomar la última sesión de Claude en ese directorio.
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¿Pasas de escribir un compose? Desde el directorio del proyecto, CLAUDEBOX_CRON_MODE=1 CLAUDEBOX_CRON_MODE_FILE=$PWD/cron.yaml claudebox arranca en segundo plano un container claude-<path>_cron para ese workspace, y CLAUDEBOX_CRON_MODE=1 claudebox stop se lo carga. Ese atajo estuvo muerto casi toda la v2, porque el wrapper pasaba un nombre de variable que el entrypoint de la v2 nunca leía, así que el container arrancaba y el cron no se encendía nunca. La v2.5.0 lo arregló. El scheduler corre en primer plano, y docker logs muestra cada tic. Si un trabajo sigue ejecutándose cuando se dispara el siguiente tic, ese tic se salta. El historial de trabajos se vuelca a ~/.aicodebox/cron/history/<workspace-slug>/<timestamp>-<job-name>/ como meta.json, stdout.log, stderr.log y result.txt, más telegram.json cuando salió una notificación. Cada ejecución añade además un resumen de una línea a ~/.aicodebox/cron/<job-name>.jsonl.
Pon telegram_chat_id (en la raíz o por trabajo) más CLAUDEBOX_TELEGRAM_MODE_TOKEN para que el resultado de Claude te llegue a Telegram al acabar cada trabajo. El bot de Telegram no necesita estar corriendo, el cron usa el token directamente.
Effort de razonamiento por trabajo. Pon effort en la raíz como valor por defecto y sobreescribe por trabajo, la misma escala que el CLI (low, medium, high, xhigh, max). El valor le llega a Claude Code como --effort, y un nivel desconocido se rechaza con un error en vez de correr calladito con el effort por defecto. Modelos baratos para trabajos baratos, effort máximo para la auditoría nocturna chunga.
Historial de ejecuciones previas, inyectado automáticamente. Cada tic de cron añade ahora un bloque de sistema que le dice a Claude dónde viven sus ejecuciones anteriores: el directorio de la ejecución previa más reciente, más un glob para todas las más viejas (~/.aicodebox/cron/history/<workspace-slug>/*-<job-name>/). Claude no los lee con ansia; recibe las rutas y decide si hace Glob o Read cuando el trabajo pide de verdad análisis de tendencias o detección de regresiones. La primerísima ejecución se salta la pista porque todavía no hay nada. Esto desbloquea «compara con la semana pasada», «ha regresado esta métrica», «qué ha cambiado desde la ejecución de ayer», sin cablear nada de eso por trabajo. Combinado con telegram_chat_id te sale un agente de resumen diario que de verdad sabe lo que dijo ayer.
Personalización
Skills Siempre Activos
Suelta ficheros SKILL.md en ~/.claude/.always-skills/ y se auto-inyectan en cada invocación de claudebox, interactiva, programática, API, Telegram, cron, todas. Contexto persistente que sigue a Claude a cada sesión sin tocar los ficheros CLAUDE.md de los proyectos individuales.
Hooks de Init
Los scripts en ~/.claude/init.d/*.sh se ejecutan una vez por container, la primera vez que arranca, justo después de los scripts de init de la propia imagen. Van en orden de nombre de fichero, como aicode, que tiene sudo sin contraseña, así que lo que necesite root lleva un sudo delante. Un docker start no los vuelve a ejecutar, pero cada container nuevo sí, así que hazlos seguros de repetir. Si uno falla, queda en el log y el resto sigue. Úsalos para instalaciones de paquetes extra o para el setup de cada container:
mkdir -p ~/.claude/init.d
cat > ~/.claude/init.d/setup.sh << 'EOF'
#!/bin/bash
sudo apt-get update && sudo apt-get install -y some-package
sudo pip install some-library
EOF
chmod +x ~/.claude/init.d/setup.shScripts Propios
Suelta ejecutables en ~/.claude/bin/ en el host y están en el PATH dentro de cada container, en todos los modos, en los init hooks y en las shells de docker exec. Persiste en todas las sesiones, en todos los workspaces.
Reenvío de Variables de Entorno
Usa el prefijo CLAUDEBOX_ENV_ para pasar variables de entorno arbitrarias al container, el prefijo se quita:
CLAUDEBOX_ENV_GITHUB_TOKEN=xxx CLAUDEBOX_ENV_MY_VAR=hello claudebox -p "do stuff"Montajes de Volumen Extra
El prefijo CLAUDEBOX_MOUNT_ para montar directorios adicionales:
# Mount at same path on both sides
CLAUDEBOX_MOUNT_DATA=/data claudebox -p "process the data"
# Explicit source:dest
CLAUDEBOX_MOUNT_STUFF=/host/path:/container/path claudebox -p "do stuff"
# Read-only
CLAUDEBOX_MOUNT_RO=/data:/data:ro claudebox -p "read the data"El Modelo de Workspace
claudebox crea dos containers por workspace: claude-<path> para sesiones interactivas y claude-<path>_prog para ejecuciones programáticas. Pueden correr a la vez, pero comparten ~/.claude, historial de sesiones incluido, así que una ejecución con -p retoma la última conversación de ese directorio salvo que le pases --no-continue. La sesión interactiva no te bloquea los scripts. Los scripts no te interrumpen la sesión.
El directorio ~/.claude se monta en cada container, y la configuración, las claves de API, los always-skills, los hooks de init y los scripts propios se comparten todos entre workspaces. Las claves SSH de ~/.ssh/claudebox se montan automáticamente. El aislamiento es a nivel de workspace, la identidad es común.
El socket de Docker se monta también para que Claude pueda construir imágenes, levantar stacks de compose y gestionar containers desde dentro de su propio container. Como el workspace se monta en su ruta real del host ($PWD:$PWD), los montajes de volumen desde dentro de Claude se resuelven bien en el host. Claude escribe un docker-compose.yml, lo ejecuta, y las rutas funcionan.
Trae Su Propio Skill y Su Plugin
El repo lleva .agents/skills/claudebox/, un skill de agente que documenta cada modo que expone la caja, así que un asistente al que le apuntes a él ya conoce la shell interactiva, la ejecución de un solo tiro, la API REST, el endpoint compatible con OpenAI, el servidor MCP, el bot de Telegram y el scheduler de cron sin que le expliques nada.
Al lado, @psyb0t/claudebox en .agents/plugins/claudebox/, un puente MCP stdio↔HTTP sobre mcp-remote, para que un agente OpenClaw o MCP pueda pilotar directamente el endpoint /mcp de una caja en marcha. Con licencia MIT. CI publica los dos en ClawHub en los pushes de tag.
Modelo de Seguridad
Esto corre con --permission-mode bypassPermissions, el equivalente moderno del viejo --dangerously-skip-permissions. Claude tiene sudo sin contraseña dentro del container y puede hacer lo que le dé la gana.
La frontera de seguridad es el container. Claude no puede tocar el sistema de ficheros de tu host más allá del workspace montado y la config ~/.claude. Si se descontrola, docker stop y docker rm y se acabó. Levantas uno nuevo en segundos.
El montaje del socket de Docker es la excepción, porque da acceso al demonio Docker del host. No lo montes si eso te preocupa. Todo lo demás queda contenido.
Las claves SSH viven en ~/.ssh/claudebox, un par de claves dedicado generado durante la instalación. Tus claves personales no entran nunca en el container. Configura qué clave se usa vía CLAUDEBOX_SSH_DIR si hace falta.
Cadena de suministro. La imagen base está fijada por digest @sha256:, no solo por un tag, porque los tags son mutables mientras que un digest está direccionado por contenido, así que una reconstrucción no puede bajarse en silencio otros bytes de base. En la imagen full el tarball de Go se descarga a disco y se verifica con sha256sum -c contra una suma de comprobación por arquitectura antes de extraerlo, en vez de la vieja tubería sin verificar curl … | tar. Una descarga manipulada o truncada revienta la build en vez de aterrizar en la imagen.
La auth del modo API va por bearer token en la cabecera Authorization. Pon CLAUDEBOX_API_MODE_TOKEN. Si no lo pones, la API corre sin autenticar, lo cual vale para uso local y es mala idea expuesto a una red.
En Resumen
Ahora ejecuto cada sesión de Claude dentro de claudebox. Siete modos, cero contaminación del host. El aislamiento significa que no me lo pienso dos veces antes de dejarle instalar paquetes, reescribir configuraciones o ejecutar los comandos que necesite para acabar el trabajo. La continuidad de sesión significa que cierro el terminal, vuelvo horas después y sigo exactamente donde lo dejé. La API y el endpoint de OpenAI significan que puedo enganchar Claude a otros servicios sin escribir código de pegamento. El bot de Telegram significa que puedo arrancar una tarea desde el móvil estando lejos del escritorio. El scheduler de cron significa que Claude trabaja mientras yo duermo.
Cógelo aquí: github.com/psyb0t/docker-claudebox
Bajo licencia WTFPL, porque lo único más peligroso que una IA con acceso root es una IA con acceso root y una licencia restrictiva.
Cómo Instalarlo En Tu Agente
Claude Code ejecutando Claude Code en una caja, y ahora instalable desde dentro de Claude Code, que es más o menos tan recursivo como quiero llegar. Todo lo que hay bajo .agents/ está catalogado en un solo marketplace, así que son dos comandos:
claude plugin marketplace add psyb0t/agents
claude plugin install claudebox@psyb0tCodex usa el mismo marketplace con un verbo distinto, codex plugin add claudebox@psyb0t, porque no existe codex plugin install. También encuentra el skill por su cuenta en un checkout del repo, ya que escanea .agents/skills/ de forma nativa sin nada instalado en absoluto. Además ahora está listado en el MCP Registry oficial, así que un cliente que resuelva servidores desde ahí puede encontrarlo sin que le den una URL.