Ir para o conteúdo
decodecodeveloper docs
Studio

Arquitetura

Como o Studio é conectado de ponta a ponta — edge, cloud cluster e desktop — e como requests, runs e sandboxes fluem entre as camadas.

Esta página descreve como um deployment do Studio em execução é montado: as camadas, o que cada uma faz e como um request vira um run de agent, uma chamada de tool ou um preview de sandbox. É um bom material de fundo tanto se você faz self-host quanto se usa a cloud, e é o complemento conceitual dos guias de deploy de Kubernetes e Docker Compose.

A topologia Kubernetes segue o chart oficial 0.16.1, revisado no commit f4cd364c do Studio. O diagrama de edge representa o caminho de referência hospedado; um deployment próprio configura seu ingress e roteamento público.

As três camadas

O Studio abrange três fronteiras de confiança/localidade:

  • Edge — o caminho da internet pública: um CDN e um load balancer L4.
  • Cloud cluster — o deployment no Kubernetes: web, API, workers, Postgres, NATS e cloud sandboxes.
  • Desktop — a app nativa do Studio no laptop do usuário, onde o local-api embutido gerencia coding harnesses nativos e sandboxes locais.

O mesmo fluxo em texto:

EDGE                CLOUD CLUSTER                                     DESKTOP
────                ─────────────                                     ───────

Client ─▶ CF ─┬─▶ NLB ─▶ Web(nginx) ─▶ API ──┬─▶ MCP Proxy ─▶ Downstream MCP (ext)
              │                               ├─▶ Files/Storage ─▶ Object Store (ext)
              │                               ├─▶ DB (Postgres)
              │                               ├─▶ NATS
              │                               └─▶ Worker ─┬─▶ LLM
              │                                           ├─▶ Downstream MCP (in-process bridge)
              │                                           └─▶ AgentSandbox ─┬─ Daemon API (/_sandbox/*)
              │                                                             └─ Org FS (sidecar) ─▶ /api/:org/fs ─▶ S3
              └─▶ Gateway (k8s) ─▶ Preview (public dev-server)

Desktop app ─▶ local-api ─┬─▶ Studio API (dados upstream e MCP)
                          ├─▶ Claude Code / Codex / OpenCode
                          └─▶ Desktop Sandbox ─▶ Org FS (mount)

Edge

ComponenteFunção
CF (Cloudflare)Terminação TLS, cache estático da SPA, mitigação de DDoS/bots. Primeiro salto de todo o tráfego.
NLBLoad balancer L4 na frente do cluster. Roteia para os pods de web (front-door); o seletor frontDoorLabels decide quais pods recebem o ingress.

Cloud cluster

O front door e os workers escalam separadamente

O chart tem um Deployment principal com um container nginx e dois containers de API em cada pod. Eles escalam juntos como pods de front door. Os workers de fila rodam em outro Deployment e podem escalar independentemente do front door.

CamadaDeploymentResponsabilidade
Webnginx no pod principalServe a SPA em React e faz reverse-proxy das rotas de API e MCP para os containers de API do mesmo pod. Porta 8080.
APIHono, STUDIO_DISPATCH_ROLE=apiRotas HTTP, Better Auth, o MCP proxy, controle de acesso. Enfileira runs do Decopilot nas queues do DBOS e acompanha o NATS para fazer streaming do output de volta para a UI. Stateless. Não roda o agent loop.
WorkerHono, STUDIO_DISPATCH_ROLE=workerDesenfileira as queues do DBOS (via listenQueues) e roda o agent loop (streamText: model → tool → repete). Limitado por CPU. São os executores de run; escalam horizontalmente.

A separação é por role, não por imagem — ambos rodam o mesmo build. STUDIO_DISPATCH_ROLE decide se um pod escuta as queues do DBOS (worker) ou apenas serve HTTP e enfileira (api). Um setup de deployment único pode usar all.

O conjunto de queues que um pod worker escuta é configurado por env (listenQueues). Por isso, os pools de workers podem ser separados por queue do DBOS — rodando workflows diferentes em pools separados com seus próprios recursos e escalonamento.

Datastores

ComponenteFunção
DB (PostgreSQL, via Kysely)Sistema de registro: orgs, connections, credential vault, audit, threads + mensagens e sandbox_runner_state. Também mantém as queues do DBOS e o journal workflow_status que tornam os runs duráveis e recuperáveis.
NATSInfraestrutura de mensageria ao vivo com três tarefas: (1) o log de runs com fencing no JetStream (decopilot.stream.<thread>), usado por /stream e pelo projector durável; (2) broadcasts de cancelamento de runs entre pods; (3) estado em JetStream KV compartilhado entre réplicas para caches de listas MCP e circuit breakers de connections.

O ciclo de vida de um run do Decopilot

  1. Uma mensagem (POST /messages) ou o disparo de uma automation cria um run em uma thread.
  2. A API o enfileira em uma queue do DBOS no Postgres:
    • THREAD_GATE_QUEUE — serializada por thread (concorrência 1 por threadId).
    • AUTOMATIONS_QUEUE — particionada por org, então uma org saturada só bloqueia a própria partição.
  3. Um Worker desenfileira o gate workflow e inicia um child workflow hosted. Runs in-process do Decopilot usam HOSTED_HARNESS_QUEUE; runs de Claude Code hosted na sandbox usam HOSTED_HARNESS_SANDBOXED_QUEUE. O gate pai não espera o child — ele acompanha (live-tail) o mesmo stream do NATS que o child publica, enquanto o projector durável escreve mensagens e o status terminal.
  4. Os chunks de output são publicados no NATS e acompanhados de volta para a UI via /stream.
  5. Se o pod cair, o replay do journal do DBOS retoma os steps retriáveis em outro pod — a recuperação é trabalho do framework, não feito à mão.

Os dois caminhos são hosted e usam o provider fixo AgentSandbox do Studio quando precisam de uma sandbox:

  • Decopilot roda seu model loop in-process no worker e acessa a sandbox gerenciada para operações de repositório, filesystem, Git e shell.
  • Claude Code roda seu harness loop dentro da sandbox gerenciada; o worker encaminha seu stream para o mesmo pipeline de NATS e projector.

Chats de coding agents nativos são uma superfície separada. A app desktop roda Claude Code, Codex ou OpenCode localmente e sincroniza a thread por meio do local-api embutido.

MCP: in-process vs. as rotas de proxy

Essa distinção importa para raciocinar sobre o sistema:

  • O worker chama tools de MCP in-process. O agent loop monta um PassthroughClient sobre um bridge em memória e chama as tools diretamente — sem salto HTTP dentro do cluster. Ele ainda se conecta para fora a downstream MCP servers.
  • As rotas do MCP proxy são para clients externos. /mcp/virtual-mcp/:id, /mcp/:connectionId, /oauth-proxy/* e /.well-known são servidas pela API para IDEs externas (Cursor, Claude, VS Code). O worker não usa essas rotas.

Quais rotas são chamadas por quem

RotasAPI / externoWorker
MCP proxy (/mcp/*, /oauth-proxy/*, .well-known)✅ clients externos via API❌ (in-process no lugar)
File & object-storage (/api/:org/files/*, GET/PUT presigned, uploads, /api/:org/fs/*)✅ servindo clients✅ tools de agent leem/escrevem arquivos e geram presigned URLs
Apenas worker via HTTP—nenhuma — as chamadas de tool são in-process

As rotas de file e object-storage são a verdadeira superfície "chamada por ambos", e são respaldadas por um Object Store compatível com S3. As rotas /api/:org/fs/* também dão suporte ao mount do org-filesystem dentro das sandboxes.

Sandboxes

Uma AgentSandbox hosted clona o repo, roda o dev server e expõe um daemon in-pod. Sua superfície HTTP se divide em duas:

SuperfícieAuthPropósitoQuem chama
Preview (catch-all *)Nenhuma — o handle (subdomínio) é o segredoFaz reverse-proxy do dev server em execução (o preview ao vivo da app); injeta HMR. /_sandbox/* é ativamente rejeitado aqui.O navegador do usuário final em <handle>.preview.<domain>, através do Cloudflare (LB) → um Kubernetes Gateway (Istio Gateway API / HTTPRoute) → o daemon
Daemon API (/_sandbox/*)Bearer DAEMON_TOKENSuperfície de controle: operações de fs (read/write/edit/bash/grep), git (status/diff/publish), exec de scripts, setup (clone → install → start), tasks, eventos SSE, dispatch do harness.O cluster (worker para as tools de fs/git/bash do agent; API para setup + eventos da UI)

Cloud vs. desktop sandboxes

Cloud sandboxDesktop sandbox
Ondeoperador agent-sandbox + um pod SandboxClaim por (user, projectRef)O local-api e o SandboxManager da app nativa, com um worktree local por handle
Acessado viaport-forward do k8s / Service in-cluster (controle); ingress ou port-forward (preview)interceptação em loopback; <handle>.localhost:<port> (preview)
Dono do roteamentoA API hosted do Studio sempre usa AgentSandbox e reporta agent-sandboxA app nativa intercepta localmente as mesmas chamadas de lifecycle/filesystem e reporta local-api

Org filesystem (org-fs)

Cada sandbox pode montar o org filesystem em <appRoot>/org/<volume>, para que o agent e o dev server leiam e escrevam arquivos da org como paths comuns. A stack de mount é rclone (NFS/FUSE) → o WebDAV de loopback do daemon → /api/:org/fs/* → S3 — o mesmo object store das rotas de arquivo, exposto como um volume montado. É o mesmo filesystem que você navega na Library.

Ele é conectado em ambas as superfícies, com mecânicas de mount diferentes:

Cloud sandboxDesktop sandbox
Quem montaum container sidecar privilegiado (o daemon não-privilegiado não consegue montar)o local-api diretamente na máquina do usuário
Entrega da configpós-bind: Studio POST /_sandbox/orgfs-config; o daemon a repassa para um control volume compartilhado que o sidecar observa (claims do warm-pool rejeitam spec.env)o runtime nativo resolve os volumes da org e mantém o mount local
Propagaçãorclone com allowOther para que o mount se propague ao container principalclient único — sem necessidade de propagação

Desktop

A app nativa do Studio embute a UI de produção e um local-api em Axum na mesma origin autenticada de loopback. O local-api encaminha dados compartilhados e tráfego MCP para o upstream, mas termina no laptop as rotas exclusivas de thread, harness, sandbox, filesystem, Git, task, terminal e preview. Ele inicia o CLI do Claude Code, Codex ou OpenCode do usuário no worktree local da thread e persiste o estado nativo de threads e sandboxes no SQLite.

O roteamento do provider segue a superfície que recebe o request, e não uma opção do request: chamadas de lifecycle do browser chegam à API hosted e ao AgentSandbox; as mesmas chamadas feitas pela webview nativa são interceptadas pelo local-api e operam na sandbox local-api. Assim, os registros hosted e desktop continuam distinguíveis sem expor um seletor de provider aos callers.

Em resumo

  • Web e API compartilham o Deployment principal; workers escalam separadamente como executores de run do DBOS.
  • Os runs são duráveis via queues + journal do DBOS no Postgres; a recuperação é automática.
  • As chamadas de tool de MCP são in-process no worker; as rotas de proxy /mcp/* são apenas para clients externos.
  • As rotas de file/object-storage são a superfície compartilhada entre API e worker.
  • O NATS dá suporte a streaming e coordenação entre pods.
  • O roteamento de sandbox pertence à superfície: o Studio hosted usa AgentSandbox; a app nativa intercepta localmente e registra local-api.