5.9 KiB
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
interface NotificationChannel {
val key: FeatureKey? // null = sempre ativo
suspend fun notify(alvo: NotificationTarget, evento: NotificationEvent): Result<Unit>
}
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:
WhatsAppChannelcomkey = 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.mdcom o que falta: WABA verificada, templatesutilityaprovados, 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
@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:
- Seleciona
media_assetscomexpires_at < now()estorage_key IS NOT NULL - Apaga o objeto no MinIO
UPDATE media_assets SET storage_key = NULL, purged_at = now()- 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
WhatsAppChanneldesligado 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_PURGEapaga 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