claudebox: Claude Code în Docker, Acum Cu Șapte Feluri de a-ți Distruge Serverul de Producție

Atenție, postarea asta a fost depășită. De la v2.0.0, claudebox e o imagine copil subțire a lui aicodebox, baza agnostică la agent care deține acum fiecare suprafață descrisă mai jos. API-ul, endpointul compatibil cu OpenAI, serverul MCP, botul de Telegram și schedulerul de cron stau toate în bază și sunt împărțite cu pibox și codexbox. claudebox în sine e acum adaptorul de Claude Code plus un wrapper pe partea de host. Citește întâi postarea despre aicodebox, acolo stă de fapt arhitectura. Ce e mai jos încă merge și documentează suprafața proprie a lui claudebox, dar baza e povestea adevărată acum.

MCP e propriul lui mod, nu un colț al API-ului

Merită spus explicit, pentru că documentația l-a descris multă vreme ca pe un endpoint dinăuntrul modului API, iar asta îl subestimează. MCP rulează în două feluri:

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

Varianta de sine stătătoare coexistă cu orice alt mod în loc să îl înlocuiască și își ia propriul token, CLAUDEBOX_MCP_MODE_TOKEN, care nu cade înapoi pe tokenul de API. Îl lași gol și suprafața MCP nu are autentificare deloc, ceea ce e un default diferit și mult mai prost decât „moștenește ce folosește API-ul”. Setează-l deliberat.
Cinci unelte: run_prompt plus patru unelte de fișiere, list_files, read_file, write_file, delete_file, fiecare rezolvându-și path-ul sub rădăcina workspace-ului și respingând orice iese în afară. Preferă-le în loc să îndeși o încărcătură în prompt: agentul poate citi singur workspace-ul, deci „scrie inputul într-un fișier și spune-i care fișier” bate un prompt de 50 KB.


Folosesc Claude Code pentru tot. Scris de cod, depanat căcaturi, deployat infrastructură, administrat repo-uri, scris articole chiar pentru blogul ăsta și chiar automatizat sesiuni de browser din mers. A devenit coloana vertebrală a felului în care lucrez. Dar să dai unui agent AI acces complet la sistemul tău e al dracului de înfricoșător, nu pentru că ar fi Claude răuvoitor, ci pentru că rulează cu --permission-mode bypassPermissions și are puterea să facă ce vrea. O comandă proastă și host-ul tău e praf. În zilele astea containerul în care rulează nici măcar nu mai e specific lui claudebox, e aicodebox cu un adaptor în formă de Claude înșurubat pe el.
Răspunsul evident e: bagă-l într-un container. Dar să faci asta cum trebuie e o cu totul altă problemă. Am construit claudebox ca să o rezolv. Ce a început ca un simplu wrapper containerizat peste Claude Code a crescut în șapte feluri diferite de a rula Claude, fiecare chiar folositor, niciunul de umplutură.

Redenumirea

Chestia asta se numea înainte docker-claude-code, imaginea psyb0t/claude-code, binarul claude. Acum e claudebox, imaginea psyb0t/claudebox, binarul claudebox. Cheile SSH s-au mutat de la ~/.ssh/claude-code la ~/.ssh/claudebox.
Dacă faci upgrade: dezinstalezi binarul vechi, tragi imaginea nouă, rulezi din nou scriptul de instalare. Directorul tău de config ~/.claude și istoricul de sesiuni supraviețuiesc redenumirii neatinse.

v2.0.0, Rebazat pe aicodebox

Schimbarea mai mare a venit după redenumire. claudebox e acum o imagine copil subțire a lui psyb0t/aicodebox, o bază comună, agnostică la agent, care se ocupă de fiecare suprafață de mod. Același tipar ca psyb0t/pibox și psyb0t/codexbox. Serverul de API, botul de Telegram, schedulerul de cron și endpointul MCP stau toate în bază acum; claudebox contribuie cu un adaptor care știe să vorbească anume cu Claude Code. Reparațiile din bază ajung gratis în fiecare imagine copil.
Ăsta e un rebase arhitectural complet, deci a rupt lucruri. Unele sunt aliasate sau legate simbolic mai departe, multe nu, așa că treci prin lista asta înainte să faci upgrade:

  • Endpointuri: POST /run/cancel?runId=… a devenit DELETE /run/{run_id}. GET /health a devenit GET /healthz.
  • Unealta MCP: claude_run a devenit run_prompt. Actualizează-ți configurațiile de client MCP.
  • Variabile de mediu: CLAUDEBOX_MODE_API a devenit CLAUDEBOX_API_MODE, CLAUDEBOX_MODE_CRON_FILE a devenit CLAUDEBOX_CRON_MODE_FILE și tot așa pe listă. De la v2.5.0 entrypointul mapează pe numele noi și cele șase denumiri v1 CLAUDEBOX_MODE_* (API, portul și tokenul lui, Telegram, cron, fișierul de cron), pe lângă cele și mai vechi CLAUDE_MODE_*, CLAUDE_WORKSPACE și cele două CLAUDE_TELEGRAM_*, iar dacă le ai setate pe amândouă, câștigă numele canonic. CLAUDEBOX_TELEGRAM_BOT_TOKEN și CLAUDEBOX_TELEGRAM_CONFIG din v1 nu sunt pe listă, așa că pe alea le redenumești de mână.
  • Căi: rădăcina de workspace /workspaces a devenit /workspace (la singular). Home-ul din container /home/claude/.claude a devenit /home/aicode/.aicodebox. Legăturile simbolice de compatibilitate acoperă /workspaces și /home/aicode/.claude, dar spre /home/claude nu mai duce nimic, așa că mută-ți ținta bind mounturilor vechi.
  • Cron: n-ai nimic de redenumit, orice ar zice notele de release de la v2.0.0. Programele pe cinci câmpuri merg în continuare ca un cron normal, la minut. Al șaselea câmp e opțional, vine primul și numără secundele.
  • Dispărut: comanda /bash din Telegram. Reimplementeaz-o pe partea de client dacă ai nevoie de ea.
  • Varianta full: make build-full nu mai e o țintă multi-stage separată. Din v2.3.12 construiește direct pe imaginea full a lui aicodebox și pune deasupra doar stratul de Claude.

Adaptorul vine cu 35 de teste unitare pytest, plus 9 teste de fum containerizate care rulează pe un binar claude simulat, healthz, lista de modele OpenAI, markerele init.d, legăturile simbolice de compatibilitate, aliasarea de variabile de mediu, injecția always-skills, argumentele în plus și modul de permisiuni implicit.

Șapte Interfețe, Un Singur Container

claudebox nu mai e doar un wrapper. Sunt șapte interfețe diferite spre Claude Code care rulează înăuntrul Docker:

  • CLI interactiv: container persistent, reluare de sesiune, modul original
  • CLI programatic: neinteractiv, merge din scripturi și din CI, cu propriul container dedicat
  • Server HTTP API: API REST cu administrare de workspace-uri, operații pe fișiere, rulări sincrone și asincrone
  • Endpoint compatibil cu OpenAI: înlocuitor direct la /openai/v1/chat/completions cu streaming
  • Server MCP: cinci unelte pe care Claude le poate folosi din alți agenți prin Model Context Protocol
  • Bot de Telegram: workspace-uri per chat, partajare de fișiere, prompturi către Claude direct de pe telefon
  • Scheduler de cron: joburi programate definite în YAML, cu rezoluție sub minut, istoric per job și notificări opționale pe Telegram

Instalare

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

Asta generează chei SSH la ~/.ssh/claudebox, trage imaginea (mereu, rerularea instalatorului e modul suportat de a face upgrade) și lasă binarul claudebox la /usr/local/bin/claudebox. Dacă trebuie să dai variabile de mediu instalatorului, exportă-le întâi pe o linie separată, pentru că dacă dai VAR=x curl ... | bash prin pipe nu se transmite variabila către script:

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

Apoi:

claudebox

Prima rulare îți cere autentificare. După aia merge pur și simplu. Wrapper-ul se ocupă de tot ciclul de viață al containerului, creează unul nou dacă nu există deja pentru directorul curent, îl repornește și se reatașează dacă există.

Variante de Imagine

Full: psyb0t/claudebox:latest-full
Bază Ubuntu încărcată cu tot ce îi trebuie cu adevărat unui dezvoltator: Go cu tot toolchainul (golangci-lint, gopls, delve), Python 3.14 (flake8, black, mypy, pyright, vulture, pytest, poetry), Node.js 24 LTS cu ecosistemul obișnuit, toolchain de C/C++, Docker CE cu Compose, Terraform, kubectl, helm, GitHub CLI, clienți de baze de date pentru SQLite/PostgreSQL/MySQL/Redis și un morman de utilitare (jq, ripgrep, fd-find, bat, shellcheck, shfmt, httpie). Fiecare container nou îți pune în workspace un CLAUDE.md cu toolchainul instalat, ca să știe Claude cu ce are de lucru, și nu se atinge de al tău dacă proiectul are deja unul.
Minimală: psyb0t/claudebox:latest
Doar strictul necesar peste baza aicodebox: Ubuntu 24.04, git/curl/wget/jq, Node.js 24 LTS cu npm, Python 3.14 cu uv, Docker CE. Imagine mai mică, tras mai rapid. Claude are sudo fără parolă, așa că își instalează din mers ce îi trebuie. Ține minte că denumirea s-a inversat în v2: latest e acum imaginea minimală (înainte era cea full), latest-full e buildul cu toolchain, iar vechiul opt-in CLAUDEBOX_MINIMAL=1 nu mai face nimic, pentru că minimala e implicită. CLAUDEBOX_FULL=1 e felul în care optezi în sens invers, iar dacă instalezi cu el setat se coace alegerea aia în wrapper, ca să rămână. Folosește init hooks ca să-ți scriptezi setup-ul, așa încât fiecare container proaspăt își instalează singur ce-ți trebuie, în loc să se apuce Claude de asta de mână în mijlocul sesiunii.
Claude Code în sine nu mai e în niciuna dintre imagini, iar motivul e licențierea. CLI-ul de la Anthropic e proprietar și fără drept de redistribuire, deci să publici o imagine cu el copt înăuntru ar însemna să livrezi softul altcuiva. În schimb imaginea cară versiunea fixată în CLAUDEBOX_CLAUDE_VERSION, iar entrypointul rulează npm install -g @anthropic-ai/claude-code@<version> prima dată când pornește un container proaspăt. Nimic de-al lui Anthropic nu călătorește în straturile publicate; fiecare container îl aduce singur de la npm. Costul e că prima pornire a unui container nou are nevoie de rețea și de câteva secunde în plus. Repornirile calde o sar complet. Vrei altă versiune, setezi CLAUDEBOX_CLAUDE_VERSION la docker run.

Cutiile se pot lansa una pe alta acum

Instalează claudebox, codexbox și pibox în același director și fiecare wrapper le montează pe toate trei doar în citire, inclusiv pe el însuși, la /usr/local/bin/<name>. Adică Claude, din interiorul propriului container, poate da shell la codexbox sau pibox și poate pune alt agent să facă o bucată de treabă. Utilizarea evidentă e o a doua părere despre un diff fără să ieși din sesiune.
Partea care a cerut gândire reală e contextul. O rulare imbricată trebuie să știe unde stau lucrurile pe host, nu înăuntrul containerului care se întâmplă să cheme, așa că există un bloc versionat pentru asta: AICODEBOX_LAUNCH_CONTEXT_VERSION=1 plus AICODEBOX_HOST_HOME, AICODEBOX_HOST_WORKSPACE, AICODEBOX_HOST_WRAPPER_DIR și, per agent, AICODEBOX_HOST_CLAUDE_HOME / CODEX_HOME / PI_HOME, cu câte un *_WRAPPER potrivit pentru fiecare. Versionat pentru că forma se va schimba și o lansare imbricată ar trebui să își poată da seama.
Rulările imbricate sunt și deliberat de unică folosință: sar peste scrierile de fișiere de autentificare pe care le face o rulare de nivel superior, deci un agent copil nu poate rescrie pe tăcute credențialele cutiei care l-a chemat.
Pe lângă asta, AICODEBOX_ENV_* și AICODEBOX_MOUNT_* transmit variabile de mediu și mounturi spre fiecare cutie, lângă CLAUDEBOX_ENV_* și CLAUDEBOX_MOUNT_* existente, care sunt per cutie. Iar AICODEBOX_MANAGED_INSTALL=1 e o instalare neinteractivă pentru scripturi de provisioning care, important, refuză să calce peste o cheie SSH care există deja. CLAUDEBOX_INSTALL_DIR și CLAUDEBOX_BIN_NAME controlează unde aterizează și cum se numește.
Un detaliu de lanț de aprovizionare care merită menționat: instalatorul trage acum wrapper.sh din tagul de release imutabil potrivit, nu din master, deci dacă instalezi o versiune fixată chiar primești wrapper-ul versiunii ăleia.

Modul Interactiv

Rulezi claudebox din orice director și primești o sesiune vie. Containerul persistă între rulări, sesiunea continuă de unde ai lăsat-o. Fiecare workspace primește propriul container, numit după calea directorului.
Comenzi utilitare:

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 run

Continuitatea sesiunii. claudebox rulează Claude cu --continue, deci reia ultima conversație din directorul curent. Omori terminalul, te întorci a doua zi, îl pornești iar și Claude prinde exact de unde a rămas. Fără sesiune, nicio problemă, pornește de la zero.
Limita de memorie. Containerele sunt plafonate la 10g implicit. Suprascrii la lansare cu CLAUDEBOX_MAX_MEM=16g claudebox (vechiul CLAUDE_MAX_MEM încă merge).
Potrivirea UID/GID. Entrypointul detectează proprietarul workspace-ului și ajustează utilizatorul containerului ca să se potrivească. Fișierele create înăuntrul containerului au proprietarul corect pe host. Fără căcaturi cu chown -R.

Modul Programatic

Îi dai un prompt cu -p și claudebox rulează neinteractiv. -p nu e opțional: fără el, wrapperul îți aruncă promptul înapoi ca pe o comandă necunoscută. Folosește un container _prog dedicat per workspace, separat de cel interactiv, fără TTY, merge din scripturi, din cron, din alte unelte:

# 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-def456

Aliasuri de modele: opus (Opus 4.6), sonnet (Sonnet 4.6), haiku (Haiku 4.5), opusplan (Opus pentru planificare plus Sonnet pentru execuție), sonnet[1m] (Sonnet cu fereastră de context de 1M). Sau dai numele complet al unui model ca să fixezi o versiune anume.
Formate de ieșire: text (implicit), json (un singur obiect rezultat, cu costul și defalcarea pe tokeni), stream-json (NDJSON, un eveniment pe linie, init de sistem, răspunsuri de asistent, folosire de unelte, rezultate de unelte, evenimente de rate limit, rezultatul final). Vechiul json-verbose e mort: wrapperul din v2 îl refuză și te trimite la modul API, unde POST /run cu "eventMode": "full" îți dă în schimb fiecare înregistrare.

Modul API

Setezi CLAUDEBOX_API_MODE=1 ca să rulezi containerul ca server HTTP API. Îl bagi într-un stack docker-compose și alte servicii pot vorbi cu Claude peste 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

Endpointuri:

  • POST /run: trimiți un prompt, primești un rezultat. Câmpuri: prompt, workspace, model, system_prompt, append_system_prompt, json_schema, no_continue, resume, thinking (efortul de raționament, trimis lui Claude Code ca --effort). Întoarce 409 dacă workspace-ul procesează deja.
  • POST /run cu "async": true: întoarce imediat un runId. Interoghezi GET /run/result?runId=X până se termină. Tragi și uiți.
  • DELETE /run/{run_id}: omoară un proces care rulează (era POST /run/cancel?runId=… înainte de v2)
  • GET /files/{path}: listează un director sau descarcă un fișier
  • PUT /files/{path}: încarcă un fișier (directoarele părinte se creează automat)
  • DELETE /files/{path}: șterge un fișier
  • GET /healthz: verificare de sănătate, fără autentificare (era GET /health înainte de v2)
  • GET /status: arată ce workspace-uri sunt ocupate acum

Toate căile sunt relative la /workspace. Autentificare prin bearer token în headerul Authorization, setezi CLAUDEBOX_API_MODE_TOKEN ca să o activezi. Urmărirea workspace-urilor ocupate întoarce 409 Conflict, ca să nu îngrămădești din greșeală rulări suprapuse pe același workspace.

Ieșirea structurată și înregistrarea completă sunt butoane separate acum

Două lucruri s-au schimbat în API, lucruri care erau încurcate unul în altul. jsonSchema trece acum prin flagul propriu --json-schema al lui Claude Code în loc să fie lipit pe deasupra, iar reținerea evenimentelor e propriul ei control: eventMode: "full" îți dă înapoi fiecare înregistrare nativă stream-json, cu tot cu mesaje parțiale, text de la subagenți și evenimente de hook, fără să comprime ture și fără să trunchieze rezultate de unelte. Adaptorul îi cere acum lui Claude Code cele trei streamuri la fiecare rulare, deci reglajul hotărăște doar ce primești înapoi. Să vrei transcrierea întreagă nu mai cere să te prefaci că voiai o schemă, iar să ceri o schemă nu îți mai bagă pe gât tot furtunul. Alegi fiecare separat.

Endpoint Compatibil cu OpenAI

POST /openai/v1/chat/completions, un adaptor OpenAI de înlocuire directă care rutează cererile spre Claude Code din container. Merge cu orice vorbește API-ul OpenAI: LiteLLM, Open WebUI, clienți proprii, orice.

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
  }'

Streamingul merge prin Server-Sent Events. Conversațiile pe mai multe ture merg, dai tot istoricul de mesaje și Claude ține contextul. Merge și multimodalul, trimiți conținut de imagine în base64 în mesaj și Claude îl poate vedea. Vrei și măruntaiele? Pune "stream_options": {"include_aicodebox_events": true} într-un request cu streaming și primești, pe lângă chunk-urile normale, evenimente SSE numite aicodebox.native cu înregistrările brute ale lui Claude (thinking, apeluri de tool-uri, diagnostice), iar chunk-urile standard și [DONE] rămân exact cum erau. Trimite reasoning_effort și ajunge și el la Claude Code ca --effort: minimal devine low, none lasă valoarea implicită în pace, iar orice e în afara scalei primește o eroare în loc să fie ignorat pe tăcute.
Headere proprii pentru controlul comportamentului:

  • X-Claude-Workspace: în ce workspace să ruleze
  • X-Claude-Continue: dacă să continue sesiunea anterioară
  • X-Claude-Append-System-Prompt: adaugă instrucțiuni suplimentare la promptul de sistem

Pentru LiteLLM, îl îndrepți spre http://your-host:8080/openai/v1 ca provider OpenAI personalizat și merge fără nicio configurație specială.
Tură de întărire. Adaptorul OpenAI a primit o suită de teste adevărată și un audit de securitate: o gardă anti-SSRF aruncă URL-urile de imagine care se rezolvă la adrese private sau de loopback, finish_reason vine înapoi ca stop sau tool_calls în loc de motivele brute de oprire ale lui Claude (un stream care moare sau expiră se termină pe error sau timeout), conversațiile pe mai multe ture rămân corect pe același workspace la continuări, iar un response_format sau un header de control stricat primește 400. Câmpurile de cerere de care n-are ce face, gen temperature, sunt în continuare ignorate, nu refuzate. A venit cu 24 de teste unitare și 3 teste de integrare, doar că testele unitare au fost scrise pe fișierul de server de dinainte de v2, pe care v2 l-a șters, așa că nici măcar nu se mai importă. Acoperirea reală a endpointului stă acum în suita de teste a lui aicodebox, lângă cod.

Server MCP

Activează serverul MCP la /mcp/ ca să lași alți agenți și alte unelte să cheme în containerul tău de Claude prin Model Context Protocol. Cinci unelte expuse:

  • run_prompt: rulează un prompt într-un workspace și îți dă rezultatul înapoi (redenumită din claude_run în v2.0.0)
  • list_files: listează fișierele dintr-un director de workspace
  • read_file: citește un fișier dintr-un workspace
  • write_file: scrie un fișier într-un workspace
  • delete_file: șterge un fișier dintr-un workspace

Asta înseamnă că alte instanțe de Claude, agenți proprii sau orice client compatibil MCP pot folosi instanța ta de claudebox ca pe o unealtă, delegând muncă unei sesiuni proaspete de Claude cu acces complet la fișiere. Cât despre căi: în modul API calea canonică e /mcp/, varianta fără slash /mcp ajunge la același handler fără redirect, iar sidecar-ul standalone de pe 8081 servește direct din rădăcina portului, /. O capcană: garda anti-DNS-rebinding din SDK-ul MCP rămâne pornită și, din fabrică, acceptă doar un header Host cu localhost, 127.0.0.1 sau [::1], deci un client de pe aceeași mașină merge, dar alt container, un reverse proxy sau orice altceva care apelează endpoint-ul după hostname își ia un 421 în cap (un client din browser cu un Origin nelistat încasează un 403). De la v2.4.7 rezolvi asta cu două variabile de mediu, CLAUDEBOX_MCP_MODE_ALLOWED_HOSTS și CLAUDEBOX_MCP_MODE_ALLOWED_ORIGINS, cu valori exacte separate prin virgulă. Dacă setezi una, înlocuiește default-ul de loopback în loc să se adauge la el, așa că pune localhost și gașca lui înapoi în listă dacă încă ai nevoie de ele.

Modul Telegram

Setezi CLAUDEBOX_TELEGRAM_MODE=1 și primești un bot de Telegram care vorbește cu Claude. Fiecare chat primește propriul workspace și propriile setări. Trimiți text, fișiere, poze, clipuri, mesaje vocale. Primești fișiere înapoi cu /fetch.
Configurarea stă într-un fișier YAML, cu model, effort, workspace și prompt de sistem per 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:
      - 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

Comenzile botului:

  • orice text → trimis lui Claude ca prompt
  • trimiți un fișier/poză/clip/voce → salvat în workspace, iar descrierea devine promptul
  • /model [name]: arată modelul curent cu butoane selectabile sau îl setează direct: haiku, sonnet, opus, opusplan, reset
  • /effort [level]: arată sau selectează efortul: off, low, medium, high, xhigh, max, reset. Alegerea rămâne salvată per chat și ajunge la Claude Code ca --effort, deci un chat pus pe max chiar gândește mai mult, nu de-al pulii.
  • /system_prompt [text]: arată, setează sau resetează suprascrierea de prompt de sistem pentru chatul ăsta
  • /append_system_prompt [text]: la fel, pentru promptul de sistem adăugat
  • /fetch <path>: trimite înapoi un fișier din workspace ca atașament de Telegram
  • /cancel: omoară procesul Claude care rulează pentru chatul ăsta
  • /status: arată ce chaturi au acum procese care rulează
  • /config: afișează configurația curentă a chatului ăstuia
  • /reload: reîncarcă la cald configurația YAML fără să repornești containerul

Claude poate împinge fișiere înapoi punând [SEND_FILE: path] în răspunsul lui. Imaginile vin ca poze, clipurile ca video, tot restul ca documente. Răspunsurile lungi se despart automat în mai multe mesaje.
Randarea de Markdown. Botul traduce ieșirea markdown a lui Claude în dialectul HTML al lui Telegram înainte să o trimită, iar bold, italic, cod inline, blocuri de cod, citate, titluri, liste și linkuri se randează toate nativ în chat. Gata cu **asteriscurile brute și accentele grave care îți poluează mesajele.

Modul Cron

Setezi CLAUDEBOX_CRON_MODE=1 și îndrepți CLAUDEBOX_CRON_MODE_FILE spre un fișier YAML ca să rulezi joburi Claude programate. Pentru programe sub un minut adaugi un al șaselea câmp, primul, care numără secundele: */30 * * * * * se declanșează la fiecare 30 de secunde. Intrările simple pe cinci câmpuri merg în continuare ca un cron normal, la minut, deci configurațiile de dinainte de v2 funcționează așa cum sunt.

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.

Variabile de șablon disponibile în instruction, system_prompt și append_system_prompt: {system_datetime} (data și ora UTC curente) și {job_name} (câmpul de nume al jobului). Per job, model, system_prompt, append_system_prompt, effort, thinking și telegram_chat_id suprascriu valorile implicite din rădăcină, iar telegram_chat_id: 0 scoate un singur job din chatul de la rădăcină, pentru porcăria aia care rulează la fiecare 30 de secunde și pe care nimeni nu vrea s-o audă bâzâind în telefon. Două câmpuri există doar per job: workspace îl rulează într-un subdirector relativ din CLAUDEBOX_WORKSPACE (căile absolute și evadările cu .. sunt respinse), iar no_continue: true îl pornește de la zero în loc să preia cea mai recentă sesiune Claude din directorul ăla.

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

Ți-e lene de compose? Din directorul proiectului, CLAUDEBOX_CRON_MODE=1 CLAUDEBOX_CRON_MODE_FILE=$PWD/cron.yaml claudebox pornește un container detașat claude-<path>_cron pentru workspace-ul ăla, iar CLAUDEBOX_CRON_MODE=1 claudebox stop îl omoară. Scurtătura asta a fost moartă aproape tot v2-ul, pentru că wrapperul trimitea un nume de variabilă pe care entrypointul din v2 nu-l citea niciodată, deci containerul pornea și cronul nu se aprindea deloc. v2.5.0 a reparat-o. Schedulerul rulează în prim-plan, iar docker logs arată fiecare tic. Dacă un job încă rulează când se declanșează ticul următor, ticul ăla se sare. Istoricul jobului curge spre ~/.aicodebox/cron/history/<workspace-slug>/<timestamp>-<job-name>/ sub formă de meta.json, stdout.log, stderr.log și result.txt, plus telegram.json când a plecat o notificare. Fiecare rulare mai adaugă și un rezumat de o linie în ~/.aicodebox/cron/<job-name>.jsonl.
Setezi telegram_chat_id (în rădăcină sau per job) plus CLAUDEBOX_TELEGRAM_MODE_TOKEN ca să îți ajungă rezultatul lui Claude pe Telegram după ce se termină fiecare job. Botul de Telegram nu trebuie să ruleze, cronul folosește tokenul direct.
Efort de raționament per job. Setezi effort în rădăcină pentru o valoare implicită și suprascrii per job, aceeași scară ca la CLI (low, medium, high, xhigh, max). Valoarea ajunge la Claude Code ca --effort, iar un nivel necunoscut e respins cu eroare, în loc să ruleze pe tăcute pe efortul implicit. Modele ieftine pentru joburi ieftine, efort maxim pentru auditul nocturn nasol.
Istoricul rulărilor anterioare, injectat automat. Fiecare tic de cron adaugă acum un bloc de sistem care îi spune lui Claude unde stau rulările lui anterioare: directorul celei mai recente rulări, plus un glob pentru toate cele mai vechi (~/.aicodebox/cron/history/<workspace-slug>/*-<job-name>/). Claude nu le citește lacom; primește căile și decide dacă face Glob sau Read atunci când jobul chiar cere analiză de tendințe sau detectare de regresii. Prima rulare din istorie sare peste indiciu, pentru că încă nu există nimic. Asta deblochează „compară cu săptămâna trecută”, „a regresat metrica asta”, „ce s-a schimbat de la rularea de ieri”, fără să cablezi nimic din toate astea per job. Combinat cu telegram_chat_id primești un agent de rezumat zilnic care chiar știe ce a zis ieri.

Personalizare

Skilluri Mereu Active

Pui fișiere SKILL.md în ~/.claude/.always-skills/ și se injectează automat în fiecare invocare de claudebox, interactivă, programatică, API, Telegram, cron, toate. Context persistent care îl urmează pe Claude în fiecare sesiune, fără să atingi fișierele CLAUDE.md ale proiectelor individuale.

Hookuri de Init

Scripturile din ~/.claude/init.d/*.sh rulează o dată per container, la prima lui pornire, imediat după scripturile de init ale imaginii. Merg în ordinea numelor de fișier, ca aicode, care are sudo fără parolă, așa că tot ce cere root primește un sudo în față. Un docker start nu le mai rulează, dar fiecare container proaspăt da, deci fă-le să suporte rularea repetată. Dacă unul crapă, se loghează și restul rulează mai departe. Folosește-le pentru instalări de pachete în plus sau pentru setup per 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.sh

Scripturi Proprii

Pui executabile în ~/.claude/bin/ pe host și sunt în PATH înăuntrul fiecărui container, în orice mod, în init hooks și în shell-urile de docker exec. Persistă în toate sesiunile, în toate workspace-urile.

Transmiterea Variabilelor de Mediu

Folosește prefixul CLAUDEBOX_ENV_ ca să dai variabile de mediu arbitrare în container, iar prefixul se taie:

CLAUDEBOX_ENV_GITHUB_TOKEN=xxx CLAUDEBOX_ENV_MY_VAR=hello claudebox -p "do stuff"

Mounturi de Volum Suplimentare

Prefixul CLAUDEBOX_MOUNT_ ca să montezi directoare suplimentare:

# 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"

Modelul de Workspace

claudebox creează două containere per workspace: claude-<path> pentru sesiuni interactive și claude-<path>_prog pentru rulări programatice. Pot rula simultan, dar împart ~/.claude, cu tot cu istoricul sesiunilor, așa că o rulare cu -p preia ultima conversație din directorul ăla, dacă nu-i dai --no-continue. Sesiunea interactivă nu îți blochează scripturile. Scripturile nu îți întrerup sesiunea.
Directorul ~/.claude se montează în fiecare container, iar configurația, cheile de API, skillurile mereu active, hookurile de init și scripturile proprii sunt toate împărțite între workspace-uri. Cheile SSH din ~/.ssh/claudebox se montează automat. Izolarea e la nivel de workspace, identitatea e comună.
Socketul Docker se montează și el, ca să poată Claude construi imagini, ridica stackuri de compose și administra containere din interiorul propriului container. Pentru că workspace-ul se montează pe calea lui reală de pe host ($PWD:$PWD), mounturile de volum din interiorul lui Claude se rezolvă corect pe host. Claude scrie un docker-compose.yml, îl rulează, iar căile merg.

Își Livrează Propriul Skill și Plugin

Repo-ul cară .agents/skills/claudebox/, un skill de agent care documentează fiecare mod pe care îl expune cutia, așa că un asistent pe care îl îndrepți spre el știe deja shellul interactiv, execuția dintr-un foc, API-ul REST, endpointul compatibil cu OpenAI, serverul MCP, botul de Telegram și schedulerul de cron, fără să îi explici nimic.
Lângă el, @psyb0t/claudebox la .agents/plugins/claudebox/, o punte MCP stdio↔HTTP peste mcp-remote, ca să poată un agent OpenClaw sau MCP să conducă direct endpointul /mcp al unei cutii care rulează. Licențiat MIT. CI le publică pe amândouă pe ClawHub la push-uri de tag.

Modelul de Securitate

Chestia asta rulează cu --permission-mode bypassPermissions, echivalentul modern al vechiului --dangerously-skip-permissions. Claude are sudo fără parolă înăuntrul containerului și poate face ce vrea.
Granița de securitate e containerul. Claude nu îți poate atinge sistemul de fișiere al host-ului dincolo de workspace-ul montat și de configul ~/.claude. Dacă o ia razna, docker stop și docker rm și s-a dus. Ridici unul proaspăt în câteva secunde.
Montarea socketului Docker e excepția, pentru că dă acces la daemonul Docker al host-ului. Nu îl monta dacă te îngrijorează asta. Tot restul e ținut în frâu.
Cheile SSH stau în ~/.ssh/claudebox, o pereche de chei dedicată, generată în timpul instalării. Cheile tale personale nu intră niciodată în container. Configurezi ce cheie se folosește prin CLAUDEBOX_SSH_DIR, dacă e nevoie.
Lanțul de aprovizionare. Imaginea de bază e fixată pe digest @sha256:, nu doar pe un tag, pentru că tagurile sunt mutabile, iar un digest e adresat prin conținut, deci o reconstrucție nu poate trage pe tăcute alți octeți de bază. În imaginea full, arhiva de Go se descarcă pe disc și se verifică cu sha256sum -c pe o sumă de control per arhitectură înainte de extragere, în loc de vechea țeavă neverificată curl … | tar. O descărcare falsificată sau trunchiată pică buildul în loc să aterizeze în imagine.
Autentificarea modului API se face prin bearer token în headerul Authorization. Setezi CLAUDEBOX_API_MODE_TOKEN. Dacă nu îl setezi, API-ul rulează neautentificat, ceea ce e în regulă pentru uz local și o idee proastă expus pe o rețea.

Pe Scurt

Rulez acum fiecare sesiune de Claude înăuntrul lui claudebox. Șapte moduri, zero poluare a host-ului. Izolarea înseamnă că nu stau pe gânduri când îl las să instaleze pachete, să rescrie configurații sau să ruleze orice comenzi îi trebuie ca să ducă treaba la capăt. Continuitatea sesiunii înseamnă că închid terminalul, mă întorc peste câteva ore și prind exact de unde am rămas. API-ul și endpointul OpenAI înseamnă că pot lega Claude de alte servicii fără să scriu cod de lipit. Botul de Telegram înseamnă că pot porni o sarcină de pe telefon când sunt departe de birou. Schedulerul de cron înseamnă că Claude muncește în timp ce eu dorm.
Ia-l de aici: github.com/psyb0t/docker-claudebox
Licențiat sub WTFPL, pentru că singurul lucru mai periculos decât un AI cu acces de root e un AI cu acces de root și o licență restrictivă.

Cum Îl Instalezi în Agentul Tău

Claude Code care rulează Claude Code într-o cutie, iar acum instalabil chiar din Claude Code, ceea ce e cam atât de recursiv pe cât vreau să ajung. Tot ce e sub .agents/ e catalogat într-un singur marketplace, deci sunt două comenzi:

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

Codex folosește același marketplace cu alt verb, codex plugin add claudebox@psyb0t, pentru că nu există codex plugin install. Găsește singur și skillul într-un checkout al repo-ului, pentru că scanează .agents/skills/ nativ, fără să fie instalat absolut nimic. E listat acum și pe MCP Registry-ul oficial, deci un client care rezolvă servere de acolo îl poate găsi fără să i se dea un URL.