Modelo de domínio
O modelo de dados do Inbox Autônomo tem nove recursos first-class mais algumas tabelas internas (tokens revogados, certs cliente revogados). Tudo é multi-tenant por Workspace — toda linha pertence a exatamente um workspace, e toda requisição da API é automaticamente filtrada pelo workspace derivado do claim sub do JWT.
Diagrama de relacionamentos
Workspace
│
┌───────────┬─────────┼─────────┬───────────┐
▼ ▼ ▼ ▼ ▼
Inbox Email Agent Webhook SystemEvent
│ │ │ │
│ │ │ └── WebhookDelivery
│ │ │
│ └── AgentRun ──┘
│
└── (Agent associado opcional)
Recursos em resumo
| Recurso | Propósito | Ciclo de vida |
|---|---|---|
| Workspace | Raiz multi-tenant, espelha uma conta Kobana | Criado via hook de provisionamento do kobana-app |
| Inbox | Endereço de e-mail roteável + política de processamento | Criado via API; desativado em vez de deletado |
| Mensagem recebida + estado de processamento | Criado pela ingestão SES ou por POST /emails | |
| Agent | Configuração de LLM (modelo, system prompt, skills) | Criado via API |
| AgentRun | Uma execução de um agente sobre um e-mail | Criado pelo worker; read-only via API |
| Webhook | Assinatura para um ou mais slugs de SystemEvent | Criado via API |
| WebhookDelivery | Uma tentativa de POST de payload de webhook | Criado pelo dispatcher; read-only via API |
| SystemEvent | Registro imutável de um evento de domínio | Emitido por handlers/workers; read-only via API |
Campos compartilhados
Todo recurso expõe:
| Campo | Tipo | Notas |
|---|---|---|
id | UUID v4 | Identificador interno. |
created_at | ISO 8601 | Definido na criação. |
updated_at | ISO 8601 | Atualizado pelo Prisma em cada mudança. |
deleted_at | ISO 8601 | null | Marcador de soft-delete. |
Alguns recursos adicionalmente expõem:
| Campo | Tipo | Notas |
|---|---|---|
external_id | UUID v4 | Identificador estável do sistema de origem (Workspace, Inbox, Agent). |
metadata | JSON | Metadata gerenciada pelo sistema. |
custom_metadata | JSON | Free-form, nunca inspecionada pelo sistema. |
Soft delete
Recursos que suportam deleção são soft-deletados: deleted_at é setado, mas a linha permanece no Postgres. Queries filtram por WHERE deleted_at IS NULL via middleware Prisma. Tentar criar um recurso que conflita com um soft-deletado (ex.: mesmo email em Inbox) retorna 409 conflict — restaure o original via tooling interno em vez de recriar.
Idempotência e unicidade
| Recurso | Constraint unique |
|---|---|
| Workspace | external_id |
| Inbox | email (global, não por-workspace) |
message_id (o messageId do SES) | |
| Agent | external_id |
| Webhook | — (label não é unique) |
A unicidade de Email.message_id é o que torna o handler de ingestão SES idempotente: o SNS pode reentregar a mesma notificação, mas o segundo insert bate na unique constraint e o handler retorna 200 imediatamente.