claudebox: Claude Code en Docker, Ahora Con Siete Formas de Cargarte Tu Servidor de Producción

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 8081

La 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. Todo está aliasado o enlazado simbólicamente hacia delante para que las configuraciones existentes sigan funcionando, pero los nombres canónicos han cambiado:

  • Endpoints: POST /run/cancel?runId=… pasó a ser DELETE /run/{run_id}. GET /health pasó a ser GET /healthz.
  • Herramienta MCP: claude_run pasó a ser run_prompt. Actualiza las configuraciones de tu cliente MCP.
  • Variables de entorno: CLAUDEBOX_MODE_API pasó a ser CLAUDEBOX_API_MODE, CLAUDEBOX_MODE_CRON_FILE pasó a ser CLAUDEBOX_CRON_MODE_FILE, y así toda la lista. El entrypoint aliasa los nombres antiguos hacia delante.
  • Rutas: la raíz de workspace /workspaces pasó a ser /workspace (en singular). El home del container /home/claude/.claude pasó a ser /home/aicode/.aicodebox. Los enlaces simbólicos de compatibilidad mantienen resolviendo los bind mounts viejos.
  • Cron: solo croniter de seis campos. Antepón un 0 a cualquier horario de cinco campos que estuvieras usando.
  • Desaparecido: el comando /bash de Telegram. Reimpleméntalo del lado del cliente si lo necesitas.
  • Variante full: make build-full ahora se apila encima de la imagen mínima en vez de ser un target multi-stage aparte.

El adaptador viene con 32 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/completions con 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, comandos de shell 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 | bash

Esto 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 | bash

Luego:

claudebox

La 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 vía pyenv (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). El container autogenera un CLAUDE.md listando cada herramienta disponible para que Claude sepa con qué tiene que trabajar.
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 prehornear tu setup y no esperar instalaciones de paquetes en cada container nuevo.
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 otras dos en solo lectura 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         # pull the latest image and reinstall

Continuidad 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 y claudebox corre de forma no interactiva. Usa un container _prog dedicado por workspace, separado del interactivo, sin TTY, funciona desde scripts, desde cron, desde otras herramientas:

# 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

Alias 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), json-verbose (igual que json pero con un array turns que muestra cada llamada a herramienta, cada resultado de herramienta y cada mensaje del asistente, visibilidad completa de lo que hizo Claude), 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).

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.sock

Endpoints:

  • POST /run: mandas un prompt, recibes un resultado. Campos: prompt, workspace, model, system_prompt, append_system_prompt, json_schema, effort, no_continue, resume. Devuelve 409 si el workspace ya está procesando.
  • POST /run con "async": true: devuelve un runId al 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 /health antes 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" enciende el streaming de mensajes parciales, de texto de subagentes y de eventos de hook, y te devuelve cada registro stream-json nativo sin colapsar turnos ni truncar resultados de herramientas. 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.
Cabeceras propias para controlar el comportamiento:

  • X-Claude-Workspace: en qué workspace ejecutar
  • X-Claude-Continue: si continuar la sesión anterior
  • X-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 rechaza las peticiones que intentan colar URLs internas por los campos de workspace, los valores de finish_reason están mapeados como toca para que los clientes de OpenAI vean stop/length/tool_calls en vez de basura, las conversaciones de varios turnos se quedan correctamente en el mismo workspace entre continuaciones, y los campos de petición no soportados devuelven ahora 400 en vez de tirarse en silencio. Respaldado por 24 tests unitarios y 3 tests de integración para que la superficie siga siendo honesta según crece.

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_run en 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.

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, prompt de sistema y presupuesto 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"
    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

Comandos 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: low, medium, high, xhigh, max, reset
  • /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. Los bytes NUL de la salida de herramientas se mapean a un sustituto del área de uso privado para que sobrevivan al viaje de ida y vuelta a Telegram sin truncar el mensaje.

Modo Cron

Pon CLAUDEBOX_CRON_MODE=1 y apunta CLAUDEBOX_CRON_MODE_FILE a un fichero YAML para ejecutar trabajos de Claude programados. Solo croniter de seis campos desde la v2.0.0, */30 * * * * * se dispara cada 30 segundos. Las configuraciones de antes de la v2 escritas con entradas de cinco campos necesitan un 0 delante.

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). Los model, system_prompt y append_system_prompt por trabajo sobreescriben los valores por defecto de la raíz.

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

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 ~/.claude/cron/history/<workspace-slug>/<timestamp>-<job-name>/ como activity.jsonl, stderr.log y meta.json.
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). 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, la raíz del historial, el directorio de historial del workspace y el directorio de historial por trabajo (~/.claude/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 sola vez al crear el container por primera vez, como root, antes de bajar al usuario claude. No se vuelven a ejecutar en los docker start siguientes, solo en containers nuevos. Úsalos para instalaciones de paquetes extra o para setup de una sola vez:

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

Scripts Propios

Suelta ejecutables en ~/.claude/bin/ en el host y están en el PATH dentro de cada container. 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 "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 "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"

El Modelo de Workspace

claudebox crea dos containers por workspace: claude-<path> para sesiones interactivas y claude-<path>_prog para ejecuciones programáticas. No comparten estado y pueden correr a la vez. 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@psyb0t

Codex 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.