# Arquitetura

Visão de componentes e dos fluxos principais do AgentPack. Termos de domínio estão no
[glossário](glossary.md); o porquê das decisões, nos [ADRs](adr/README.md); as tabelas,
no [modelo de dados](data-model.md) (gerado).

## Componentes

```mermaid
flowchart LR
  subgraph Clientes
    UI[Frontend web<br/>React + Vite]
    AG[Agente de IA<br/>cliente MCP]
    CLI[Scripts / curl]
  end
  subgraph Backend["backend (Fastify 5)"]
    REST["/api/* REST"]
    MCP["/mcp gateway<br/>Streamable HTTP, stateless"]
    HEALTH["/health, /health/live,<br/>/health/ready, /metrics"]
    SVC[services/<br/>regra de negócio]
  end
  PG[(PostgreSQL 16+<br/>ltree, pg_trgm)]
  RD[(Redis opcional<br/>cache e rate limit)]
  FS[(/data<br/>anexos e assets)]
  SHOT[shot/<br/>Chromium headless]
  UP[MCPs externos]
  GH[api.github.com]

  UI -->|JWT| REST
  AG -->|Bearer: apk_* ou token| MCP
  CLI --> REST
  REST --> SVC
  MCP --> SVC
  MCP -->|proxy, Client do SDK| UP
  SVC --> PG
  SVC --> RD
  SVC --> FS
  SVC -->|x-shot-token| SHOT
  SVC -->|safeFetch| GH
  SHOT -->|carrega o preview| UI
```

- **Frontend** (`frontend/`): SPA servida pelo nginx em produção. Fala só com o backend
  (`VITE_API_URL`/`VITE_MCP_URL`), com o JWT do usuário no `sessionStorage`.
- **Backend** (`backend/`): um processo Fastify que serve a API REST, o gateway MCP, os
  health checks, as métricas Prometheus e os arquivos `llms.txt`/`llms-full.txt`.
- **PostgreSQL**: a única fonte da verdade. O schema vem só das migrations
  (`prisma migrate deploy` no entrypoint da imagem). Extensões `ltree` (hierarquia do
  knowledge) e `pg_trgm` (busca aproximada).
- **Redis** (opcional): cache de knowledge e contadores de rate limit. Sem `REDIS_URL`
  o cache cai para memória do processo e o rate limit conta por instância.
- **`/data`**: volume com os anexos de task (`TASK_ATTACHMENTS_DIR`) e os assets do
  Render (`RENDER_ASSETS_DIR`). Entra no backup junto com o banco
  ([runbook](ops/backup-restore.md)).
- **shot** (`shot/`): serviço separado que abre a página de preview do Render num
  Chromium e devolve a PNG. Autenticado por `x-shot-token`, com allowlist de origens e
  egress fechado. Sem `SHOT_SERVICE_URL`, as tools de captura devolvem só a URL.

## Camadas do backend

```
routes/ (REST)  ─┐
tools/ (MCP)    ─┼─▶ services/ ─▶ lib/ (prisma, safe-fetch, cache, sanitize...)
mcp-server.ts   ─┘
```

Uma operação de domínio tem uma única função de service, usada pela rota e pela tool
(BE-M4). `routes/`, `tools/` e o gateway não importam `lib/prisma`; `services/` e
`lib/` não importam `routes/` nem `tools/`. O `eslint` avisa e o `madge` barra ciclos.

A validação é zod em todas as pontas: o schema da rota gera a validação, a
serialização da resposta e o OpenAPI (`/docs`, `/docs/json`). As tools MCP usam o
mesmo schema de domínio em `schemas/`.

## Fluxo de uma requisição REST

1. `request-id`: aceita `X-Request-Id`/`traceparent` válidos ou gera um id; ele vira o
   `reqId` de todo log e volta no header `x-request-id`.
2. `cors` (Origin fora da lista → 403), `mcp-guard` (Host do `/mcp`), `rate-limit` por
   IP, cabeçalhos de segurança.
3. `auth`: resolve o principal (`request.actor`) a partir do Bearer (JWT de usuário,
   chave de agente `apk_*` ou o token de bootstrap). Sem credencial, 401 com
   `WWW-Authenticate`, exceto nas rotas `PUBLIC_ROUTE`. Escrita exige o escopo `write`.
4. Rate limit por cliente e validação zod (422 com `errors[]`).
5. Handler fino → service → Prisma. Autorização por objeto: o service confere que o
   recurso pertence ao projeto da URL e que a chave alcança o projeto.
6. Erro em qualquer ponto vira `application/problem+json` (RFC 9457) pelo
   `problem-details`; 4xx loga em warn, 5xx em error com stack.

## Fluxo do `/mcp`

```mermaid
sequenceDiagram
  participant A as Agente
  participant G as /mcp (gateway)
  participant C as Catálogo
  participant S as services/
  participant U as MCP externo
  A->>G: POST initialize / tools/list / tools/call (Bearer, X-AgentPack-Project)
  G->>G: auth, Origin/Host, Content-Type, rate limit
  G->>C: toolsets pedidos ∩ toolsets da chave ∩ escopo do principal
  alt tool built-in (agentpack_*)
    G->>S: handler da tool (mesmo service do REST)
    S-->>G: resultado ou erro de domínio (isError)
  else tool proxied (<toolPrefix>_<tool>)
    G->>U: tools/call pelo Client do SDK (pool por principal e projeto)
    U-->>G: CallToolResult
  end
  G-->>A: JSON-RPC (application/json)
```

- **Stateless**: sem `Mcp-Session-Id`; cada POST é independente, então qualquer
  réplica atende. `GET`/`DELETE /mcp` respondem 405.
- **Toolsets**: `core`, `tasks`, `knowledge`, `skills` e `proxied` por padrão; `decisions`
  (as tools de decisão humana), os `<domínio>-admin`, `prompts*`, `render`, `render-admin` e
  `github` são opt-in pelo header `x-agentpack-toolsets`. O catálogo
  completo, com escopos, está em [docs/mcp/tools.md](mcp/tools.md).
- **Escopo por projeto**: `X-AgentPack-Project` limita as tools externas aos MCPs
  vinculados ao projeto; a chave `apk_*` só alcança os projetos dela.
- Detalhes do transporte, do proxy e dos limites: `backend/docs/mcp-server.md`.

## Portão de decisão humana (HITL)

Uma task pode nascer com decisões (`TaskDecision`). Enquanto houver decisão
obrigatória sem resposta, a máquina de estados (`services/task-status.ts`) só aceita
`awaiting_human_decision`, `awaiting_more_information` e `cancelled`. Só um usuário
autenticado por JWT responde a decisão, pela interface ou pela API; nenhuma tool MCP
responde por humano. Toda mudança de status gera um `TaskEvent` com o ator real.

## Knowledge

Páginas em HTML ou Markdown, com hierarquia (`parentId` + caminho `ltree` mantido por
trigger), versões append-only (`KnowledgeVersion`, If-Match/`expectedVersion`),
sanitização no servidor (DOMPurify) antes de gravar, busca Postgres (FTS + `pg_trgm`),
lixeira e cache em camadas. As decisões originais estão nos ADR-001 a ADR-009.

## Render

Telas declarativas (`RenderDesign`/`RenderScreen` com árvore JSON por tema), bibliotecas
de componentes, estilos e tokens (`RenderLibrary`), design systems e portes entre
escopos (global → projeto). A escrita passa por um service transacional compartilhado
entre REST e MCP; o frontend tem o motor que renderiza a árvore (`frontend/src/render`)
com saneamento de atributos, URLs e CSS. O `shot` fotografa a página de preview.

## Operação

- Imagens: `backend/Dockerfile` (Node 24, usuário não root, `/data`, `HEALTHCHECK` em
  `/health/live`, migrations no entrypoint) e `frontend/Dockerfile` (nginx com CSP).
- Saúde: `/health/live` (processo, sem I/O), `/health/ready` (banco, Redis, shot e
  schema; 503 quando o banco cai ou no shutdown), `/health` (formato antigo).
- Observabilidade: logs JSON do pino com `reqId`, `GET /metrics` (Prometheus, com
  credencial) e o `errorId` das falhas internas do MCP, gravado na linha
  `[MCP] internal error` do log.
- Runbooks em [docs/ops/](ops/README.md).
