Sandbox-uri
Workspace-uri gVisor izolate în care agenții rulează Python, bash și LibreOffice și predau fișierele ca artefacte scanate și semnate.
Un sandbox este un container izolat cu un director /workspace persistent. Codul tău sau agentul tău rulează comenzi în el (Python 3, bash, LibreOffice, Node 24, bibliotecile uzuale pentru date și documente, fonturi cu acoperire completă pentru limba română), scrie rezultatele în /workspace/out și le primește înapoi ca artefacte: scanate, cu hash și descărcabile printr-un link semnat. Workspace-ul se păstrează între execuții până când sandbox-ul este distrus sau expiră.
Două backend-uri folosesc același API. v2 este implicit din 18 octombrie 2026: un pod de lungă durată cu snapshot-uri de memorie (pauză/reluare, fork, kernel Python persistent, URL-uri de previzualizare), vezi Sandbox-uri v2. v1 (Cloud Run, workspace restaurat la fiecare exec, plus POST /snapshot) rămâne disponibil cu backend: "v1". Dacă omiți backend și v2 nu este disponibil pentru cheia ta, gateway-ul trece pe v1; câmpul backend din fiecare obiect sandbox îți spune ce ai primit.
Fiecare execuție este contorizată; timpul de exec se plătește ulterior din portofel (vezi Facturare), de aceea răspunsurile au x-codai-cost-micro-usd: 0. Fiecare rută cere o cheie codai_ care atinge nivelul de unelte l3_sandbox.
Modelul de securitate
- Izolare gVisor. Fiecare sandbox rulează sub gVisor, cu propria graniță de kernel: v2 ca pod dedicat pe GKE, v1 pe Cloud Run (gen2). Sistemul de fișiere al imaginii este doar-citire pentru codul tău și doar
/workspacepoate fi scris. - Fără rețea implicit. Cu
egress: { mode: 'none' }(implicit) nu există nicio rută spre exterior: IP-urile publice, rețelele private și serverul de metadate sunt inaccesibile. - Egress printr-un proxy cu listă de domenii permise.
{ mode: 'allowlist', domains: [...] }(cel mult 20 de nume de gazde, fără adrese IP, fără sufixe interne) sau{ mode: 'preset', preset: 'pypi' | 'npm' }permite HTTPS doar către acele gazde, prin proxy-ul de egress codai, care rezolvă singur DNS-ul și jurnalizează fiecare conexiune. Funcționează pe ambele backend-uri; proxy-ul deschide tuneluri simple și nu injectează niciodată credențiale. - Niciun secret înăuntru. În sandbox nu există nicio cheie codai, credențială cloud sau token; variabilele de mediu numite
CODAI_*sauGOOGLE_*sunt respinse. - Auditat. Fiecare creare, execuție, transfer de fișier, artefact, snapshot și distrugere este scrisă într-un jurnal de audit append-only; fiecare execuție returnează o chitanță de reproductibilitate.
- Datele rămân în
europe-west1(x-codai-data-region).
Limite
LibreOffice convertește toate formatele office în sandbox, de exemplu soffice --headless --convert-to pdf --outdir /workspace/out raport.docx (și .xlsx, .pptx, .odt, HTML, CSV). Fiecare comandă rulează sub un init mic care preia procesele ajutătoare din fundal, iar exit_code este codul real de ieșire al comenzii tale.
| Limită | Valoare implicită | Eroare |
|---|---|---|
| Sandbox-uri pornite per cheie | 3 | 429 sandbox_concurrency_limit |
| Sandbox-uri v2 pornite per utilizator | 5 (toate cheile la un loc) | 429 sandbox_concurrency_limit |
| Sandbox-uri v2 pe pauză per utilizator | 10 | 429 sandbox_paused_limit (la pauză) |
| Capacitatea platformei v2 | comună | 503 sandbox_capacity (reîncearcă) |
| Execuții simultane per sandbox | 1 | 409 sandbox_busy |
Durata de viață (ttl_seconds) | implicit 1 h, maximum 24 h | 400 invalid_ttl |
Timeout per execuție (timeout_seconds) | 900 s | 400 |
| Mărimea workspace-ului | 2 GB | eveniment error workspace_too_large |
| Secunde de execuție per utilizator pe zi | 7 200 | 429 sandbox_daily_limit |
| Încărcare fișier | 100 MB | 413 |
| Listă de egress | 20 de domenii | 400 |
| stdout / stderr per execuție | câte 1 MB, apoi un eveniment truncated | — |
Artefacte
GET /v1/sandboxes/{id}/artifacts colectează fiecare fișier din /workspace/out. Fiecare artefact are:
mime, detectat din conținut (nu din numele fișierului);sha256al octeților;scan: verdictul ClamAV,clean,infectedsauerror;url: un link de descărcare semnat, valabil 1 h, prezent doar cândscanesteclean(expires_atspune când expiră).
POST /v1/sandboxes/run returnează artefactele direct în rezultat.
Chitanțe
Fiecare execuție încheiată (rezultatul blocant și evenimentul final exit din stream) include o receipt: digest-ul imaginii, șablonul, SHA-256 al intrărilor și al comenzii, fiecare fișier rezultat cu SHA-256 și mărime, plus generația workspace-ului înainte și după. Păstreaz-o lângă documentul produs ca dovadă a codului care l-a generat și a intrărilor folosite.
Streaming
Trimite Accept: text/event-stream la POST /v1/sandboxes/{id}/exec ca să primești cadre data: pe măsură ce comanda rulează: start, apoi oricâte fragmente stdout / stderr (și truncated când un stream atinge 1 MB), apoi exact un exit (sau error). Fără acest header apelul blochează și returnează un singur rezultat JSON.
Idempotency-Key pe exec și run returnează, timp de 24 h, răspunsul salvat al unei cereri anterioare cu aceeași cheie, în loc să ruleze comanda din nou.
Pornire rapidă: un PDF cu diacritice românești
Dintr-o singură cerere cu POST /v1/sandboxes/run: sandbox-ul este creat, scriptul rulează, /workspace/out este colectat și sandbox-ul este distrus.
curl https://ai.codai.ro/v1/sandboxes/run \
-H "Authorization: Bearer $CODAI_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: act-2026-10-16-001" \
-d @- <<'JSON'
{
"language": "python",
"timeout_seconds": 120,
"code": "import os\nfrom reportlab.lib.pagesizes import A4\nfrom reportlab.pdfbase import pdfmetrics\nfrom reportlab.pdfbase.ttfonts import TTFont\nfrom reportlab.pdfgen import canvas\n\npdfmetrics.registerFont(TTFont('DejaVu', '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf'))\nos.makedirs('/workspace/out', exist_ok=True)\nc = canvas.Canvas('/workspace/out/act.pdf', pagesize=A4)\nc.setFont('DejaVu', 14)\nc.drawString(72, 770, 'Act adițional nr. 1 la contractul de prestări servicii')\nc.drawString(72, 745, 'Părțile convin: ședința se mută în București, str. Ștefan cel Mare.')\nc.save()\nprint('ok')\n"
}
JSONRăspunsul este un rezultat de execuție în care artifacts[0].url descarcă act.pdf timp de o oră. Fontul DejaVu Sans include tot setul ă â î ș ț, așa că ș și ț cu virgulă dedesubt se afișează corect.
SDK-uri
import { Codai } from 'codai-sdk';
const client = new Codai({ apiKey: process.env.CODAI_API_KEY! });
const sb = await client.sandboxes.create({ ttl_seconds: 1800 });
await client.sandboxes.files.put(sb.id, 'in/date.csv', 'client,suma\nSC Exemplu SRL,1200\n');
for await (const ev of client.sandboxes.execStream(sb.id, {
shell: 'python3 -c "import pandas as pd; print(pd.read_csv(\'in/date.csv\'))"',
})) {
if (ev.type === 'stdout') process.stdout.write(ev.data);
if (ev.type === 'exit') console.log('exit', ev.exit_code, ev.receipt.command_sha256);
}
const { data } = await client.sandboxes.artifacts(sb.id);
await client.sandboxes.delete(sb.id);from codai import Codai
client = Codai(api_key="...")
sb = client.sandboxes.create(ttl_seconds=1800)
client.sandboxes.files.put(sb["id"], "in/date.csv", "client,suma\nSC Exemplu SRL,1200\n")
for ev in client.sandboxes.exec_stream(sb["id"], language="python", code="print(open('in/date.csv').read())"):
if ev["type"] == "stdout":
print(ev["data"], end="")
result = client.sandboxes.run(shell="weasyprint in/page.html out/page.pdf",
inputs=[{"path": "page.html", "url": "https://example.com/page.html"}])
client.sandboxes.delete(sb["id"])Unealta de chat codai_sandbox
Adaugă { "type": "codai_sandbox" } în tools la POST /v1/chat/completions (opțional cu egress și template), iar gateway-ul face restul pe server: îi dă modelului sandbox_bash, sandbox_python, sandbox_edit și sandbox_files, le rulează într-un sandbox per x-codai-session-id (durată de viață 1 h, prelungită la fiecare folosire) și, la final, adaugă artefactele la mesajul final ca codai_sandbox.artifacts, plus o listă markdown cu linkuri semnate. Cheia are nevoie în continuare de nivelul de unelte l3_sandbox.
curl https://ai.codai.ro/v1/chat/completions \
-H "Authorization: Bearer $CODAI_API_KEY" \
-H "Content-Type: application/json" \
-H "x-codai-session-id: acte-firma-42" \
-d '{"model":"codai","tools":[{"type":"codai_sandbox"}],
"messages":[{"role":"user","content":"Fă-mi un PDF cu o hotărâre AGA pentru SC Exemplu SRL."}]}'Unealta integrată exec_python rulează acum pe același backend de sandbox; nu mai execută cod în procesul gateway-ului.
Skill-uri pentru documente (compatibile cu Claude Skills)
Agent Skills predefinite de la Anthropic (docx, xlsx, pdf, pptx) au nevoie de un container propriu Anthropic, care nu există pe cloud-urile din spatele codai, așa că codai le oferă pe propriul sandbox, pentru orice model. Două forme echivalente:
{ "tools": [{ "type": "codai_sandbox", "skills": ["docx", "pdf"] }] }{ "container": { "skills": [{ "type": "anthropic", "skill_id": "xlsx", "version": "latest" }] } }Ambele merg pe /v1/chat/completions și pe /v1/messages. Pornesc unealta codai_sandbox și adaugă un bloc fix de instrucțiuni pentru skill-urile cerute (python-docx, openpyxl, reportlab/pypdf, python-pptx, weasyprint), astfel încât modelul produce fișiere reale în /workspace/out, pe care le primești ca linkuri semnate. Skill-urile proprii (type: "custom") și reutilizarea containerului (container.id) răspund cu 400; folosește x-codai-session-id ca să păstrezi un sandbox pe conversație.
Unelte MCP
Serverul MCP codai (https://ai.codai.ro/mcp) expune sandbox_create, sandbox_list, sandbox_exec, sandbox_files (list / read / write / artifacts) și sandbox_kill, plus uneltele v2 sandbox_run_code, sandbox_pause, sandbox_resume, sandbox_fork și sandbox_preview (vezi Sandbox-uri v2). Ele apelează aceleași rute cu cheia ta, deci nivelul de unelte, limitele și contorizarea sunt aceleași ca la API-ul REST. Citirile cu sandbox_files întorc cel mult 256 KB direct în răspuns; fișierele mai mari le iei prin op: "artifacts" (link semnat + SHA-256).
Facturare
Din 17 octombrie 2026 (00:00 UTC) timpul de exec se plătește din portofel cu 0,10 € pe oră de exec, iar primele 10 ore de exec pe utilizator pe lună calendaristică (UTC) sunt gratuite; consumul de dinainte de această dată nu se facturează niciodată. Un sandbox pornit, dar nefolosit, nu costă nimic. Consumul se decontează pe intervale de 10 minute (wallet_ledger, sursa sandbox_charge). Dacă un interval nu poate fi plătit, crearea și exec-urile noi răspund 402 insufficient_balance (cu unpaid_micro_eur) până când o alimentare a portofelului îl achită.
Această secțiune descrie prețul backend-ului v1. Sandbox-urile v2 (implicite) se facturează altfel, vezi Facturare v2.
Sandbox-uri v2
Disponibile pentru orice cheie din 18 octombrie 2026 și backend-ul implicit când omiți backend. API-ul REST de mai sus este comun: un sandbox își alege backend-ul o singură dată, la creare, iar backend (v1 sau v2) apare în fiecare obiect sandbox. Dacă v2 nu este disponibil pentru cheia ta și nu l-ai cerut explicit, crearea trece pe v1; cu backend: "v2" explicit, cererea eșuează.
Un sandbox v2 este o mașină izolată de lungă durată, nu un workspace restaurat la fiecare exec: procesele continuă să ruleze între apeluri, iar întreaga stare (memorie, procese pornite, /workspace) poate fi salvată ca snapshot, pusă pe pauză, reluată și copiată prin fork. Pe v2:
- Egress funcționează ca pe v1:
none(implicit), un presetpypi/npmsau o listă de cel mult 20 de domenii, prin proxy-ul de egress codai (tuneluri simple, fără injectare de credențiale). Fork-urile moștenesc egress-ul părintelui. - Unelte: Python 3, bash, bibliotecile pentru documente și Node 24 cu npm în fiecare șablon.
inputsla exec șiGET /artifacts(scanate ClamAV, SHA-256, URL-uri semnate) se comportă exact ca pe v1.- Doar pe v1:
POST /v1/sandboxes/{id}/snapshotrăspunde409 not_supported_on_v2; folosește pauza/fork-ul.
Limite v2
| Limită | Valoare | Eroare |
|---|---|---|
| Sandbox-uri pornite per cheie | 3 | 429 sandbox_concurrency_limit |
| Sandbox-uri v2 pornite per utilizator | 5, pe toate cheile tale | 429 sandbox_concurrency_limit |
| Sandbox-uri v2 pe pauză per utilizator | 10 | 429 sandbox_paused_limit la pauză |
| Copii per fork | 5 | 400 invalid_count |
| Capacitatea platformei | rezervă comună | 503 sandbox_capacity, reîncearcă |
503 sandbox_capacity este temporar: reîncearcă cu pauze din ce în ce mai lungi sau creează cu backend: "v1".
Șabloane
template | Ce conține |
|---|---|
codai-base | Imaginea implicită: Python 3, bash, Node 24 + npm și bibliotecile pentru documente. |
brivio-acte | codai-base plus LibreOffice, pregătit pentru documente de birou și pornit deja încălzit. |
desktop | Un desktop Linux accesibil prin noVNC pe portul 6080 (îl deschizi cu un URL de previzualizare). |
Când un șablon are un snapshot încălzit, răspunsul la creare conține restored: true.
Kernel persistent: run_code
POST /v1/sandboxes/{id}/run_code cu { "code": "...", "timeout_seconds": 60 } rulează o celulă Python într-un singur kernel de lungă durată. Variabilele globale se păstrează între apeluri, peste pauză/reluare și în fork-uri. Răspunsul este { stdout, stderr, result, error, duration_ms, execution_count, kernel_restarted }: result este repr-ul ultimei expresii (cel mult 64 KB), iar error este { name, value, traceback } pentru o excepție neprinsă. O celulă care depășește timpul limită repornește kernelul (kernel_restarted: true, starea se pierde). POST /v1/sandboxes/{id}/kernel/reset îl repornește intenționat; fișierele rămân.
Pauză și reluare
POST /v1/sandboxes/{id}/pause face un snapshot al sandbox-ului și îl oprește; statusul devine paused. Poți păstra cel mult 10 sandbox-uri v2 pe pauză (429 sandbox_paused_limit); șterge-le pe cele vechi ca să faci loc. La reluare, sandbox-ul intră din nou în limitele de sandbox-uri pornite. POST /v1/sandboxes/{id}/resume îl readuce și răspunde cu sandbox-ul plus restored și ready_ms. restored: true înseamnă că memoria și procesele au revenit exact cum erau; restored: false înseamnă o pornire la rece din workspace-ul salvat (fișierele sunt intacte, starea kernelului se pierde). codai măsoară asta în interiorul sandbox-ului; nu presupune niciodată.
Fork
POST /v1/sandboxes/{id}/fork cu { "count": 1..5, "ttl_seconds"?: n } face un snapshot al unui sandbox pornit (care continuă să ruleze) și pornește count copii din acel snapshot. Fiecare copie este un sandbox complet, cu id, workspace și stare de kernel proprii, și se facturează ca oricare altul. Răspunsul este { object: "list", parent_id, data: [{ id, restored, ready_ms, error? }] }: pentru fiecare copie, restored: false înseamnă că a pornit la rece, iar error înseamnă că a eșuat și nu rulează. Un fork nu poate duce o cheie peste 6 sandbox-uri active și nici pe tine peste 5 sandbox-uri v2 pornite (429 sandbox_concurrency_limit).
URL-uri de previzualizare
POST /v1/sandboxes/{id}/preview cu { "port": 3000, "ttl_seconds": 900 } răspunde { port, url, expires_at }: un URL HTTPS semnat pe *.sbx.codai.ro care trimite la acel port din sandbox (un server de dezvoltare sau noVNC pe 6080 în desktop). Porturi 1024–65535, cu excepția lui 8080; valabilitate între 60 s și 1 h (implicit 15 min). URL-ul nu cere cheie: oricine îl are ajunge la port până la expirare, așa că trimite-l doar celor care trebuie să vadă aplicația.
const sb = await client.sandboxes.create({
template: 'codai-base',
egress: { mode: 'preset', preset: 'pypi' },
}); // v2 implicit
await client.sandboxes.runCode(sb.id, {
code: 'import pandas as pd\ndf = pd.DataFrame({"a": [1, 2]})',
});
const r = await client.sandboxes.runCode(sb.id, { code: 'df["a"].sum()' }); // r.result === '3'
const { data } = await client.sandboxes.fork(sb.id, { count: 3 });
await client.sandboxes.pause(sb.id);
const back = await client.sandboxes.resume(sb.id); // back.restored, back.ready_mssb = client.sandboxes.create(backend="v2", template="desktop")
p = client.sandboxes.preview(sb["id"], 6080, ttl_seconds=600)
print(p["url"], p["expires_at"])Facturare v2
- Pornit: 0,10 € pe oră de ceas cât timp sandbox-ul există (ocupat sau nefolosit), cu 5 ore gratuite pe utilizator pe lună calendaristică (UTC) (separat de orele de exec v1 de mai sus).
- Pe pauză: se stochează doar snapshot-ul, cu 0,05 € pe GB-lună, contorizat orar.
- Sumele se decontează din portofel ca la v1 (
402 insufficient_balancecând un interval nu poate fi plătit).
bearerAuthAutorizareBearer <codai_…>O cheie API codai (prefix codai_). Tokenurile efemere de la POST /v1/tokens sunt acceptate doar de suprafețele media potrivite scope-ului lor (/v1/realtime, /v1/audio/*, /v1/embeddings).
x-request-id?stringId de corelare ales de client; e returnat ca x-codai-trace-id și persistat pe rândul de usage.
length <= 128application/json- body
backend?stringBackend-ul de execuție: v2 (implicit: pod de lungă durată cu snapshot-uri de memorie, pauză/reluare, fork, kernel persistent, URL-uri de previzualizare, egress prin proxy-ul codai, Node 24 + npm) sau v1 (Cloud Run, workspace restaurat la fiecare exec, POST /snapshot). Dacă lipsește și v2 nu este disponibil pentru cheie, sandbox-ul se creează pe v1; un v2 explicit eșuează.
"v2""v1""v2"template?stringȘablonul de imagine. v1: doar codai-base. v2: codai-base, brivio-acte (unelte de birou deja încălzite) sau desktop (desktop Linux prin noVNC pe portul 6080).
"codai-base""codai-base""brivio-acte""desktop"ttl_seconds?integerDurata de viață; sandbox-ul expiră după aceea (glisantă pentru sesiunile de chat). Cel mult 86400 pe v1 și 604800 pe v2.
60 <= value <= 6048003600egress?||Rețeaua spre exterior, pe ambele backend-uri. none (implicit) nu are deloc rețea; allowlist permite HTTPS către cel mult 20 de domenii prin proxy-ul de egress codai (fără adrese IP, fără gazde interne); preset deschide o listă predefinită (pypi, npm). Proxy-ul deschide tuneluri simple și nu injectează niciodată credențiale; fork-urile v2 moștenesc politica părintelui.
metadata?Etichetele tale, returnate neschimbate.
Sandbox-ul nou.
application/json- response
id*stringuuidobject*"sandbox""sandbox"status*string"running""paused""stopped""expired""destroyed"backend*stringBackend-ul ales la creare.
"v1""v2"restored?booleanDoar la crearea unui sandbox v2: adevărat când a pornit dintr-un snapshot de șablon încălzit.
template*stringegress*||Rețeaua spre exterior, pe ambele backend-uri. none (implicit) nu are deloc rețea; allowlist permite HTTPS către cel mult 20 de domenii prin proxy-ul de egress codai (fără adrese IP, fără gazde interne); preset deschide o listă predefinită (pypi, npm). Proxy-ul deschide tuneluri simple și nu injectează niciodată credențiale; fork-urile v2 moștenesc politica părintelui.
created_at*stringdate-timeexpires_at*stringdate-timelast_exec_at*|date-timemetadata*usage*curl -X POST 'https://ai.codai.ro/v1/sandboxes' \ -H 'Content-Type: application/json' \ -d '{}'{ "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "object": "sandbox", "status": "running", "backend": "v1", "restored": true, "template": "string", "egress": { "mode": "none" }, "created_at": "2019-08-24T14:15:22Z", "expires_at": "2019-08-24T14:15:22Z", "last_exec_at": "2019-08-24T14:15:22Z", "metadata": { "property1": "string", "property2": "string" }, "usage": { "exec_count": 0, "exec_seconds": 0 }}bearerAuthAutorizareBearer <codai_…>O cheie API codai (prefix codai_). Tokenurile efemere de la POST /v1/tokens sunt acceptate doar de suprafețele media potrivite scope-ului lor (/v1/realtime, /v1/audio/*, /v1/embeddings).
status?stringDoar sandbox-urile cu acest status.
"running""paused""stopped""expired""destroyed"x-request-id?stringId de corelare ales de client; e returnat ca x-codai-trace-id și persistat pe rândul de usage.
length <= 128Sandbox-urile tale.
application/json- response
object?"list""list"data*array<>curl -X GET 'https://ai.codai.ro/v1/sandboxes'{ "object": "list", "data": [ { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "object": "sandbox", "status": "running", "backend": "v1", "restored": true, "template": "string", "egress": { "mode": "none" }, "created_at": "2019-08-24T14:15:22Z", "expires_at": "2019-08-24T14:15:22Z", "last_exec_at": "2019-08-24T14:15:22Z", "metadata": { "property1": "string", "property2": "string" }, "usage": { "exec_count": 0, "exec_seconds": 0 } } ]}bearerAuthAutorizareBearer <codai_…>O cheie API codai (prefix codai_). Tokenurile efemere de la POST /v1/tokens sunt acceptate doar de suprafețele media potrivite scope-ului lor (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringId-ul sandbox-ului (uuid).
uuidx-request-id?stringId de corelare ales de client; e returnat ca x-codai-trace-id și persistat pe rândul de usage.
length <= 128Sandbox-ul.
application/json- response
id*stringuuidobject*"sandbox""sandbox"status*string"running""paused""stopped""expired""destroyed"backend*stringBackend-ul ales la creare.
"v1""v2"restored?booleanDoar la crearea unui sandbox v2: adevărat când a pornit dintr-un snapshot de șablon încălzit.
template*stringegress*||Rețeaua spre exterior, pe ambele backend-uri. none (implicit) nu are deloc rețea; allowlist permite HTTPS către cel mult 20 de domenii prin proxy-ul de egress codai (fără adrese IP, fără gazde interne); preset deschide o listă predefinită (pypi, npm). Proxy-ul deschide tuneluri simple și nu injectează niciodată credențiale; fork-urile v2 moștenesc politica părintelui.
created_at*stringdate-timeexpires_at*stringdate-timelast_exec_at*|date-timemetadata*usage*curl -X GET 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08'{ "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "object": "sandbox", "status": "running", "backend": "v1", "restored": true, "template": "string", "egress": { "mode": "none" }, "created_at": "2019-08-24T14:15:22Z", "expires_at": "2019-08-24T14:15:22Z", "last_exec_at": "2019-08-24T14:15:22Z", "metadata": { "property1": "string", "property2": "string" }, "usage": { "exec_count": 0, "exec_seconds": 0 }}bearerAuthAutorizareBearer <codai_…>O cheie API codai (prefix codai_). Tokenurile efemere de la POST /v1/tokens sunt acceptate doar de suprafețele media potrivite scope-ului lor (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringId-ul sandbox-ului (uuid).
uuidx-request-id?stringId de corelare ales de client; e returnat ca x-codai-trace-id și persistat pe rândul de usage.
length <= 128Idempotency-Key?stringTimp de 24 h returnează răspunsul salvat al unei cereri anterioare cu aceeași cheie, în loc să o ruleze din nou.
length <= 255application/json- body
Exact unul dintre cmd, shell sau language + code.
cmd?array<string>Comanda ca argv (fără shell).
items <= 256shell?stringO linie de shell rulată cu /bin/bash -lc.
length <= 65536language?stringRulează code cu acest interpretor.
"python""bash"code?stringCodul sursă pentru language.
length <= 1048576env?Variabile de mediu (cel mult 64; numele care încep cu CODAI_ sau GOOGLE_ sunt respinse).
cwd?stringDirectorul de lucru din /workspace.
length <= 1024timeout_seconds?integerLimita de timp pentru acest exec.
1 <= value <= 900120stdin?stringIntrarea standard.
length <= 1048576inputs?array<>Fișiere scrise doar-citire în /workspace/in înainte de rularea comenzii.
items <= 50Rezultat JSON sau stream SSE când cererea a trimis Accept: text/event-stream.
- response
exit_code*|Codul de ieșire propriu al comenzii; 128 + n când a fost încheiată de semnalul n. null doar când execuția a depășit timpul (timed_out: true) sau procesul nu a putut fi pornit.
stdout*stringIeșirea standard (cel mult 1 MB).
stderr*stringIeșirea de erori (cel mult 1 MB).
timed_out*booleanduration_ms*integerartifacts?array<>Prezent pe /v1/sandboxes/run.
receipt*Ce a rulat și ce a produs: digest-ul imaginii, hash-urile intrărilor și ale comenzii, fiecare fișier rezultat cu SHA-256 și generațiile workspace-ului înainte și după.
curl -X POST 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08/exec' \ -H 'Content-Type: application/json' \ -d '{}'{ "exit_code": 0, "stdout": "string", "stderr": "string", "timed_out": true, "duration_ms": 0, "artifacts": [ { "id": "string", "path": "string", "bytes": 0, "mime": "string", "sha256": "string", "scan": "clean", "url": "string", "expires_at": "2019-08-24T14:15:22Z" } ], "receipt": { "image_digest": "string", "template": "string", "input_sha256": "string", "command_sha256": "string", "outputs": [ { "path": "string", "sha256": "string", "bytes": 0 } ], "generation_before": 0, "generation_after": 0 }}bearerAuthAutorizareBearer <codai_…>O cheie API codai (prefix codai_). Tokenurile efemere de la POST /v1/tokens sunt acceptate doar de suprafețele media potrivite scope-ului lor (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringId-ul sandbox-ului (uuid).
uuidpath*stringCalea fișierului relativă la /workspace (sunt permise slash-uri, fiecare segment codat URL).
length <= 1024x-request-id?stringId de corelare ales de client; e returnat ca x-codai-trace-id și persistat pe rândul de usage.
length <= 128body*fileapplication/octet-streambinaryScris: mărime, SHA-256 și noua generație.
application/json- response
path*stringbytes*integersha256*stringgeneration*integercurl -X PUT 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08/files/string' \ -H 'Content-Type: application/octet-stream' \ -d 'string'{ "path": "string", "bytes": 0, "sha256": "string", "generation": 0}bearerAuthAutorizareBearer <codai_…>O cheie API codai (prefix codai_). Tokenurile efemere de la POST /v1/tokens sunt acceptate doar de suprafețele media potrivite scope-ului lor (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringId-ul sandbox-ului (uuid).
uuidpath*stringCalea fișierului relativă la /workspace (sunt permise slash-uri, fiecare segment codat URL).
length <= 1024x-request-id?stringId de corelare ales de client; e returnat ca x-codai-trace-id și persistat pe rândul de usage.
length <= 128Conținutul fișierului.
response?fileapplication/octet-streambinarycurl -X GET 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08/files/string'"string"bearerAuthAutorizareBearer <codai_…>O cheie API codai (prefix codai_). Tokenurile efemere de la POST /v1/tokens sunt acceptate doar de suprafețele media potrivite scope-ului lor (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringId-ul sandbox-ului (uuid).
uuidpath?stringDirectorul relativ la /workspace.
length <= 1024x-request-id?stringId de corelare ales de client; e returnat ca x-codai-trace-id și persistat pe rândul de usage.
length <= 128Intrările din director.
application/json- response
entries*array<>curl -X GET 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08/files'{ "entries": [ { "path": "string", "bytes": 0, "type": "file", "mtime": "2019-08-24T14:15:22Z" } ]}bearerAuthAutorizareBearer <codai_…>O cheie API codai (prefix codai_). Tokenurile efemere de la POST /v1/tokens sunt acceptate doar de suprafețele media potrivite scope-ului lor (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringId-ul sandbox-ului (uuid).
uuidx-request-id?stringId de corelare ales de client; e returnat ca x-codai-trace-id și persistat pe rândul de usage.
length <= 128Artefactele.
application/json- response
data*array<>curl -X GET 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08/artifacts'{ "data": [ { "id": "string", "path": "string", "bytes": 0, "mime": "string", "sha256": "string", "scan": "clean", "url": "string", "expires_at": "2019-08-24T14:15:22Z" } ]}bearerAuthAutorizareBearer <codai_…>O cheie API codai (prefix codai_). Tokenurile efemere de la POST /v1/tokens sunt acceptate doar de suprafețele media potrivite scope-ului lor (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringId-ul sandbox-ului (uuid).
uuidx-request-id?stringId de corelare ales de client; e returnat ca x-codai-trace-id și persistat pe rândul de usage.
length <= 128application/json- body
name?stringNume opțional pentru snapshot.
length <= 128Snapshot-ul.
application/json- response
object*stringNumele obiectului din stocare.
bytes*integersha256*stringcurl -X POST 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08/snapshot' \ -H 'Content-Type: application/json' \ -d '{}'{ "object": "string", "bytes": 0, "sha256": "string"}bearerAuthAutorizareBearer <codai_…>O cheie API codai (prefix codai_). Tokenurile efemere de la POST /v1/tokens sunt acceptate doar de suprafețele media potrivite scope-ului lor (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringId-ul sandbox-ului (uuid).
uuidx-request-id?stringId de corelare ales de client; e returnat ca x-codai-trace-id și persistat pe rândul de usage.
length <= 128Distrus.
curl -X DELETE 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08'bearerAuthAutorizareBearer <codai_…>O cheie API codai (prefix codai_). Tokenurile efemere de la POST /v1/tokens sunt acceptate doar de suprafețele media potrivite scope-ului lor (/v1/realtime, /v1/audio/*, /v1/embeddings).
x-request-id?stringId de corelare ales de client; e returnat ca x-codai-trace-id și persistat pe rândul de usage.
length <= 128Idempotency-Key?stringTimp de 24 h returnează răspunsul salvat al unei cereri anterioare cu aceeași cheie, în loc să o ruleze din nou.
length <= 255application/json- body
Câmpurile din SandboxCreate plus cele din SandboxExecRequest.
template?"codai-base"Șablonul de imagine.
"codai-base""codai-base"ttl_seconds?integerDurata de viață; sandbox-ul expiră după aceea (glisantă pentru sesiunile de chat).
60 <= value <= 864003600egress?||Rețeaua spre exterior, pe ambele backend-uri. none (implicit) nu are deloc rețea; allowlist permite HTTPS către cel mult 20 de domenii prin proxy-ul de egress codai (fără adrese IP, fără gazde interne); preset deschide o listă predefinită (pypi, npm). Proxy-ul deschide tuneluri simple și nu injectează niciodată credențiale; fork-urile v2 moștenesc politica părintelui.
metadata?Etichetele tale, returnate neschimbate.
cmd?array<string>Comanda ca argv (fără shell).
items <= 256shell?stringO linie de shell rulată cu /bin/bash -lc.
length <= 65536language?stringRulează code cu acest interpretor.
"python""bash"code?stringCodul sursă pentru language.
length <= 1048576env?Variabile de mediu (cel mult 64; numele care încep cu CODAI_ sau GOOGLE_ sunt respinse).
cwd?stringDirectorul de lucru din /workspace.
length <= 1024timeout_seconds?integerLimita de timp pentru acest exec.
1 <= value <= 900120stdin?stringIntrarea standard.
length <= 1048576inputs?array<>Fișiere scrise doar-citire în /workspace/in înainte de rularea comenzii.
items <= 50Codul de ieșire, output-ul, artefactele și chitanța.
application/json- response
exit_code*|Codul de ieșire propriu al comenzii; 128 + n când a fost încheiată de semnalul n. null doar când execuția a depășit timpul (timed_out: true) sau procesul nu a putut fi pornit.
stdout*stringIeșirea standard (cel mult 1 MB).
stderr*stringIeșirea de erori (cel mult 1 MB).
timed_out*booleanduration_ms*integerartifacts?array<>Prezent pe /v1/sandboxes/run.
receipt*Ce a rulat și ce a produs: digest-ul imaginii, hash-urile intrărilor și ale comenzii, fiecare fișier rezultat cu SHA-256 și generațiile workspace-ului înainte și după.
curl -X POST 'https://ai.codai.ro/v1/sandboxes/run' \ -H 'Content-Type: application/json' \ -d '{}'{ "exit_code": 0, "stdout": "string", "stderr": "string", "timed_out": true, "duration_ms": 0, "artifacts": [ { "id": "string", "path": "string", "bytes": 0, "mime": "string", "sha256": "string", "scan": "clean", "url": "string", "expires_at": "2019-08-24T14:15:22Z" } ], "receipt": { "image_digest": "string", "template": "string", "input_sha256": "string", "command_sha256": "string", "outputs": [ { "path": "string", "sha256": "string", "bytes": 0 } ], "generation_before": 0, "generation_after": 0 }}bearerAuthAutorizareBearer <codai_…>O cheie API codai (prefix codai_). Tokenurile efemere de la POST /v1/tokens sunt acceptate doar de suprafețele media potrivite scope-ului lor (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringId-ul sandbox-ului (uuid).
uuidx-request-id?stringId de corelare ales de client; e returnat ca x-codai-trace-id și persistat pe rândul de usage.
length <= 128application/json- body
code*stringCodul Python al celulei.
1 <= length <= 1000000timeout_seconds?integerSecunde până la repornirea kernelului (implicit 60, maxim 900).
1 <= value <= 90060Rezultatul celulei.
application/json- response
stdout*stringTextul scris la stdout în timpul celulei.
stderr*stringTextul scris la stderr în timpul celulei.
result*|repr-ul ultimei expresii sau null.
error*|Excepția neprinsă sau null.
duration_ms*integerMilisecunde de ceas.
execution_count*integerContorul de execuții al kernelului.
kernel_restarted*booleanAdevărat când kernelul a fost repornit (timp depășit sau eroare) și starea lui s-a pierdut.
curl -X POST 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08/run_code' \ -H 'Content-Type: application/json' \ -d '{ "code": "string"}'{ "stdout": "string", "stderr": "string", "result": "string", "error": { "name": "string", "value": "string", "traceback": "string" }, "duration_ms": 0, "execution_count": 0, "kernel_restarted": true}bearerAuthAutorizareBearer <codai_…>O cheie API codai (prefix codai_). Tokenurile efemere de la POST /v1/tokens sunt acceptate doar de suprafețele media potrivite scope-ului lor (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringId-ul sandbox-ului (uuid).
uuidx-request-id?stringId de corelare ales de client; e returnat ca x-codai-trace-id și persistat pe rândul de usage.
length <= 128Kernelul a fost repornit.
application/json- response
ok*truetruecurl -X POST 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08/kernel/reset'{ "ok": true}bearerAuthAutorizareBearer <codai_…>O cheie API codai (prefix codai_). Tokenurile efemere de la POST /v1/tokens sunt acceptate doar de suprafețele media potrivite scope-ului lor (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringId-ul sandbox-ului (uuid).
uuidx-request-id?stringId de corelare ales de client; e returnat ca x-codai-trace-id și persistat pe rândul de usage.
length <= 128Sandbox-ul pus pe pauză.
application/json- response
id*stringuuidobject*"sandbox""sandbox"status*string"running""paused""stopped""expired""destroyed"backend*stringBackend-ul ales la creare.
"v1""v2"restored?booleanDoar la crearea unui sandbox v2: adevărat când a pornit dintr-un snapshot de șablon încălzit.
template*stringegress*||Rețeaua spre exterior, pe ambele backend-uri. none (implicit) nu are deloc rețea; allowlist permite HTTPS către cel mult 20 de domenii prin proxy-ul de egress codai (fără adrese IP, fără gazde interne); preset deschide o listă predefinită (pypi, npm). Proxy-ul deschide tuneluri simple și nu injectează niciodată credențiale; fork-urile v2 moștenesc politica părintelui.
created_at*stringdate-timeexpires_at*stringdate-timelast_exec_at*|date-timemetadata*usage*curl -X POST 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08/pause'{ "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "object": "sandbox", "status": "running", "backend": "v1", "restored": true, "template": "string", "egress": { "mode": "none" }, "created_at": "2019-08-24T14:15:22Z", "expires_at": "2019-08-24T14:15:22Z", "last_exec_at": "2019-08-24T14:15:22Z", "metadata": { "property1": "string", "property2": "string" }, "usage": { "exec_count": 0, "exec_seconds": 0 }}bearerAuthAutorizareBearer <codai_…>O cheie API codai (prefix codai_). Tokenurile efemere de la POST /v1/tokens sunt acceptate doar de suprafețele media potrivite scope-ului lor (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringId-ul sandbox-ului (uuid).
uuidx-request-id?stringId de corelare ales de client; e returnat ca x-codai-trace-id și persistat pe rândul de usage.
length <= 128Sandbox-ul reluat.
application/json- response
id*stringuuidobject*"sandbox""sandbox"status*string"running""paused""stopped""expired""destroyed"backend*stringBackend-ul ales la creare.
"v1""v2"restored*booleanAdevărat când starea din memorie a fost restaurată, fals la o pornire la rece.
template*stringegress*||Rețeaua spre exterior, pe ambele backend-uri. none (implicit) nu are deloc rețea; allowlist permite HTTPS către cel mult 20 de domenii prin proxy-ul de egress codai (fără adrese IP, fără gazde interne); preset deschide o listă predefinită (pypi, npm). Proxy-ul deschide tuneluri simple și nu injectează niciodată credențiale; fork-urile v2 moștenesc politica părintelui.
created_at*stringdate-timeexpires_at*stringdate-timelast_exec_at*|date-timemetadata*usage*ready_ms*integerMilisecunde până când sandbox-ul a răspuns.
curl -X POST 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08/resume'{ "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "object": "sandbox", "status": "running", "backend": "v1", "template": "string", "egress": { "mode": "none" }, "created_at": "2019-08-24T14:15:22Z", "expires_at": "2019-08-24T14:15:22Z", "last_exec_at": "2019-08-24T14:15:22Z", "metadata": { "property1": "string", "property2": "string" }, "usage": { "exec_count": 0, "exec_seconds": 0 }, "restored": true, "ready_ms": 0}bearerAuthAutorizareBearer <codai_…>O cheie API codai (prefix codai_). Tokenurile efemere de la POST /v1/tokens sunt acceptate doar de suprafețele media potrivite scope-ului lor (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringId-ul sandbox-ului (uuid).
uuidx-request-id?stringId de corelare ales de client; e returnat ca x-codai-trace-id și persistat pe rândul de usage.
length <= 128application/json- body
count*integerCâți copii să pornească (1–5).
1 <= value <= 5ttl_seconds?integerDurata de viață a fiecărui copil în secunde (implicit: cât a rămas din părinte).
60 <= value <= 604800Copiii.
application/json- response
object*"list""list"parent_id*stringuuiddata*array<>curl -X POST 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08/fork' \ -H 'Content-Type: application/json' \ -d '{ "count": 1}'{ "object": "list", "parent_id": "1c6ca187-e61f-4301-8dcb-0e9749e89eef", "data": [ { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "restored": true, "ready_ms": 0, "error": { "code": "string", "message": "string" } } ]}bearerAuthAutorizareBearer <codai_…>O cheie API codai (prefix codai_). Tokenurile efemere de la POST /v1/tokens sunt acceptate doar de suprafețele media potrivite scope-ului lor (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringId-ul sandbox-ului (uuid).
uuidx-request-id?stringId de corelare ales de client; e returnat ca x-codai-trace-id și persistat pe rândul de usage.
length <= 128application/json- body
port*integerPortul din sandbox (1024–65535, nu 8080).
1024 <= value <= 65535ttl_seconds?integerValabilitatea în secunde (60–3600, implicit 900).
60 <= value <= 3600900URL-ul de previzualizare semnat.
application/json- response
port*integerurl*stringURL HTTPS semnat; oricine îl are ajunge la port până la expirare.
uriexpires_at*stringdate-timecurl -X POST 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08/preview' \ -H 'Content-Type: application/json' \ -d '{ "port": 1024}'{ "port": 0, "url": "http://example.com", "expires_at": "2019-08-24T14:15:22Z"}