# Projeto Portaria Portaria virtual para prédios residenciais e comerciais. O visitante escaneia um QR na entrada, o morador valida por videochamada e autoriza a entrada — com operador humano de retaguarda para quando ninguém atende. **Este repositório contém, por enquanto, a especificação e os prompts de implementação.** Nenhum código de aplicação foi escrito ainda. --- ## Como usar Leia `docs/` na ordem, depois execute `prompts/` fase por fase. Cada prompt referencia as seções dos documentos que precisa respeitar — os documentos são a fonte de verdade, os prompts são o roteiro. **Não pule fases e não junte duas num prompt só.** O fatiamento existe porque as áreas de maior risco (WebRTC, notificação de chamada no iOS, LGPD) são exatamente as que um prompt monolítico implementa pela metade. ## Documentos | Documento | Conteúdo | |---|---| | [`00-VISAO-E-PRODUTO`](docs/00-VISAO-E-PRODUTO.md) | Problema, personas, jornadas, planos comerciais, escopo, métricas | | [`01-ARQUITETURA`](docs/01-ARQUITETURA.md) | **Espinha.** Stack, monorepo, os 7 padrões de robustez, ADRs | | [`02-MODELO-DE-DADOS`](docs/02-MODELO-DE-DADOS.md) | Schema Postgres, outbox, fila, retenção, RLS preparada | | [`03-FLUXOS-E-CONTRATOS`](docs/03-FLUXOS-E-CONTRATOS.md) | Máquinas de estado, REST, WebSocket, idempotência | | [`04-DESIGN-SYSTEM-UX`](docs/04-DESIGN-SYSTEM-UX.md) | Tokens, telas, movimento, acessibilidade | | [`05-INFRA-DOCKER`](docs/05-INFRA-DOCKER.md) | Compose, LiveKit, portas, banda, observabilidade, CI/CD | | [`06-LGPD-E-SEGURANCA`](docs/06-LGPD-E-SEGURANCA.md) | Bases legais, modelo de ameaças, direitos do titular | | [`07-REQUISITOS-DE-CAMPO`](docs/07-REQUISITOS-DE-CAMPO.md) | **Pré-requisitos contratuais de instalação**, não backlog | ## Fases | Fase | Entrega | |---|---| | [0 — Bootstrap](prompts/FASE-0-bootstrap.md) | Monorepo, Docker, CI verde | | [1 — Domínio e dados](prompts/FASE-1-dominio-e-dados.md) | `shared/` KMP, schema, outbox, fila, auth | | [2 — API](prompts/FASE-2-backend-api.md) | REST, WebSocket, mídia, ciclo de vida da visita | | [3 — Web do visitante](prompts/FASE-3-web-visitante.md) | PWA, ambos os funis, espera narrada | | [4 — Chamada](prompts/FASE-4-chamada.md) | LiveKit, tokens, degradação graciosa | | [5 — App do morador](prompts/FASE-5-app-morador.md) | Compose MP, FCM, PushKit/CallKit | | [6 — Painel admin](prompts/FASE-6-painel-admin.md) | Auditoria, gestão, reconciliação | | [7 — Escalonamento](prompts/FASE-7-escalonamento.md) | Fila de operador, app do operador, recado | | [8 — Planos e LGPD](prompts/FASE-8-planos-e-lgpd.md) | Feature flags, retenção, direitos, SLOs | | [9 — Hardening](prompts/FASE-9-hardening.md) | E2E, carga, falhas, segurança, piloto | ## Stack **Backend** Spring Boot 3 + Kotlin · PostgreSQL 17 + PostGIS · jOOQ · Flyway **Apps** Compose Multiplatform (Android + iOS) · **Web** Vite + React + TS **Compartilhado** Kotlin Multiplatform (DTOs + máquina de estados) **Mídia** LiveKit self-hosted + coturn · **Objetos** MinIO/S3 **Infra** Docker Compose + Traefik · OpenTelemetry + Prometheus + Grafana + Loki ## As decisões que mais importam **O modelo é híbrido, não autônomo.** O morador é chamado primeiro — é a tese do produto. Mas a taxa de atendimento nunca é 100%, e o mercado consolidado usa operador humano 24h justamente por isso. Por isso vendemos dois planos, com o operador como retaguarda paga. **Entrega tem funil próprio.** É a maioria do volume, e o entregador não espera videochamada. Foto do pacote, um toque, meta de 10s. **A busca de unidade é cega.** A API nunca confirma se uma unidade existe — nem pelo corpo da resposta, nem pelo tempo. Sem isso, qualquer pessoa com o QR mapeia quem mora onde. **A base legal é legítimo interesse, não consentimento.** Consentimento sob "aceite ou não entre" não é livre, e portanto não é válido. O que se faz é informar com clareza e arquivar a LIA. **Sem reconhecimento facial.** Elevaria os dados a sensíveis, sem base legal disponível. Decisão jurídica, não limitação técnica. **iOS exige PushKit + CallKit.** Notificação comum não faz o telefone tocar como chamada. Sem isso, o app não funciona como portaria. **Wi-Fi e botão físico são contratuais.** Hall blindado não pega 4G, e nem todo visitante tem smartphone com bateria. Nenhum código resolve isso. ## Primeiro passo Antes da FASE 4, derrube o maior risco técnico: suba `livekit-server` + `coturn` e feche uma chamada entre um navegador móvel em 4G real e um Android, medindo o tempo até o primeiro frame. É barato de testar e caro de descobrir tarde.