Arquitetura do Backend CRM

Phase 22.0–48. Status: DEPLOYED (VPS ao vivo, 68+ endpoints, arquitetura modular). Última atualização: 2026-04-28 (Phase 48 — Decomposição de Arquitetura)

Visão Geral

Browser (:18888)
  │
  ├── Static files ──► Nginx ──► Docker frontend (React + Nginx)
  │
  ├── /api/crm/* ────► Nginx ──► Bun Master Bot (:19210)
  │                                 ├── crmAuthMiddleware (HMAC-SHA256)
  │                                 └── shared/routes/router.ts → 17 domain modules
  │
  ├── /api/auth/* ───► Nginx ──► Bun (:19210) → master-bot/routes/auth.ts
  ├── /api/cli/*  ───► Nginx ──► Bun (:19210) → master-bot/routes/cli.ts
  ├── /api/internal/* ► Bun (:19210, loopback only) → master-bot/routes/internal.ts
  │
  ├── /api/sse/* ────► Nginx (unbuffered) ──► Bun (:19210)
  │
  └── /ws/terminal/* ► Nginx (upgrade) ──► Bun (:19210) → master-bot/routes/websocket.ts

Estrutura de Módulos (Phase 48)

API Server (master-bot/api-server.ts, 196 linhas) — dispatcher enxuto:

Rotas CRM (shared/routes/router.ts, 512 linhas) — tabela de dispatch para 17 módulos de domínio:

Fluxo de Autenticação

User ──► LoginOverlay (frontend)
         │
         ├── Email/password → POST /api/auth/login
         └── OAuth → /api/auth/{google,github} → callback
         │
         ▼
    Auth bem-sucedida → token HMAC-SHA256
         │
         ├── payload = { sub: userId, iat, exp: +24h }
         ├── signature = HMAC-SHA256(base64url(payload), CRM_SECRET)
         └── token = base64url(payload) + "." + signature
         │
         ▼
    localStorage('crm-token') → Todas as chamadas de API: Authorization: Bearer <token>
         │
         ▼
    Token expirado → 401 → login necessário

Arquivos-Chave

Arquivo Função
shared/auth.ts Geração/verificação de token, email/OAuth, reset de senha, verificação de email, CORS, middleware
shared/vault.ts Armazenamento seguro AES-256-GCM (CRM_SECRET criado automaticamente)
frontend/src/crm/components/LoginOverlay.jsx UI de login (email, OAuth, reset de senha, verificação de email)

Propriedades de Segurança

Rotas da API CRM

Todas as rotas sob /api/crm/ exigem Authorization: Bearer <token>.

GET /api/crm/projects

Lista todos os projetos registrados com status de health ao vivo. Inclui o master como pseudo-projeto (DEBT-6).

Campo Fonte
name, type, bot_username config/bot_registry.json (master: entrada virtual, type: "system")
healthy HTTP health check (children: porta do child, master: /api/master/health)
tmux_alive tmux has-session -t <name> (master: citadel-master)
status Arquivo de heartbeat ou derivado de health+tmux (master: healthy/degraded/down)

O master é sempre adicionado como primeira entrada no array de resposta.

GET /api/crm/projects/:name

Card de detalhes completo do projeto.

Campo Fonte
manifest Texto bruto de <cwd>/MANIFEST.md
heartbeat JSON de state/heartbeat_<name>.json
skills[] Nomes de arquivos <cwd>/skills/*.md (excluindo _*.md)
healthy, tmux_alive Mesmo que o endpoint de listagem

GET /api/crm/projects/:name/logs

Tail de logs estruturados JSONL.

Parâm. Padrão Máx.
lines 100 500
category dialog system, dialog, error

Fonte: /var/log/citadel/<name>/<category>-YYYY-MM-DD.log

Lê os arquivos de log mais recentes primeiro, faz parse do JSONL e retorna em ordem cronológica.

GET /api/crm/projects/:name/files

Listagem de diretório com proteção contra path traversal.

Parâm. Padrão Descrição
path / Path relativo dentro do cwd do projeto

Segurança: resolve(base, requested) deve resultar em startsWith(resolve(base)). Violação → 403.

Entradas da resposta: { name, type: "file"|"directory", size?, modified }, ordenadas com diretórios primeiro.

GET /api/crm/projects/:name/skills

Skills instaladas com atribuição de fonte.

Fonte Detecção
file <cwd>/skills/*.md (excluindo _*.md)
manifest MANIFEST.mdskills[] + library_skills[] (parse JSON)

Deduplicado: a fonte file tem prioridade sobre o manifest.

POST /api/crm/projects/:name/restart (Phase 22.3)

Reinicia um child bot via watchdog.

Aspecto Detalhe
Método Apenas POST (GET retorna 405)
Auth Token HMAC-SHA256 (mesmo das outras rotas CRM)
Cooldown 30 segundos por projeto (retorna 429 se muito frequente)
Mecanismo Chama restartChild() de watchdog.ts
Resposta { ok: true } ou { error: "..." }

GET /api/crm/projects/:name/metrics (Phase 22.1)

Série temporal de métricas de qualidade para gráficos sparkline.

Parâm. Padrão Descrição
days 7 Número de dias para agregar

Fonte: /var/log/citadel/<name>/quality-events.log → contagens de sucesso/falha por dia.

WS /ws/terminal/:name (Phase 22.3)

Terminal WebSocket transmitindo conteúdo do painel tmux.

Aspecto Detalhe
Auth Parâmetro de query ?token= (verificado via verifyToken().valid)
Modo Somente leitura por padrão; ?mode=interactive habilita send-keys
Poll tmux capture-pane -p -e -t {session} -S -80 a cada 200ms
Delta Envia apenas quando o conteúdo muda em relação ao anterior

Segurança: Proteção contra Path Traversal (Phase 22.3, DEBT-7)

Todos os endpoints validam nomes de projeto antes de processar:

const SAFE_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/;
export function isValidProjectName(name: string): boolean {
  return name.length > 0 && name.length <= 64 && SAFE_NAME_RE.test(name);
}

Aplicado em 3 pontos de entrada: routeCrmRequest(), routeSseRequest(), handler de upgrade WebSocket em bot.ts. Nomes inválidos → 403 Forbidden.

Integridade de Dados: Escritas Atômicas (Phase 22.3, DEBT-2)

Escritas no registry em onboarding.ts usam padrão atômico:

async function atomicWriteJson(filePath: string, data: unknown): Promise<void> {
  const tmp = `${filePath}.tmp.${process.pid}`;
  await Bun.write(tmp, JSON.stringify(data, null, 2));
  Bun.spawn(["mv", tmp, filePath]); // POSIX atomic rename
}

Arquivos-Chave

Arquivo Função Linhas
shared/crm-routes.ts 55+ handlers + roteador routeCrmRequest() + isValidProjectName() ~4800
shared/sage.ts Sage Worker: runSageAnalysis(), runBenchmark(), LLM judge, A/B testing ~550
shared/db.ts SQLite SSOT: userQueries, projectQueries, skillQueries, benchmarkQueries, chatQueries ~600
shared/auth.ts Geração de token, verificação, CORS, middleware ~100
master-bot/bot.ts Bun.serve: roteador HTTP + handler de terminal WebSocket ~1160
master-bot/onboarding.ts atomicWriteJson() para escritas no registry ~850

Proxy Nginx

Arquivo: infra/nginx/citadel-crm.conf — substitui a config legada citadel-os.

Três upstreams:

Upstream Porta Serviço
crm_backend 19210 Bun Master Bot (CRM API, health)
phaser_frontend 18889 Docker Phaser office (legado)
mcp_bridge 19200 Docker MCP state-bridge (legado)
Location Backend Auth Config Especial
/ phaser_frontend basic auth Docker Phaser frontend
/api/crm/ crm_backend token (sem basic) timeout 30s, keepalive
/api/master/health crm_backend nenhuma Health check público
/api/sse/ crm_backend token (sem basic) proxy_buffering off, timeout 1h (Phase 22.1)
/ws/ crm_backend token (sem basic) Upgrade WebSocket, timeout 1h (Phase 22.1)
/api/ mcp_bridge nenhuma (sem basic) API de estado legada + SSE
/health mcp_bridge nenhuma Health do bridge legado

Todos os locations encaminham X-Real-IP, X-Forwarded-For, X-Forwarded-Proto.

Paths bloqueados: /.* (dotfiles), /config/, /state/, /scripts/.

Nota: A prioridade de location do Nginx garante que /api/crm/ e /api/sse/ correspondam antes do catch-all /api/ (MCP legado).

Integração no Master Bot

master-bot/bot.ts Bun.serve (:19210):

async fetch(req) {
  /api/master/health  →  public, no auth
  /api/crm/*          →  CORS preflight → crmAuthMiddleware → routeCrmRequest()
  *                   →  404
}

O roteador despacha para funções handler de shared/crm-routes.ts. Headers CORS são injetados em toda resposta CRM (incluindo erros 401).

Resumo de Endpoints (Todas as Phases)

Phase Funcionalidade Endpoint Status
22.0 Lista de projetos GET /api/crm/projects DONE
22.0 Detalhes do projeto GET /api/crm/projects/:name DONE
22.0 Tail de logs GET /api/crm/projects/:name/logs DONE
22.0 Listagem de arquivos GET /api/crm/projects/:name/files DONE
22.0 Listagem de skills GET /api/crm/projects/:name/skills DONE
22.1 Streaming SSE de logs GET /api/sse/logs/:name DONE
22.1 Histórico de métricas GET /api/crm/projects/:name/metrics DONE
22.3 Reinício de projeto POST /api/crm/projects/:name/restart DONE
22.3 Terminal WebSocket WS /ws/terminal/:name DONE
24.5 CRUD de specs GET/POST /api/crm/projects/:name/specs DONE
24.5 Papel ativo GET/POST /api/crm/projects/:name/active-role DONE
25 Bundle de skills GET /api/crm/projects/:name/skills-bundle DONE
25 Sync de learnings GET/POST /api/crm/projects/:name/learnings DONE
26 CRUD de workers GET/POST /api/crm/projects/:name/workers DONE
32 Salvar wiki PUT /api/crm/projects/:name/wiki/save DONE
32 Salvar/excluir skills PUT/DELETE /api/crm/projects/:name/skills/* DONE
33 Configurações da conta GET/PUT /api/crm/account/settings DONE
33 Criar projeto POST /api/crm/projects/create DONE
34 CRUD de issues POST/GET/PUT /api/mcp/issues/:project DONE
34 Wiki MCP PUT /api/mcp/wiki/:project DONE
34 Sync de roadmap GET/PUT /api/mcp/roadmap/:project DONE
34 Init CLI GET /api/cli/init/:project/:mode DONE
35 Ingestão de log de terminal POST /api/crm/projects/:name/terminal/log DONE
36 Chat Cloud PM POST /api/crm/projects/:name/chat DONE
36.6 Geração de skill POST /api/crm/projects/:name/skills/generate DONE
36.7 Listagem de notebooks GET /api/crm/projects/:name/notebooks DONE

| 40.14 | Roadmap (conteúdo) | GET /api/mcp/roadmap/:project (+ campo content) | DONE |

Total: 46+ endpoints (REST + SSE + WebSocket + CLI/MCP)

NotebookLM Bridge (Phase 36.3)

Serviço Python FastAPI separado em :19213 (somente localhost). Não faz parte da API CRM Bun.

Endpoint Método Propósito
/query POST Busca semântica via Google NotebookLM
/sync POST Enfileira fonte para sync assíncrono (fire-and-forget)
/notebooks/init POST Cria notebook para novo projeto
/health GET Status de auth, contagem de notebooks, stats da fila

O CRM Bun dispara chamadas fire-and-forget para o bridge ao:

Dívida Técnica

Veja docs/backlog/technical-debt.md para o registry completo. Itens-chave:

ID Título Status
DEBT-2 Escritas JSON atômicas RESOLVIDO (Phase 22.3)
DEBT-3 Token Docker fixo → auth de sessão RESOLVIDO (Phase 31)
DEBT-6 Ponto cego do master bot no CRM RESOLVIDO (Phase 22.3)
DEBT-7 Defesa contra path traversal RESOLVIDO (Phase 22.3)

Mantido por Rick (Orchestrator). Atualizado após a conclusão de cada phase.