Sistema de Sorteios ACID
Detalhes do Projeto
- Período:
- 08.2026 → hoje
- Client Project
- Criptografia Aplicada
- HMAC-SHA256
- TypeScript
- Next.js
- Prisma
- PostgreSQL
- Turborepo
- Docker
- Auditoria

Impacto em números
Posições derivadas por HMAC, não guardadas
Commit-reveal auditável por terceiros
Cobradas por constraint, gatilho ou teste
Em 33 s, com meta de 60 s
Sobre o projeto
Campanha promocional de seladinhas físicas, com prêmio instantâneo e sorteio final ao vivo. O risco central não é técnico, é reputacional: código previsível, lista de premiados acessível internamente e sorteio sem prova pública geram a percepção de sorteio armado, o que corrói a campanha inteira e expõe a diretoria da associação.
Este é um sistema de registro com valor jurídico: seus dados são a prova de que a promoção foi conduzida corretamente. A constituição do repositório declara explicitamente que integridade e rastreabilidade vencem velocidade de entrega, elegância de código e beleza de tela, sempre.
Derivação por HMAC, sem lista de premiados
posicao_A = HMAC-SHA256(K, lote_id + "|A") mod 250 + 1
posicao_B = HMAC-SHA256(K, lote_id + "|B") mod 250 + 251
Ninguém, nem o administrador, consegue saber qual selo é premiado sem a chave mestra. E a distribuição é verificável depois por qualquer auditor que receba K.
Commit-reveal no sorteio final. A impressão digital da semente é publicada antes do sorteio e revelada depois, então qualquer pessoa recalcula e confirma o resultado. O sistema também suporta semente derivada da Loteria Federal.
Log à prova de adulteração. Cada registro guarda hash = SHA256(hash_anterior + conteudo), e qualquer alteração retroativa quebra a cadeia.
A regra do repositório é dura: invariante defendida só por código de aplicação conta como não implementada. São 14, e cada uma tem quem a cobre, seja constraint, gatilho ou teste. Código não se repete (UNIQUE). Código consumido nunca volta a ficar disponível, e sorteio finalizado não muda mais, porque gatilhos no banco recusam. O log de auditoria só aceita inserção, já que o papel da aplicação perdeu UPDATE e DELETE. E a lista de quem concorre é congelada antes de o sorteio rodar, para ninguém entrar ou sair no meio.
O CPF nunca é gravado em claro nem aparece em log: a coluna é cifrada e um redator limpa a saída. A gráfica recebe os códigos em arquivo com checksum, e a geração aguenta o volume da campanha, com 100 mil códigos em 33 segundos numa meta de 60.
A noite do sorteio ganhou tela própria. O telão do palco abre com a cortina fechada, e enquanto ela está fechada ou se movendo nenhum gesto chega ao sorteio. No caminho apareceu um defeito que eu não teria achado lendo código: o botão de confirmar nascia no mesmo lugar do de sortear, e um clique duplo armava e confirmava de uma vez. Hoje existem 700 ms de guarda entre os dois gestos.
Até apagar uma campanha usada tem cerimônia. Só o super admin faz, digitando a senha de novo, o código do autenticador, o número exato de participações, o motivo e a confirmação de que existe backup. Cinco erros de identidade bloqueiam a tentativa por 15 minutos.
Outras decisões: tentativa de ativação é registrada mesmo quando falha, porque a ausência de evento também é dado. OTP por sessão valida o consumidor sem exigir cadastro pesado. CPF restrito bloqueia quem a regulamentação impede. E o projeto evoluiu para monorepo Turborepo, separando API, web e worker quando o painel da gráfica justificou.
O desafio
Projetar um sistema onde a ausência de informação é a própria feature de segurança. O instinto de engenheiro é guardar tudo, e aqui guardar a lista de premiados seria o vetor de fraude.
Precisei inverter o modelo mental: derivar em vez de armazenar, provar em vez de afirmar, e registrar até a tentativa que falhou. Foi o projeto que mais me ensinou a pensar em ameaça em vez de caso feliz.
Métricas de Crescimento
Decisões registradas em ADR
Resultados
Nenhuma lista de premiados armazenada. Sorteio final verificável publicamente por commit-reveal, 14 invariantes cobradas por constraint, gatilho ou teste, 59 decisões registradas em ADR e base de consumidores da região coletada com consentimento LGPD.
Stack técnica
HMAC-SHA256
Derivação determinística das posições premiadas a partir de uma chave mestra secreta.
Commit-reveal
Publicação prévia da impressão digital da semente e revelação posterior para verificação pública.
Log hash-encadeado
SHA256 do hash anterior somado ao conteúdo, de modo que alteração retroativa quebra a cadeia.
PostgreSQL como fiscal
Constraints, 9 gatilhos e REVOKE no papel da aplicação: a regra não depende de a tela estar certa.
Next.js 16 e Prisma 7
31 models no schema, com PostgreSQL como sistema de registro.
Turborepo
Monorepo com api, web e worker separados, mais packages compartilhados.
Docker Compose
Postgres real e seed idempotente, com ambiente de teste espelhando produção.
Galeria(2)


