João Pedro

Command Palette

Search for a command to run...

Command Palette

Search for a command to run...

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
Logo de Sistema de Sorteios ACID
Sistema de Sorteios ACID

Impacto em números

Listas de Premiados
0

Posições derivadas por HMAC, não guardadas

Verificabilidade
Pública

Commit-reveal auditável por terceiros

Invariantes no Banco
14

Cobradas por constraint, gatilho ou teste

Códigos Gerados
100 mil

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

59ADRs
119%
20/0819/0921/09

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

01

HMAC-SHA256

Derivação determinística das posições premiadas a partir de uma chave mestra secreta.

02

Commit-reveal

Publicação prévia da impressão digital da semente e revelação posterior para verificação pública.

03

Log hash-encadeado

SHA256 do hash anterior somado ao conteúdo, de modo que alteração retroativa quebra a cadeia.

04

PostgreSQL como fiscal

Constraints, 9 gatilhos e REVOKE no papel da aplicação: a regra não depende de a tela estar certa.

05

Next.js 16 e Prisma 7

31 models no schema, com PostgreSQL como sistema de registro.

06

Turborepo

Monorepo com api, web e worker separados, mais packages compartilhados.

07

Docker Compose

Postgres real e seed idempotente, com ambiente de teste espelhando produção.

Galeria(2)

Sistema de Sorteios ACID, imagem 1
Sistema de Sorteios ACID, imagem 2