# FASE 8 — Planos, módulos, LGPD e observabilidade ## Objetivo Fechar o que torna o produto operável e vendável: módulos ligáveis por tenant, canal de WhatsApp preparado (interface, não integração), retenção e expurgo automáticos, direitos do titular, e os SLOs medidos de verdade. ## Pré-requisitos FASES 0–7 concluídas. Ler `docs/06-LGPD-E-SEGURANCA.md` §3 e §4, e `docs/05-INFRA-DOCKER.md` §6. ## Tarefas ### 1. Canais de notificação plugáveis ```kotlin interface NotificationChannel { val key: FeatureKey? // null = sempre ativo suspend fun notify(alvo: NotificationTarget, evento: NotificationEvent): Result } ``` `PushChannel` (FCM + APNs) sempre ativo. `WebSocketChannel` sempre ativo. `NotificationDispatcher` resolve os canais habilitados para o tenant e despacha **em paralelo**, tolerando falha individual — um canal fora do ar não pode impedir os demais. ### 2. WhatsApp — interface e flag, sem integração O WhatsApp é **módulo pago opcional**; o app é o notificador principal. Nesta fase entrega-se apenas: - `WhatsAppChannel` com `key = FeatureKey.WHATSAPP_NOTIFICATIONS`, implementação **stub** que registra o que seria enviado - Toggle no painel admin, com o efeito descrito em uma frase - Estrutura de templates prevista - Documentação em `docs/anexos/whatsapp-v2.md` com o que falta: WABA verificada, templates `utility` aprovados, custo (~US$0,004/msg no Brasil, grátis dentro da janela de 24h) **Não integre com a Meta agora.** Verificação de negócio e aprovação de template levam dias e bloqueariam o lançamento. E registre no documento a limitação de fundo: a latência do WhatsApp não serve para *tocar* uma chamada — ele é aviso paralelo com deep link, nunca o canal primário. ### 3. Feature flags ```kotlin @Service class FeatureService { fun habilitado(tenantId: UUID, key: FeatureKey): Boolean fun dentroDaQuota(tenantId: UUID, key: FeatureKey, uso: Int): Boolean } ``` Cache curto (60s) com invalidação ao alterar. Toda mudança de flag vai para `audit_log` com autor. Quotas relevantes: minutos de vídeo por mês, gravações armazenadas, operadores simultâneos. ### 4. Gravação de chamada Módulo `video_recording`. LiveKit Egress grava para o MinIO ao entrar em `EM_CHAMADA`, se habilitado. **Aviso reforçado obrigatório:** ao gravar, o visitante vê aviso explícito antes do início, e a versão desse aviso é registrada. Gravar sem avisar é violação direta. Retenção de 90 dias. Cada gravação soma banda no SFU — ative com quota. ### 5. Retenção e expurgo Job diário `MEDIA_PURGE`: 1. Seleciona `media_assets` com `expires_at < now()` e `storage_key IS NOT NULL` 2. Apaga o objeto no MinIO 3. `UPDATE media_assets SET storage_key = NULL, purged_at = now()` 4. **Preserva a linha** O passo 4 é o ponto: fica provado que existiu uma foto e que ela foi eliminada no prazo — exatamente o que se demonstra numa fiscalização. Prazos configuráveis por tenant, com **teto** definido pela plataforma. Um condomínio querendo guardar foto por 5 anos configuraria um risco que a plataforma não aceita hospedar. Job diário de limpeza de `idempotency_keys` vencidas. ### 6. Direitos do titular `POST /admin/lgpd/export` — busca por CPF ou telefone, devolve JSON com todas as visitas, mídias (URLs assinadas) e registros de aviso. `POST /admin/lgpd/erase` — apaga mídia, anonimiza `visitor_name` para `[REMOVIDO]`, limpa documento e telefone, **preserva a linha da visita** com data, unidade e resultado. Justificativa: obrigação de guarda de registro de acesso para segurança patrimonial. A linha vira estatística, não identificação. Ambas registram em `audit_log` e devolvem comprovante em PDF — o condomínio precisa provar que atendeu no prazo de 15 dias. ### 7. Versionamento do aviso de privacidade Editor no admin. Alterar o texto **cria nova versão**; as visitas antigas continuam apontando para a versão que foi realmente exibida. Sem isso, não há como provar o que o visitante leu naquele dia. ### 8. SLOs medidos Instrumentar as métricas de `01-ARQUITETURA.md` §5.7: ``` portaria.visit.ring_latency QR → push entregue no dispositivo portaria.visit.answered_ratio atendidas pelo morador / total portaria.delivery.resolution_latency QR → resolvida portaria.delivery.abandoned_ratio canceladas ou expiradas portaria.entry_flow.availability caminho crítico ``` `ring_latency` mede até a **entrega** do push, não até o envio. A diferença entre os dois é justamente onde a falha acontece. Dashboards e alertas conforme `05-INFRA-DOCKER.md` §6, incluindo os dois alertas de negócio (taxa de atendimento baixa e tokens mortos) que viram tarefa de customer success, não plantão. ### 9. Runbooks `docs/anexos/runbook-incidente.md` — vazamento, indisponibilidade, perda de dados; com prazos ANPD. `docs/anexos/runbook-operacao.md` — restore testado, rotação de segredo, invalidação de QR, adição de condomínio. `docs/anexos/LIA-modelo.md` — modelo de Legitimate Interest Assessment para preenchimento por condomínio. ## Critérios de aceite - [ ] Dispatcher despacha em paralelo e tolera falha de um canal - [ ] `WhatsAppChannel` desligado não é invocado; ligado, registra o que enviaria - [ ] Toggle de módulo tem efeito imediato e fica em `audit_log` - [ ] Gravação só ocorre com módulo ativo e aviso reforçado exibido - [ ] `MEDIA_PURGE` apaga o objeto e preserva a linha - [ ] Exportação LGPD devolve todos os dados de um titular - [ ] Eliminação anonimiza e preserva a linha da visita - [ ] Alterar o aviso cria versão; visitas antigas mantêm a original - [ ] Todos os SLOs visíveis no Grafana com dados reais - [ ] Alertas disparam em condição simulada - [ ] Restore de backup testado e cronometrado ## Não faça nesta fase - Integração real com a Meta (v2) - Billing automatizado (v2) - Ativar multi-tenancy