O projeto do CRM na Vercel foi desvinculado do GitHub por decisao do dono: a plataforma fica so com a landing page, em outro repositorio. Medido com a mesma sonda nos dois lados — `gh api repos/.../commits/<sha>/status` devolve `Vercel` no commitf65d04667(antes) e vazio ema8c9e1ad3,7168961ade no head do #1081 (depois). Com isso, dezenas de afirmacoes do repositorio publico viraram falsas. As que falavam no presente sairam: - mensagem automatica ao contribuinte, molde de PR, CONTRIBUTING e os espelhos na skill de contribuir prometiam um check "Vercel" vermelho que nao aparece mais. No lugar, a mensagem passa a dizer onde olhar o que trava o merge — os checks marcados Required no proprio PR, com a ressalva de que `--required` so lista os que ja reportaram; - runbooks de operacao mandavam abrir o painel da plataforma e redeployar a main; passam a descrever o `.env` da instalacao e a recriacao dos conteineres; - `lib/env.ts` mandava, no boot, ajustar variavel "na Vercel"; - specs e PRDs descreviam topologia, crons e guarda de segredo na plataforma; - comentarios de codigo ancoravam decisoes vivas em premissa morta. Registros datados (pesquisa, pitch, epicos, handoffs) nao foram reescritos: ganharam uma linha dizendo que sao registro da fase hospedada. O `vercel.ts` fica: ele serve quem hospeda um fork, e o gate `tests/unit/cron-routes-scheduled.test.ts` continua reprovando divergencia de rotas entre ele e o crontab do scheduler. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
20 KiB
title, parent, version, status, date, owner, referencia_arquitetural
| title | parent | version | status | date | owner | referencia_arquitetural |
|---|---|---|---|---|---|---|
| Sub-PRD 01 — Plataforma Base | 00-prd-master.md | 0.1 | em revisão | 2026-04-28 | Rafael Melgaço | docs/research/reference-synthesis.md |
Sub-PRD 01 — Plataforma Base
Foundation do DeskcommCRM. Todo subsistema posterior (Customer 360, WhatsApp, Pipeline, IA, Nuvemshop) depende das capacidades aqui definidas. Profundidade técnica (schema, RLS policies, payloads de API) vai pra
docs/specs/na próxima fase.
1. Contexto & Posicionamento
A Plataforma Base resolve o problema fundacional: todo dado tocado pelo DeskcommCRM precisa estar autenticado, isolado por tenant, autorizado por role, auditado por mutação, e respeitar LGPD desde o primeiro request. Sem isso, qualquer feature em cima vira risco regulatório e operacional.
A Plataforma Base também sustenta a administração transversal da instalação. Quando uma pessoa administradora precisa acompanhar uma organização, o acesso operacional é uma sessão temporária com alvo, modo e prazo próprios; não é um bypass permanente nem uma membership implícita. O contrato vigente está em docs/support-sessions.md.
Esta camada é invisível pro cliente final mas governa todas as garantias de segurança, conformidade e operação multi-tenant do produto.
2. Escopo
Dentro do escopo deste sub-PRD
- Autenticação de usuários (humanos via UI, server-to-server via API)
- Modelo de tenancy (organizations, user_organizations, super-admin)
- RBAC com 4 roles canônicas + super-admin de plataforma
- Audit trail de toda mutação relevante
- Framework LGPD (consentimento, redact, data_request)
- Onboarding de tenant (mesmo que ainda não exposto publicamente)
- Convenções da API REST
/api/v1/(auth, idempotência, paginação, formato de erro, rate limit)
Fora do escopo deste sub-PRD (vão pra outros sub-PRDs)
- Modelagem de cliente final / contatos / pedidos → Sub-PRD 02
- Captura/envio de mensagens WhatsApp → Sub-PRD 03
- Pipeline e tickets → Sub-PRD 04
- IA, RAG, sentiment → Sub-PRD 05
- Webhooks Nuvemshop → Sub-PRD 06
3. Capacidades Funcionais
3.1 Autenticação
O que provê. Login humano via Supabase Auth (email + senha + opcionalmente magic link); auth server-to-server via Bearer token (Authorization: Bearer tok_...); MFA TOTP forçado pra role admin e super-admin.
Princípios.
- Sempre
getUser()(valida JWT no backend), nuncagetSession()(confia no cookie local) - API key NUNCA aceita em query string (vaza em logs de Vercel/CloudFlare)
- Plaintext de Bearer token mostrado uma vez na criação; depois apenas hash SHA256 no DB
- Session timeout de 1h com refresh rotation habilitado
- Cookie SameSite=Strict, HttpOnly, Secure em produção
ACs principais.
- Login com email/senha bem-sucedido devolve JWT com claim
tenant_id - Tentativa de uso de token revogado retorna 401 com
error.code='token_revoked' - Admin sem MFA é forçado a configurar TOTP no primeiro login
- API key passada em query string retorna 400 com
error.code='auth_in_query_forbidden'
3.2 Tenancy multi-tenant
O que provê. Isolamento de dados forte por tenant via Postgres Row-Level Security em toda tabela tenant-aware. Helper canônico fn_user_org_ids() retorna lista de orgs do usuário; policies usam essa função pra filtrar.
Princípios.
organization_id uuid not null references organizations(id) on delete cascadeem toda tabela tenant-aware- Policy padrão
tenant_isolation_<tabela>_allaplicada a todas - Service role bypassa RLS apenas em handlers de webhook e cron, e deve filtrar manualmente
organization_idresolvido de fonte confiável (cookie, JWT, webhook secret, path token) — nunca do body - Roteamento de API pode usar header
X-Tenant-IDou subdomain (<tenant>.api.deskcomm.com); decisão final na Spec
ACs principais.
- Cliente A não consegue ler nenhum recurso do cliente B em nenhum endpoint
- Tentativa de inserir registro com
organization_iddiferente do JWT do usuário retorna 403 - Teste automatizado de isolamento (cria 2 tenants + verifica não-vazamento) passa no CI antes de qualquer merge
- Query do admin client com
bypassed_rls=trueé logada no audit trail
3.3 RBAC
O que provê. 4 roles canônicas hierarquicas dentro de cada tenant.
| Role | Hierarchy | Permissões |
|---|---|---|
viewer |
1 | GET em todos os recursos do tenant |
agent |
2 | + POST/PATCH em leads e activities atribuídos a si; sem DELETE |
manager |
3 | + DELETE leads; CRUD em pipelines e stages |
admin |
4 | Tudo, incluindo gestão de api_tokens, webhooks e visualização de audit log |
Princípios.
- Roles vivem em
user_organizations(junção user × org × role) - 1 usuário pode ter roles diferentes em orgs diferentes
- Permissão por pipeline (
user_pipeline_access) NÃO entra no MVP — adicionar quando cliente real pedir - Role mudança é evento auditado
ACs principais.
- Viewer não consegue criar lead (403
forbidden_role) - Agent só consegue editar leads atribuídos a si (
owner_user_id = self) - Audit trail registra mudança de role com
who/from/to/when
3.4 Super-admin de plataforma
O que provê. Autoridade transversal para administrar a instalação e iniciar acompanhamento temporário de uma organização sem assumir a identidade de cliente.
Princípios.
- A autoridade vive em
platform_admins;scopelimita o máximo efetivo e revogação encerra a continuidade. - Dados de uma organização são acompanhados por uma linha em
platform_support_sessions, ligada ao ator e àauth.session_id, com modofullousupport_readonlye TTL máximo de uma hora. - A sessão projeta a organização e o papel efetivos sem criar
user_organizations. Outra sessão do mesmo ator mantém seu próprio contexto, e direitos de plataforma preexistentes fora do alvo não são removidos. support_readonlyprevalece até sobre membership admin física no alvo; cercas de banco e guardas da aplicação bloqueiam efeitos sem paralisar workers ou outros usuários.- O início e cada uso reconfirmam autoridade, sessão, alvo, prazo e política de MFA. Expiração ou revogação exigem saída explícita; a saída restaura a organização anterior.
- Operações pelas superfícies do aplicativo registram o ator real e
support_session_id; não há promessa de auditoria de DML bruto fora dessas superfícies.
ACs principais.
- A pessoa administradora escolhe edição ou somente leitura no detalhe da organização e vê banner com ação de saída durante todo o acompanhamento.
- Outra sessão do mesmo ator não muda de organização. Expiração ou revogação retiram o snapshot até a saída; downgrade
full → support_readonlyretira escrita e estado de admin, mas mantém a leitura permitida na sessão ativa. - Somente leitura não marca inbox como lida, não executa mutações e continua restritiva mesmo se o ator for admin físico no alvo.
- O acompanhamento termina pela posse da própria sessão, mesmo se a autoridade de plataforma tiver sido revogada.
Contrato completo e limites OAuth: docs/support-sessions.md.
3.5 Audit trail
O que provê. Log denso de toda mutação relevante (CRUD em leads, pipelines, contacts, mensagens, configurações, tokens, webhooks). Imutável após inserção. Visível pra admin do tenant e super-admin de plataforma.
Princípios.
- Tabela
api_audit_logregistra:who(user_id, api_token_id, ip, user_agent),what(action canônicalead.created,token.revoked, etc.),which(resource_type, resource_id),when(created_at),metadata(diff, params relevantes) - Logging é fire-and-forget (nunca bloqueia request)
- GET não-batch não é logado por padrão (volume); GET de export massivo SIM
- Retenção 5 anos (boas práticas ANPD)
- Audit log NÃO recebe RLS policy de UPDATE/DELETE — é append-only
ACs principais.
- Toda mutação POST/PATCH/DELETE bem-sucedida gera 1 entrada de audit em ≤500ms p99
- Audit log não pode ser editado nem deletado via API (só via DBA manual com double-confirmation)
- Admin do tenant consulta audit log filtrável por
actor,action,resource_type,date_rangeem endpoint dedicado - Falha em escrever audit log gera alerta operacional (Sentry/PagerDuty), mas não impede a mutação principal
3.6 LGPD framework
O que provê. Mecanismos pro tenant (e pra ANPD em última instância) exercerem direitos LGPD: consentimento granular, exportação de dados pessoais, anonimização ou exclusão.
Princípios.
- Consentimento granular:
contacts.consentjsonb com chavesmarketing,transactional,profiling, cada uma com{granted_at, source, version} - Anonimização preferida sobre delete: vendas históricas precisam permanecer pro faturamento; nome/telefone/email do contato vira hash + token "Cliente Anonimizado #N"
- Delete físico apenas quando solicitado e sem dependências (raro: pré-venda sem nenhum pedido)
- Audit completo de toda operação em dados sensíveis (
lgpd.data_request_received,lgpd.export_generated,lgpd.redact_executed,lgpd.consent_changed) - SLA: data_request entregue em D+7 dias úteis; redact executado em D+15
- Imutabilidade: dados anonimizados não podem ser revertidos (decisão definitiva)
Endpoints (detalhe vai na Spec):
POST /api/v1/lgpd/data-request— recebe{contact_id | email | phone | cpf}, dispara job assíncrono que gera export estruturado (JSON + PDF assinado) e o entrega via email ou link assinadoPOST /api/v1/lgpd/redact— recebe{contact_id, mode: 'anonymize' | 'delete'}, valida pré-condições, executa cascadeGET /api/v1/contacts/:id/consent— lê estado de consentimentoPATCH /api/v1/contacts/:id/consent— atualiza com audit
ACs principais.
- Tenant admin solicita export de cliente X via UI → recebe PDF/JSON em D+7 com 100% dos dados pessoais armazenados
- Anonimização cascade afeta: contact, conversations (preserva histórico), messages (mídia removida do storage), activities (mantém timestamps e tipos)
- Tentativa de reverter anonimização retorna 403
lgpd_anonymization_irreversible - Audit do redact registra
who,which contact,mode,cascaded_to=[conversations:N, messages:M, activities:K]
3.7 Criação e primeiro acesso de uma organização
O que provê. Em Administração → Gerenciar organizações, uma pessoa com autoridade de plataforma full cria a organização e define o responsável. A porta continua visível mesmo quando ela possui um único vínculo.
Princípios.
- A criação valida UUID, payload, autoridade e MFA em dívida. Organização, vínculo
adminaceito do ator criador e recibo idempotente confiável são gravados na mesma transação; falha intermediária reverte o conjunto. - A chave idempotente pertence ao ator e ao payload normalizado, vale por 24 horas e permite recuperar a mesma organização e o mesmo link após perda de resposta, sem repetir envio nem auditoria da criação. Recibo antigo ou editável não é autoridade.
- Se o responsável é outra pessoa, o convite HMAC é emitido depois do commit. O link assinado de 24 horas é sempre exibido e pode ser copiado; ausência ou falha do Resend não desfaz a criação nem finge e-mail entregue.
- O aceite serializa o vínculo. Replay de vínculo já ativo não altera papel ou interface; vínculo revogado só pode ser reativado por convite emitido depois da revogação.
- O aceite ativa a organização convidada; troca normal continua sendo uma operação distinta, baseada em membership aceita e ativa.
- OAuth, conexão WhatsApp e seed de pipeline são fluxos independentes; este contrato de criação não os declara concluídos.
ACs principais.
- A criação devolve organização, validade, estado real do envio e link copiável mesmo sem Resend.
- Repetir a mesma intenção dentro do TTL recupera o mesmo resultado; trocar o payload sob a mesma chave retorna conflito.
- Responsável aceita o convite e entra na organização; replay não promove papel nem sobrescreve preferência alterada depois.
- Falha da troca mantém cookie, contexto e dados da organização anterior e apresenta erro.
3.8 API base & convenções
O que provê. Convenções universais que toda rota /api/v1/ seguirá, em todo subsistema.
| Aspecto | Convenção |
|---|---|
| Versionamento | Por path: /api/v1/, depois /api/v2/ |
| Formato | JSON snake_case em request e response |
| IDs | UUID v4 |
| Datas | ISO-8601 UTC |
| Dinheiro | _cents (bigint) + currency ISO-4217 |
| Wrapper de sucesso | { data: T, meta?: { cursor, has_more, total } } |
| Wrapper de erro | { error: { code, message, details? } } |
| Paginação | Cursor por default (cursor opaco base64 com HMAC) |
| Idempotência | Header Idempotency-Key: <uuid> em POST de criação; TTL 24h |
| Rate limit | Upstash Redis sliding window; headers X-RateLimit-* + Retry-After |
| Auth | Cookie session (frontend) OU Authorization: Bearer tok_... (server-to-server) |
| Status codes | 200 / 201 / 204 / 400 / 401 / 403 / 404 / 409 / 422 / 429 / 500 |
| CORS | Allowlist explícita por tenant; nunca * |
| Request ID | Header X-Request-Id correlaciona com audit log |
ACs principais.
- Toda response de erro segue o wrapper
{ error: { code, message, ...} } - Idempotency-Key recebida 2x com mesmo body retorna mesma response sem duplicar efeito; com body diferente retorna 409
idempotency_conflict - Rate limit excedido retorna 429 com
Retry-Aftereerror.code='rate_limited' - Cliente recebe
X-Request-Idem todo response e pode usá-lo pra suporte (audit log linka via esse ID)
4. Requisitos Não-Funcionais
4.1 Segurança
- Toda comunicação HTTPS (TLS 1.3 mínimo)
- Postgres com
pgcryptoativo pra encrypt at-rest de campos sensíveis quando aplicável - Sentry com
beforeSendremovendo headers/body sensíveis antes de enviar evento - Logs sanitizados:
x-api-key,authorization,cookie,password,webhook_secretfiltrados antes de gravar INTERNAL_SECRET(cron) diferente doSUPABASE_SERVICE_ROLE_KEY- DNS rebinding protection ativa em endpoints sensíveis
4.2 Performance
- p95 de auth check: <50ms
- p95 de tenant resolution + RLS query simples: <100ms
- p95 de mutação simples (POST/PATCH lead): <300ms
- p95 de audit log write: <500ms (fire-and-forget)
- Suporte concorrente: 100 RPS por tenant no MVP
4.3 Compliance
- LGPD desde o dia 1 (vide §3.6)
- Audit log retenção 5 anos
- Backup diário do Postgres (Supabase) + retenção 30 dias
- Documentação operacional (runbook LGPD) entregue antes do primeiro tenant em produção
4.4 Observabilidade
- Sentry pra erros (com sanitização)
- Métricas custom (latência, taxa de erro, audit log lag) em painel a definir na Spec
- Health check endpoint público
/api/v1/health(sem auth, retorna 200 + status de dependências)
5. Acceptance Criteria do sub-PRD
A Plataforma Base é considerada MVP-completa quando:
- ✅ 2 tenants podem ser criados pela porta administrativa com vínculo inicial e recibo idempotente atômicos; isolamento de dados é verificado por teste automatizado no CI
- ✅ MFA TOTP pode ser exigido pelas políticas independentes da plataforma e da organização; quem já possui fator verificado prova
aal2nas rotas protegidas - ✅ Bearer token criado pelo admin permite chamada server-to-server, e tem audit log de criação/uso
- ✅ Endpoint LGPD
data-requestgera export JSON + PDF em ≤7 dias úteis pra um contato real - ✅ Endpoint LGPD
redactanonimiza um contato com cascade pra conversations/messages/activities, sem perda de histórico de pedidos - ✅ Audit log captura todas as mutações listadas e não pode ser editado via API
- ✅ Super-admin inicia acompanhamento temporário em edição ou somente leitura, sai para o contexto anterior e mantém auditoria do ator real nas operações do aplicativo
- ✅ Rate limit funciona em endpoint crítico (login + criação de lead)
- ✅ Health check endpoint retorna 200 com status de Supabase, Redis e WAHA
- ✅ Documentação operacional (runbook LGPD + matriz RBAC + lista de eventos auditados) entregue
6. Dependências
Internas (outros sub-PRDs)
- Nenhuma. Plataforma Base é foundation de todos os outros.
Externas
- Supabase (Auth, Postgres, Storage) — projeto provisionado, plano Pro mínimo pra produção
- Vercel AI Gateway — só o gateway de LLM, via
AI_GATEWAY_API_KEY(pra Sub-PRDs futuros). Hospedagem não entra aqui: quem instala roda o CRM na própria infraestrutura - Upstash Redis — instância de produção pra rate limit
- Sentry — projeto criado, com regras de sanitização aprovadas
- Domínio + subdomínio
admin.deskcomm.com(pra UI super-admin)
Decisões deferidas pra Spec
- Tabela auxiliar
platform_adminsvs colunais_platform_adminemauth.users - Roteamento por subdomain vs header
X-Tenant-ID - Gerador de cursor (HMAC com qual chave?)
- Política exata de retenção de mensagens por tenant (configurável dentro de bounds)
7. Riscos Específicos do sub-PRD
| # | Risco | Mitigação |
|---|---|---|
| P1 | RLS policy mal escrita vaza dados cross-tenant | Templates de policy + linter SQL no CI + teste de isolamento obrigatório por tabela tenant-aware |
| P2 | Service role key vaza em log/repo | Variável de ambiente apenas; nunca em código; .gitleaks no pre-commit; rotação trimestral |
| P3 | MFA TOTP perdido bloqueia admin | Códigos de recuperação gerados na configuração; processo de reset via super-admin com audit duplo |
| P4 | Audit log explode em volume e custa caro | Retenção em hot storage 90 dias; rest em cold storage (S3) com lifecycle policy; sampling em GET-volume baixo |
| P5 | Super-admin abusivo vê dados sem necessidade | Toda ação loggeada com flag; revisão semanal pelo líder operacional; consideração futura de "modo restrito" exigindo justificativa por sessão |
| P6 | LGPD data_request demora além de D+7 | Job assíncrono com fila própria + alarme em D+5; runbook de escalação pra super-admin |
| P7 | Onboarding manual erra config (webhook secret, role inicial) | CLI script ou wizard guiado; validação automática pós-criação (ping em todos os endpoints essenciais) |
8. Fora de Escopo (deste sub-PRD)
- SSO empresarial (SAML, OIDC, Google Workspace) — entra em fase pós-MVP quando demanda enterprise surgir
- 2FA por SMS — não recomendado (SIM swap); só TOTP/WebAuthn
- Pentest formal externo — entra na Fase 1.5 (Hardening)
- Certificações SOC 2, ISO 27001 — entram na Fase 2+ conforme demanda comercial
- Multi-fator avançado (WebAuthn / passkey) — Fase 1.5 ou 2
- Self-service de criação de tenant (signup público) — Fase SaaS
- Permissão por pipeline (
user_pipeline_access) — adicionar quando 1º cliente real pedir - Localização (multi-language) — produto começa só em PT-BR
9. Decisões deferidas pra Spec (Fase 3)
A serem decididas no spec correspondente (docs/specs/01-spec-platform-base.md):
- Schema SQL completo de
organizations,user_organizations,auth.usersextensions,api_tokens,api_audit_log,platform_admins(se virar tabela) - Templates exatos de RLS policy por tabela
- Estrutura completa do payload de Bearer token (claims, escopos)
- Formato exato do cursor opaco (algoritmo HMAC, schema interno)
- Lista canônica de actions auditadas (ex:
lead.created,token.revoked,consent.changed,redact.executed, etc.) — versão completa - Esquema do export LGPD (estrutura JSON, layout do PDF)
- Fluxo de UI do super-admin (mockups na Spec)
- CLI ou wizard de onboarding de tenant
- Estratégia de cold storage de audit log (S3 + lifecycle policies)
Anexos
docs/research/reference-synthesis.md— pontos herdados (especialmente §4 Multi-tenancy, §6 API REST, §7 RBAC)docs/prd/00-prd-master.md— visão geraltasks/todo.md— fluxo de construção