Practical guide

Doop: a design canvas where you and your agents work side by side.

An analysis of the open-source Doop: what it is, how it is built, how to run it on your computer or in a cloud of yours, which settings matter and how to plug Claude Code, Codex and other agents into it.

O que eu fiz e o que não fiz. Li o README e a documentação pública do repositório kgoedecke/doop em 2026-10-29. Não executei o Doop: os comandos abaixo vêm da documentação do projeto e não foram testados por mim. Confira no README do commit que você baixar, porque o projeto muda rápido.What I did and did not do. I read the README and the public documentation of the kgoedecke/doop repository on 2026-10-29. I did not run Doop: the commands below come from the project documentation and were not tested by me. Check the README of the commit you download, because the project moves fast.

O que é o DoopWhat Doop is

O Doop é uma alternativa de código aberto ao Paper.design: uma tela de design multiusuário onde pessoas e agentes de IA desenham juntos, em tempo real. Cada design vive numa tela compartilhável (/c/<id>) feita de frames que renderizam HTML dentro de iframes isolados. Pessoas editam no navegador; agentes editam por um servidor MCP embutido. A licença é AGPL-3.0; o nome e o logo são marcas registradas e a licença não os cobre.Doop is an open-source alternative to Paper.design: a multiplayer design canvas where people and AI agents design together in real time. Each design lives on a shareable canvas (/c/<id>) made of frames that render HTML inside sandboxed iframes. People edit in the browser; agents edit through a built-in MCP server. The license is AGPL-3.0; the name and logo are trademarks and the license does not cover them.

FunçãoFeatureO que fazWhat it does
Design por agentes (MCP)Agent design (MCP)Claude Code, Codex, ChatGPT, Cursor, OpenCode e outros clientes MCP desenham na tela, tiram screenshots e revisam o próprio trabalho.Claude Code, Codex, ChatGPT, Cursor, OpenCode and other MCP clients draw on the canvas, take screenshots and review their own work.
Agente embutidoBuilt-in agentO Doop Agent roda numa conta de modelo que você conecta (inclusive assinatura do ChatGPT) ou numa chave OpenAI, Claude, Gemini ou OpenRouter. Há uma camada gratuita opcional paga pelo servidor.The Doop Agent runs on a model account you connect (including a ChatGPT subscription) or on an OpenAI, Claude, Gemini or OpenRouter key. There is an optional free tier paid by the server.
EditorEditorFrames, retângulos, círculos, caminhos de caneta e texto, com layout flex, réguas, guias e zoom de 8% a 1600%.Frames, rectangles, circles, pen paths and text, with flex layout, rulers, guides and zoom from 8% to 1600%.
MultiusuárioMultiplayerCursores ao vivo, presença, modo seguir, comentários em fio e feed de atividade, numa sala WebSocket por tela.Live cursors, presence, follow mode, threaded comments and an activity feed, over one WebSocket room per canvas.
Protótipo e aprovaçãoPrototype and approvalInterações clicáveis entre frames (/c/<id>/proto) e frames marcados “pronto para dev”, que os agentes podem consultar.Clickable interactions between frames (/c/<id>/proto) and frames marked “ready for dev”, which agents can query.
Memória de designDesign memoryGuias de estilo, referências fixadas e decisões registradas que os agentes seguem.Style guides, pinned references and recorded decisions that agents follow.
Importar e exportarImport and exportCaptura de sites e apps, revisão de repositório do GitHub; exporta PNG, JPG e PDF. Figma, Canva e PSD só no Doop Cloud.Website and app capture, GitHub repo review; exports PNG, JPG and PDF. Figma, Canva and PSD are Doop Cloud only.
ContasAccountsE-mail e senha, OIDC SSO, Google, Microsoft e Apple, workspaces e plano Team opcional com Stripe.Email and password, OIDC SSO, Google, Microsoft and Apple, workspaces and an optional Stripe-backed Team plan.

Como ele é por dentroHow it works inside

O repositório separa o servidor, o cliente e o código compartilhado. Isso importa na hora de hospedar: em produção é um único processo Node na porta 4400 que serve a API, o WebSocket, o MCP e o cliente já compilado.The repository splits the server, the client and the shared code. That matters when hosting: in production it is a single Node process on port 4400 that serves the API, the WebSocket, the MCP and the built client.

PastaFolderPapelRole
server/Node na porta 4400: API REST (Express), salas WebSocket, presença, servidor MCP (HTTP em streaming sem estado em /mcp), better-auth, Doop Agent e automações. O estado quente fica em memória e é gravado no banco.Node on port 4400: REST API (Express), WebSocket rooms, presence, the MCP server (stateless streamable HTTP at /mcp), better-auth, the Doop Agent and automations. Hot state is in memory and written through to the database.
src/Cliente React + Vite + zustand na porta 4300 (só no desenvolvimento; ele repassa /api, /ws e /mcp ao servidor).React + Vite + zustand client on port 4300 (development only; it proxies /api, /ws and /mcp to the server).
shared/Tipos e lógica usados pelos dois lados.Types and logic used by both sides.
desktop/, ios/, claude-extension/, cursor-plugin/, chatgpt-plugin/Pacotes de clientes: app desktop em Tauri (macOS e Windows), app iOS em SwiftUI (em desenvolvimento) e plugins.Client packages: a Tauri desktop app (macOS and Windows), a SwiftUI iOS app (in development) and plugins.

Stack: Bun (instalar e compilar), Node (rodar o servidor), TypeScript, Express, ws, Drizzle ORM, PGlite (Postgres embutido) ou Postgres, better-auth, React, Tailwind e Vitest. Cada frame é um iframe com sandbox="allow-scripts", atualizado por “morph” do DOM em vez de recarregar. As imagens dos frames vêm do puppeteer-core dirigindo o Chrome ou Chromium do sistema.Stack: Bun (install and build), Node (server runtime), TypeScript, Express, ws, Drizzle ORM, PGlite (embedded Postgres) or Postgres, better-auth, React, Tailwind and Vitest. Each frame is an iframe with sandbox="allow-scripts", updated by DOM morphing instead of reloading. Frame screenshots come from puppeteer-core driving the system Chrome or Chromium.

Rodar no seu computadorRun it on your computer

Requisitos: Bun (instala e compila), Node no PATH (o bun run dev e o bun run build também empacotam a extensão do Claude Desktop com Node) e Chrome ou Chromium para os screenshots (detectado sozinho, ou defina CHROME_PATH).Requirements: Bun (install and build), Node on your PATH (bun run dev and bun run build also package the Claude Desktop extension with Node) and Chrome or Chromium for screenshots (auto-detected, or set CHROME_PATH).

git clone https://github.com/kgoedecke/doop && cd doop
bun install
bun run dev

Abra http://localhost:4300 (o app). A API, o WebSocket e o MCP ficam em http://localhost:4400. Não precisa de configuração: os dados vão para um Postgres embutido (PGlite) em data/pg. Apague essa pasta para zerar tudo.Open http://localhost:4300 (the app). The API, WebSocket and MCP are on http://localhost:4400. No configuration is needed: data goes to an embedded Postgres (PGlite) in data/pg. Delete that folder to reset everything.

Para testar o build de produção sem Docker, num único processo:To try the production build without Docker, as a single process:

bun run build && bun run start
# tudo em http://localhost:4400
# com um Postgres de verdade: DATABASE_URL=postgres://usuario:senha@host:5432/doop bun run start

Rodar com DockerRun it with Docker

O Compose do projeto sobe o app e um Postgres, na porta 4400. O BETTER_AUTH_SECRET assina as sessões: gere um valor longo e aleatório e guarde-o, porque trocá-lo derruba os logins.The project Compose file starts the app and a Postgres on port 4400. BETTER_AUTH_SECRET signs sessions: generate a long random value and keep it, because changing it logs everyone out.

BETTER_AUTH_SECRET=$(openssl rand -hex 32) docker compose up -d

Para só conferir a imagem de produção na sua máquina:To just check the production image on your machine:

docker build -t doop .
docker run -p 4400:4400 \
  -e BETTER_AUTH_URL=http://localhost:4400 \
  -e BETTER_AUTH_SECRET=dev-only doop
O segredo dev-only serve apenas para esse teste local. Nunca o use numa máquina acessível de fora. Sem DATABASE_URL o contêiner usa PGlite: monte um volume em /app/data ou os dados somem quando o contêiner for recriado.The dev-only secret is for that local test only. Never use it on a machine reachable from outside. Without DATABASE_URL the container uses PGlite: mount a volume at /app/data or the data disappears when the container is recreated.

Rodar numa nuvemRun it in a cloud

O Dockerfile de produção já inclui o cliente compilado e o Chromium. A documentação indica o Railway e o Fly como o caminho de menor atrito, mas qualquer lugar que rode um contêiner serve (uma VPS com Docker Compose, ECS, Cloud Run).The production Dockerfile already bundles the built client and Chromium. The documentation names Railway and Fly as the least-friction path, but anywhere that runs a container works (a VPS with Docker Compose, ECS, Cloud Run).

  1. Crie o app a partir do repositório. As duas plataformas detectam o Dockerfile sozinhas.Create the app from the repository. Both platforms detect the Dockerfile on their own.
  2. Adicione um Postgres gerenciado e defina DATABASE_URL. O PGlite é de um processo só: só serve para uma instância com um volume persistente em /app/data.Add a managed Postgres and set DATABASE_URL. PGlite is single-process: it only suits one instance with a persistent volume at /app/data.
  3. Defina BETTER_AUTH_SECRET (string longa e aleatória, obrigatória em produção) e BETTER_AUTH_URL (a origem pública, como https://doop.exemplo.com, também obrigatória). Use TRUSTED_ORIGINS para outras origens permitidas.Set BETTER_AUTH_SECRET (a long random string, required in production) and BETTER_AUTH_URL (the public origin, such as https://doop.example.com, also required). Use TRUSTED_ORIGINS for any other allowed origins.
  4. Use GET /healthz como health check. O servidor confia em um salto de proxy, então terminar o TLS na borda da plataforma funciona.Use GET /healthz as the health check. The server trusts one proxy hop, so terminating TLS at the platform edge works.
  5. Se quiser, defina um SMTP, o armazenamento S3 e os logins sociais (próxima seção).Optionally set SMTP, S3 storage and social sign-in (next section).

Exemplo numa VPS qualquer, com Postgres no mesmo Compose e HTTPS na frente (Caddy ou Cloudflare Tunnel, que não precisam de portas abertas além da 443):Example on any VPS, with Postgres in the same Compose and HTTPS in front (Caddy or a Cloudflare Tunnel, which need no ports open besides 443):

# no servidor, dentro de uma cópia do repositório
cat > .env <<EOF
BETTER_AUTH_SECRET=$(openssl rand -hex 32)
BETTER_AUTH_URL=https://doop.exemplo.com
EOF
docker compose up -d
curl -fsS http://127.0.0.1:4400/healthz   # health check
# coloque o proxy HTTPS na frente da porta 4400
Se o SMTP estiver bloqueado (comum em PaaS), o cadastro ou a recuperação de senha pode travar. A documentação cita o Resend com SMTP_PORT=2587 como contorno. Sem SMTP configurado, os e-mails só aparecem no log do servidor.If SMTP is blocked (common on PaaS hosts), signup or password reset can hang. The documentation mentions Resend with SMTP_PORT=2587 as a workaround. With no SMTP configured, emails only show up in the server log.

Variáveis de ambiente que importamEnvironment variables that matter

ÁreaAreaVariáveisVariablesSem elasWithout them
NúcleoCoreBETTER_AUTH_SECRET, BETTER_AUTH_URL, DATABASE_URL, TRUSTED_ORIGINS, PORTPGlite embutido em data/pg; origens localhost (as duas primeiras são obrigatórias em produção)Embedded PGlite in data/pg; localhost origins (the first two are required in production)
E-mailEmailSMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS, SMTP_SECURE, EMAIL_FROM, REQUIRE_EMAIL_VERIFICATIONE-mails vão para o log; sem verificaçãoEmails go to the log; no verification
LoginSign-inOIDC_*, GOOGLE_*, MICROSOFT_*, APPLE_*, SIGNUP_EMAIL_DOMAINS, ADMIN_EMAILSSó e-mail e senha; cadastro abertoEmail and password only; open signup
Doop AgentDoop AgentANTHROPIC_API_KEY, RESIDENT_TASK_LIMIT, DOOP_AGENT_PROVIDER, DOOP_AGENT_MODEL, AZURE_OPENAI_*Só roda em contas que os usuários conectam (RESIDENT_TASK_LIMIT padrão 0)Only runs on accounts users connect (RESIDENT_TASK_LIMIT defaults to 0)
ArmazenamentoStorageBUCKET, ACCESS_KEY_ID, SECRET_ACCESS_KEY, ENDPOINT, REGIONArquivos em data/assets (S3 compatível se definido)Files in data/assets (S3-compatible if set)
ScreenshotsScreenshotsCHROME_PATH, CHROME_NO_SANDBOXChrome ou Chromium detectado sozinhoChrome or Chromium auto-detected
CobrançaBillingSTRIPE_SECRET_KEY, STRIPE_PRICE_TEAM_*, STRIPE_WEBHOOK_SECRETCobrança desligada; todo workspace ativoBilling off; every workspace active

O arquivo .env.example do repositório lista o conjunto completo (endpoints, chaves de geração de imagem e busca, limites de taxa).The repository .env.example lists the full set (endpoint overrides, image generation and search keys, rate limits).

Conectar agentes de IAConnecting AI agents

O endpoint MCP é /mcp. Em desenvolvimento, http://localhost:4300/mcp; em produção, a sua origem pública mais /mcp.The MCP endpoint is /mcp. In development, http://localhost:4300/mcp; in production, your public origin plus /mcp.

# Claude Code (login pelo navegador, OAuth)
claude mcp add --transport http doop http://localhost:4300/mcp

# Codex
codex mcp add doop --url http://localhost:4300/mcp
codex mcp login doop

Configuração genérica para qualquer cliente MCP:Generic configuration for any MCP client:

{ "mcpServers": { "doop": { "type": "http", "url": "http://localhost:4300/mcp" } } }

Para agentes sem navegador (CI, servidores), crie uma chave de agente em Configurações → Agent keys e envie-a como cabeçalho Authorization: Bearer. Ela dispensa o login e, ao ser revogada, corta o acesso na hora. Use uma chave por agente. O Doop Agent embutido fica desligado até um usuário conectar uma conta de modelo ou o operador definir ANTHROPIC_API_KEY e um RESIDENT_TASK_LIMIT maior que 0; agentes ligados por MCP usam as próprias assinaturas.For agents without a browser (CI, servers), create an agent key under Settings → Agent keys and send it as an Authorization: Bearer header. It skips the sign-in and, once revoked, cuts access immediately. Use one key per agent. The built-in Doop Agent stays off until a user connects a model account or the operator sets ANTHROPIC_API_KEY and a RESIDENT_TASK_LIMIT above 0; agents connected over MCP use their own subscriptions.

Atualizar quando o projeto melhorarUpdating when the project improves

O Doop ainda é jovem (versão 0.x, lançamentos frequentes), então atualizar faz parte da rotina. O que eu vi no repositório em 2026-10-29: o ramo padrão é main; os lançamentos são tags vX.Y.Z geradas pelo release-please (a última do servidor era v0.10.0); o CHANGELOG.md lista cada versão; tags desktop-v… são só do app desktop; as mudanças entram por squash no main, e há dezenas de ramos feat/* e fix/* de trabalho em andamento.Doop is still young (0.x versions, frequent releases), so updating is part of the routine. What I saw in the repository on 2026-10-29: the default branch is main; releases are vX.Y.Z tags produced by release-please (the latest server one was v0.10.0); CHANGELOG.md lists every version; desktop-v… tags are the desktop app only; changes land as squashes on main, and there are dozens of work-in-progress feat/* and fix/* branches.

UsoUseO que seguirWhat to followPor quêWhy
Produção ou uso diário (recomendado)Production or daily use (recommended)Uma tag de versão (v0.10.0, a mais recente que não comece com desktop-)A version tag (v0.10.0, the newest one that does not start with desktop-)É um ponto fixo e reproduzível, com notas no changelog, e dá para voltar para a anterior.It is a fixed, reproducible point with changelog notes, and you can go back to the previous one.
Testar novidades, contribuirTrying new things, contributingmainTem tudo o que já foi aceito, mas entre dois lançamentos pode estar quebrado ou mudar o banco.It has everything accepted so far, but between two releases it can be broken or change the database.
EvitarAvoidRamos feat/*, fix/*, ci/*feat/*, fix/*, ci/* branchesTrabalho em andamento, que pode ser reescrito ou apagado.Work in progress that can be rewritten or deleted.

Se você só clonou, o jeito simples é fixar numa tag. Se pretende ter alterações suas, faça um fork e mantenha um ramo seu em cima da tag.If you only cloned, the simple way is to pin to a tag. If you plan to keep changes of your own, make a fork and keep a branch of yours on top of the tag.

Passo a passo (Docker Compose ou Bun)Step by step (Docker Compose or Bun)

cd doop
git fetch --tags origin
git tag -l 'v*' --sort=-v:refname | head -5     # as versões mais recentes / the newest versions
git log --oneline HEAD..origin/main | head -20  # o que mudou desde a sua versão / what changed

# 1) leia o CHANGELOG e o .env.example novo (v0.11.0 é só um exemplo: use a tag que aparecer acima) / read them (v0.11.0 is just an example: use the tag listed above)
git diff HEAD v0.11.0 -- CHANGELOG.md .env.example docker-compose.yml

# 2) faça backup ANTES (banco e arquivos)
docker compose exec db pg_dump -U doop doop > backup-$(date +%F).sql

# 3) troque para a tag nova (exemplo: v0.11.0)
git checkout v0.11.0

# 4a) Docker Compose: recompile a imagem e recrie
docker compose build --pull && docker compose up -d
# 4b) Bun, sem Docker
bun install && bun run build && bun run start

# 5) confira
curl -fsS http://127.0.0.1:4400/healthz

Para acompanhar o main em vez de uma tag, troque o passo 3 por git checkout main && git pull --ff-only. Se você tem commits seus numa cópia (fork), use git remote add upstream https://github.com/kgoedecke/doop, depois git fetch upstream --tags e git rebase v0.11.0 (ou git merge v0.11.0) no seu ramo.To follow main instead of a tag, replace step 3 with git checkout main && git pull --ff-only. If you have commits of your own in a copy (fork), use git remote add upstream https://github.com/kgoedecke/doop, then git fetch upstream --tags and git rebase v0.11.0 (or git merge v0.11.0) on your branch.

  • Seus dados ficam fora do código. No Compose, o banco está no volume doop-pg e sobrevive ao docker compose up -d; não use down -v, que apaga o volume. Com PGlite, o dado está em data/pg (ou no volume em /app/data) e o git checkout não mexe nele.Your data lives outside the code. In Compose the database is in the doop-pg volume and survives docker compose up -d; do not use down -v, which deletes the volume. With PGlite the data is in data/pg (or the volume at /app/data) and git checkout does not touch it.
  • Mudanças de esquema. O projeto usa migrações do Drizzle (pasta server/db). Eu não confirmei se elas rodam sozinhas ao iniciar; por isso o backup do passo 2 e a leitura do changelog antes de subir uma versão nova em produção.Schema changes. The project uses Drizzle migrations (the server/db folder). I did not confirm whether they run on their own at startup; hence the backup in step 2 and reading the changelog before putting a new version in production.
  • Troque uma versão por vez quando houver muitas entre a sua e a nova, e teste primeiro numa cópia (outra porta, outro banco).Move one version at a time when there are many between yours and the new one, and test first on a copy (another port, another database).
  • Voltar atrás: git checkout <tag-anterior>, recompile e, se o esquema mudou, restaure o backup (docker compose exec -T db psql -U doop doop < backup.sql num banco vazio).Rolling back: git checkout <previous-tag>, rebuild and, if the schema changed, restore the backup (docker compose exec -T db psql -U doop doop < backup.sql into an empty database).
  • Avisos de nova versão: no GitHub, “Watch” → “Custom” → “Releases” no repositório, ou assine o feed https://github.com/kgoedecke/doop/releases.atom.New-version alerts: on GitHub, “Watch” → “Custom” → “Releases” on the repository, or subscribe to the feed https://github.com/kgoedecke/doop/releases.atom.

Cuidados antes de abrir para outras pessoasCare before opening it to other people

  • Cadastro aberto por padrão. Sem SIGNUP_EMAIL_DOMAINS, qualquer pessoa que alcance o endereço cria uma conta. Restrinja por domínio, ou deixe o serviço atrás de VPN, Tailscale ou Cloudflare Access.Open signup by default. Without SIGNUP_EMAIL_DOMAINS, anyone who can reach the address creates an account. Restrict by domain, or keep the service behind a VPN, Tailscale or Cloudflare Access.
  • Um agente com acesso à tela escreve HTML que roda. Os frames ficam em iframes com sandbox="allow-scripts", mas trate o conteúdo gerado por agentes como não confiável e não cole nele segredos.An agent with canvas access writes HTML that runs. Frames sit in iframes with sandbox="allow-scripts", but treat agent-generated content as untrusted and do not paste secrets into it.
  • O servidor dirige um Chrome. CHROME_NO_SANDBOX existe para contêineres sem as permissões do sandbox; só o ligue se precisar e dentro de um contêiner sem privilégios.The server drives a Chrome. CHROME_NO_SANDBOX exists for containers without sandbox permissions; only turn it on if you must, and inside an unprivileged container.
  • Backup. Faça backup do Postgres (pg_dump) e do armazenamento de arquivos (data/assets ou o bucket S3).Backup. Back up Postgres (pg_dump) and file storage (data/assets or the S3 bucket).
  • AGPL-3.0. Se você modificar o Doop e oferecê-lo a outras pessoas pela rede, precisa disponibilizar o código modificado a elas. As marcas “doop” e o logo não vêm com a licença.AGPL-3.0. If you modify Doop and offer it to other people over a network, you must make the modified source available to them. The “doop” name and logo do not come with the license.

Cola e revisãoCheat sheet and review

ColaCheat sheet

AssuntoTopicRegraRule
DoopTela de design multiusuário com agentes via MCP; AGPL-3.0; um processo Node na 4400 em produção.Multiplayer design canvas with agents over MCP; AGPL-3.0; one Node process on 4400 in production.
LocalLocalbun install e bun run dev; app em 4300, API e MCP em 4400; dados em data/pg.bun install and bun run dev; app on 4300, API and MCP on 4400; data in data/pg.
Dockerdocker compose up -d com BETTER_AUTH_SECRET; volume em /app/data se usar PGlite.docker compose up -d with BETTER_AUTH_SECRET; a volume at /app/data if using PGlite.
NuvemCloudDockerfile + Postgres gerenciado + BETTER_AUTH_SECRET e BETTER_AUTH_URL + /healthz.Dockerfile + managed Postgres + BETTER_AUTH_SECRET and BETTER_AUTH_URL + /healthz.
AgentesAgents/mcp; OAuth no navegador, ou chave de agente em Authorization: Bearer./mcp; browser OAuth, or an agent key in Authorization: Bearer.
AtualizarUpdatingSiga uma tag vX.Y.Z (não desktop-v…), não ramos de trabalho; main só para testar. Backup, changelog, git checkout <tag>, recompilar; nunca down -v.Follow a vX.Y.Z tag (not desktop-v…), not work branches; main only for testing. Backup, changelog, git checkout <tag>, rebuild; never down -v.
CuidadosCareCadastro aberto por padrão; restrinja domínios; backup do banco e dos arquivos.Open signup by default; restrict domains; back up the database and files.

Perguntas de revisãoReview questions

  • Por que o Doop usa duas portas no desenvolvimento e uma só em produção?Why does Doop use two ports in development and only one in production?
  • Quando o PGlite basta e quando você precisa de um Postgres gerenciado?When is PGlite enough and when do you need a managed Postgres?
  • Quais duas variáveis são obrigatórias em produção, e por quê?Which two variables are required in production, and why?
  • Qual a diferença entre o login por OAuth e uma chave de agente?What is the difference between OAuth sign-in and an agent key?
  • Qual ramo ou tag você seguiria em produção, e o que fazer antes de atualizar?Which branch or tag would you follow in production, and what do you do before updating?
FontesSources