Repository navigation
Description
Contexto
O Thinker só tem um modo, o self-hosted. Hoje common/utils/detect-self-hosted.ts decide entre cloud e self-hosted por MANIFEST_MODE (cloud/selfhosted/local) ou por detecção de container (/.dockerenv, /run/.containerenv, KUBERNETES_SERVICE_HOST), e o padrão é cloud. Fora de container (ex.: npm run start em dev), o backend roda com semântica cloud. Há cerca de 244 referências a MANIFEST_MODE, entre código, testes, compose, deploy e docs.
Depois de #14, #15, #16 e #17, os ramos cloud que sobram estão em: setup/setup.service.ts, setup/setup.controller.ts, auth/cimd-client-metadata-fetch.ts, common/utils/provider-availability.ts, common/services/request-recording-storage.service.ts, routing/tier.controller.ts, routing/proxy/provider-client.ts, routing/custom-provider/custom-provider.service.ts e database/backfills/*.boot.service.ts.
Segurança. Remover o modo não significa adotar todas as permissões do ramo self-hosted. Há dois casos com tratamento diferente:
- CIMD (
auth/cimd-client-metadata-fetch.ts:79-81). O servidor busca o documento de metadados de um cliente MCP numa URLclient_idHTTPS que o próprio cliente informa durante o OAuth. HojeallowPrivate = isSelfHosted()libera hosts privados e de loopback e tolera falha de DNS. Se a remoção do modo deixasse isso incondicional, qualquer cliente MCP poderia fazer o servidor buscar URLs na rede interna (SSRF), sem precisar de conta. Decisão: o padrão passa a ser só endereços públicos, com opt-inTHINKER_CIMD_ALLOW_PRIVATE=truepara quem hospeda clientes MCP na LAN. - Provedores customizados e modelos locais (
routing/custom-provider/custom-provider.service.ts:309,412,555,validatePublicUrl(..., { allowPrivate: isSelfHosted() })). Aqui a permissão de URLshttpe IPs privados é uma funcionalidade: apontar para Ollama, LM Studio ou vLLM na LAN é um caso de uso central. A URL é informada por um usuário autenticado, não por um cliente anônimo. Decisão explícita: continua permitido por padrão; os endereços de metadados de nuvem continuam sempre bloqueados (url-validation.ts). Numa instância exposta, isso só é aceitável porque o cadastro fica fechado (Fechar o cadastro público depois do primeiro admin #29, mais adiante neste milestone). O endurecimento por organização fica para a v0.3.
Escopo
- Remover
isSelfHosted()e toda a detecção de modo; mantergetContainerHostAlias()(host.docker.internal/host.containers.internal). - Em cada arquivo listado acima, manter só o ramo self-hosted, exceto em
auth/cimd-client-metadata-fetch.ts. - CIMD:
allowPrivatepassa a vir deTHINKER_CIMD_ALLOW_PRIVATE(padrãofalse). Comfalse, endereços privados e de loopback são recusados e uma falha de DNS é erro (o comportamento atual do ramo cloud). Documentar a variável empackages/backend/.env.examplee emdocker/.env.example(comentada). - Provedores customizados:
validatePublicUrl(..., { allowPrivate: true })com um comentário que registre a decisão (LAN é funcionalidade; metadados de nuvem sempre bloqueados). - Remover o campo
isSelfHostedda resposta de status do setup (o frontend já não o lê desde Remover a camada SaaS do dashboard #12). - Remover
MANIFEST_MODEdedocker/docker-compose.yml,docker/.env.exampleepackages/backend/.env.example. Os templates dedeploy/ficam para Templates de deploy da comunidade #30. - Ajuste mínimo nas migrations antigas que ainda importem
isSelfHosted, mantendo o SQL do ramo self-hosted. - Testes: remover o
MANIFEST_MODEque specs e helpers e2e definem; cobrir que, sem nenhuma variável e fora de container, o comportamento é o self-hosted. - Testes de SSRF:
- unit em
cimd-client-metadata-fetch: host que resolve para endereço privado ou de loopback é recusado por padrão e aceito comTHINKER_CIMD_ALLOW_PRIVATE=true; - e2e: um fluxo OAuth MCP com
client_idapontando para uma URL que resolve para endereço privado é recusado por padrão; - e2e de provedor customizado: URL na LAN aceita;
169.254.169.254recusada.
- unit em
Fora de escopo
- Endurecimento de SSRF por organização (v0.3).
- Rebranding de outras variáveis
MANIFEST_*(Rebranding do backend: variáveis THINKER_*, chaves thk_ e cabeçalhos x-thinker-* #21).
Critérios de aceite
grep -rnE "MANIFEST_MODE|isSelfHosted|KUBERNETES_SERVICE_HOST" packages dockernão encontra nada.- Lint, unit e e2e passam (
custom-providers.url-validation,setup,request-recordings), incluindo os testes de SSRF acima. - Sem
THINKER_CIMD_ALLOW_PRIVATE, o e2e de CIMD comclient_idprivado é recusado. qa-docker: provedor customizado apontando para o mock emhost.docker.internalfunciona; gravação em filesystem funciona sem credenciais S3.
Dependências
- CI mínima provisória #38: CI mínima.
- Remover billing, Stripe e limites de plano do backend #14, Remover waitlist, CRM, Gemini Free, free-models, GitHub stars e páginas de erro públicas #15, Remover Autofix/Phoenix do backend #16, Remover phone-home do backend e da CLI #17
Agente responsável
backend-dev
Activity
patrickbrandao commentedon Oct 10, 2026
Encontrado durante a #12 (PR #51):
MANIFEST_MODEainda aparece emdocker/docker-compose.ymle emdocker/DOCKER_README.md.fix(cli): default to a local gateway instead of Manifest Cloud
patrickbrandao commentedon Oct 11, 2026
Do PR #70 (#17):
backend/src/auth/auth.instance.tsainda lista hosts da Manifest Cloud (app.manifest.buildegateway.manifest.build) como trusted origins do Better Auth e como resources de MCP. Remover junto com oMANIFEST_MODE. O guardno-phonehome-egressfixa esse arquivo numa lista de exceções; tirar daquela lista quando limpar.feat(backend)!: remove phone-home from the backend and the CLI (#70)
feat(backend): THINKER_CATALOG_SYNC and OpenCode Go doc only with a p…