Comando: cat projects/luxury-registry.md
Registro de veículos e bens de luxo, do site editorial ao console de vendas
Duas gerações de produto para um cliente internacional do setor de luxo: um registro editorial com CMS próprio, login em dois fatores e medição de audiência sem cookie, e depois uma vitrine de veículos com um console para a mesa de vendas. Nenhuma das duas foi implantada; este case trata da engenharia e das decisões de produto, não de resultado de negócio.
abrir demonstraçãosimulação do sistema com dados fictícios · 6 telas
- Papel
- Único engenheiro, do sistema visual ao console de vendas
- Período
- jul/2026 – set/2026
- O que eu fiz
Eu: Produto, sistema visual, arquitetura, modelo de dados, CMS, medição de audiência, vitrine, console da mesa de vendas, regras de acesso, testes de segurança e a revisão de veracidade da interface.
Projeto privado, descrito sem identificar cliente, produto ou empresa.
Diagrama
Na primeira geração, o registro editorial com o catálogo alimenta a medição de audiência sem cookie, que guarda o IP só em hash, e o CMS, com segundo fator e portão de publicação, mostra o painel de audiência. Na segunda, a vitrine tem as páginas renderizadas no servidor, e só o tipo público de cada carro chega ao navegador; o interesse do comprador vai para o banco. O console da mesa de vendas confere os papéis no middleware, na página e na action. Os dois usam um Postgres com RLS, com carros, leads e perfis da equipe, e a trilha de auditoria é gravada só por uma função que deriva o autor da sessão.
Contexto
Um cliente internacional do setor de luxo vende veículos e outros bens de forma discreta. O produto precisava parecer um registro privado, e não uma revenda, e dar à mesa de vendas um lugar para trabalhar os interessados. Foram duas gerações: um registro editorial com CMS, entre julho e agosto de 2026, e uma vitrine com console de vendas, entre agosto e setembro. Nenhuma das duas foi implantada. A primeira roda inteira localmente; a segunda tem CI completo, mas nenhum alvo de deploy. Não há tráfego, venda nem outro resultado de negócio para mostrar.
O que eu construí
- O registro editorial. Home com uma treliça geométrica desenhada só em CSS e uma faixa de luz que percorre o padrão, departamentos numerados, catálogo de automóveis com busca e filtros na URL, ficha de proveniência por lote e um interesse por lote em que o navegador manda só o identificador.
- O CMS. Login com segundo fator, cadastro de lotes com portão de publicação, fotos conferidas e recodificadas e o painel “quem está no registro”: funil de interesse, lotes mais vistos, filtros usados e as buscas sem resultado, que apontam o que falta no inventário.
- A vitrine. Busca por marca, modelo e motorização, preço em duas moedas com câmbio fixo, filtros de categoria, preço e potência, página por carro renderizada no servidor e um formulário de interesse que já leva o carro anexado e devolve ao comprador um protocolo para citar quando a mesa ligar.
- O console da mesa de vendas. Funil de leads no fuso horário da mesa, com mudança de estado gravada e exportação CSV, e a gestão da frota com especificação verificada ou estimada e pré-visualização do anúncio como o comprador vê.
O que eu faria diferente
Começaria pela revisão de veracidade, em vez de terminar nela. Na primeira geração a home chegou a afirmar coisas que o catálogo não tinha, e a métrica de busca nunca era preenchida; na segunda, as alegações sem lastro só saíram na reta final. E escreveria testes já na primeira geração, que chegou ao fim com tipos e lint no CI, mas sem nenhum teste.
Restrições
- Venda discreta, então o site precisa parecer um registro privado, não uma revenda.
- O que a mesa de vendas lê sobre o carro ou o lote não pode vir do navegador do visitante.
- Medir a audiência sem cookie e sem identificador persistente.
- Preço só aparece quando a especificação do carro foi conferida.
- Uma equipe com três papéis e contas criadas com senha temporária.
Decisões
Medição de audiência própria, sem cookie
- Contexto
- A casa precisava saber o que os visitantes procuram e não encontram, sem cookie e sem identificador persistente no caminho de um comprador discreto.
- Escolha
- Eventos próprios com sessão que vive só na aba por 30 minutos, respeito a Do-Not-Track e GPC, IP guardado apenas como hash com sal diário e apagado em 30 dias, texto livre sem e-mail nem telefone e eventos mantidos por 13 meses.
- Ganhos
- O painel mostra o funil de interesse e as buscas sem resultado, que viram lacunas de inventário.
- Nenhum dado pessoal fica guardado em claro.
- Custos
- Sem identificador persistente, não dá para saber se o mesmo visitante voltou em outro dia.
Lote como máquina de estados, com portão de publicação
- Contexto
- Um lote passa por rascunho, disponível, em fechamento, adquirido e retirado, e um lote vendido continua valendo como prova de histórico da casa.
- Escolha
- Transições explícitas, "adquirido" como estado terminal e publicação bloqueada sem história de proveniência de pelo menos 180 caracteres e sem ao menos uma foto.
- Ganhos
- O catálogo nunca mostra lote sem história nem foto.
- O lote vendido fica no registro como proveniência realizada, sem botão de interesse.
- Custos
- Corrigir um lote adquirido exige retirá-lo, porque essa é a única saída do estado terminal.
Fronteira entre servidor e navegador medida
- Contexto
- O registro interno de cada carro tem 30 campos, e a vitrine renderizada no servidor mandava todos para o navegador.
- Escolha
- Um tipo público com 17 campos, montado no servidor, e um teste que reprova qualquer componente de cliente que importe o tipo interno.
- Ganhos
- Os dados serializados da vitrine caíram 28,7%.
- Um campo novo no tipo interno não vaza por descuido.
- Custos
- Dois tipos para manter em sincronia sempre que a vitrine passa a mostrar um campo novo.
Autorização em três camadas, de propósito
- Contexto
- O console tem três papéis (administração, gerência de vendas e equipe de inventário), e toda conta nasce com senha temporária.
- Escolha
- Checagem no middleware, na página e na action ou rota; a troca da senha temporária é imposta nas mesmas três camadas; sessões assinadas com algoritmo fixo e emissor e público validados.
- Ganhos
- Esquecer a checagem numa camada não abre a tela nem a ação.
- Custos
- A mesma regra aparece em três lugares e precisa mudar junto.
A interface só afirma o que é verdade
- Contexto
- A vitrine exibia alegações de processo, instalações e serviços que não existiam, transações inventadas como prova social e dados de veículo gerados a partir de hash.
- Escolha
- Tirar tudo o que não tinha lastro, mostrar preço só em anúncio com especificação verificada e marcar no console a especificação estimada, que aguarda conferência.
- Ganhos
- O comprador não vê número que ninguém conferiu.
- A mesa sabe quais anúncios ainda precisam de conferência.
- Custos
- Mais carros ficam "sob consulta" até alguém conferir a especificação.
Stack e por quê
- Next.js
- As duas gerações: páginas renderizadas no servidor, revalidação sob demanda e a fronteira com o navegador sob teste.
- TypeScript e Zod
- Validação em toda fronteira; o inventário vindo de fora passa pelo mesmo schema e falha fechado.
- Supabase (Postgres com RLS)
- Carros, leads e perfis da equipe, com trilha de auditoria gravada só por função que deriva o autor da sessão.
- jose
- Sessões do console assinadas, com algoritmo fixo e emissor e público validados.
- sharp
- Recodificação das fotos enviadas ao CMS, sem GPS nem EXIF.
- Tailwind CSS v4
- O sistema visual do registro em tokens, com a treliça desenhada só em CSS.
- GitHub Actions
- Tipos e lint na primeira geração; na segunda, também testes de segurança, varredura de segredos, auditoria de dependências e varredura do bundle.
Resultados
CI da segunda geração com tipos, lint sem aviso, 21 casos de teste de segurança, varredura de segredos, auditoria de dependências e build com varredura do bundle.
repositório privado auditadoWorkflow e testes do repositório(Auditoria do repositório privado, set/2026)Revisão adversarial do próprio código antes de fechar a segunda geração: 44 achados corrigidos, 1 deles crítico.
repositório privado auditadoHistórico do repositório(Auditoria do repositório privado, set/2026)Dados serializados da vitrine de 193.016 para 137.580 bytes (−28,7%) ao entregar só 17 dos 30 campos de cada carro.
declaradoMedição registrada no projetoAcervo de fotos de 663,8 MB para 51,2 MB (−92,3%) na conversão para WebP, e requisições de imagem na primeira carga de 306 para 66.
declaradoMedição registrada no projeto
Ângulo de segurança
Superfície de ataque
- Formulário público de interesse, que grava leads com dados de contato.
- Console interno com três papéis e contas criadas com senha temporária.
- CMS do registro com login e envio de fotos.
- Coleta de eventos de audiência vinda do navegador.
- Exportação CSV do funil de vendas.
Controles implementados
- Teto de 16 KB contado em bytes antes do parse, validação estrita, dois limites por IP e campo-armadilha no formulário público.
- Caracteres de controle e de inversão bidirecional recusados; IP derivado no servidor e nunca usado para autorizar.
- Lead gravado só pelo servidor, com RLS no banco e trilha de auditoria gravada por função que deriva o autor da sessão.
- Login do CMS com hash scrypt e TOTP com tolerância de um passo e bloqueio de reuso do mesmo código.
- Interesse que envia só o identificador do lote; o servidor resolve título e código no registro.
- Fotos conferidas pelo conteúdo e recodificadas sem metadados; CSV exportado com fórmulas neutralizadas.
O que eu testaria hoje
- Um cadastro público tentando virar equipe com as migrations aplicadas juntas, o tipo de escalada que a revisão encontrou e fechou.
- Contorno do limite por IP com cabeçalhos de proxy forjados.
- Reuso do mesmo código TOTP dentro da janela de tolerância.
Evidências
- repositório privado auditadoDois repositórios privados auditados(Auditoria do repositório privado, set/2026)