Seslock Holmes é um dashboard web de investigação de e-mails do AWS SES armazenados no Supabase/PostgreSQL. Ele foi feito para apoiar suporte, operações e análise na leitura de eventos, rastreamento de mensagens e diagnóstico de falhas — com relatórios agendados por e-mail e compartilhamento público somente-leitura de visões do dashboard.
Graças ao Seslock Holmes, é possível investigar e corrigir problemas de entregabilidade em produção. Os números falam por si:
| Métricas | Antes | Depois | Redução |
|---|---|---|---|
| Bounce rate (1 provedor) | 11,6% | 0,8% | -93% |
| Bounce rate geral | 6,8% | 1,8% | -74% |
Cada bounce diagnosticado virou uma correção concreta:
- 📧 Endereços inválidos removidos da base
- 🌐 Domínios sem registro MX corrigidos
- 🔐 Falhas de autenticação DMARC ajustadas
O resultado indireto é tão importante quanto o número: reputação de envio melhorada, menos mensagens caindo em spam, e menor risco de provedores bloquearem o envio por conta da reputação baixa.
O projeto está disponível através da Vercel:
🔗 https://seslock-holmes.vercel.app/
A aplicação é hospedada na Vercel e pode ser utilizada para validar a interface, navegação e integração com um projeto Supabase configurado.
A leitura dos eventos de e-mail é somente leitura:
- não cria, edita nem remove eventos de e-mail;
- consultas de eventos dependem de RLS/autenticação no Supabase.
Além da leitura, o projeto também escreve em duas áreas próprias, protegidas por RLS separada da tabela de eventos:
- relatórios agendados, que disparam e-mails periódicos por conta própria;
- links de compartilhamento, que geram acesso público somente-leitura a uma visão do dashboard.
- Visão geral com atividade recente, eventos problemáticos e principais origens, com indicadores calculados no próprio banco (via RPC) para refletir o período inteiro filtrado, não apenas uma amostra. Um toggle opcional compara os indicadores com o período anterior de mesma duração, com seta de alta/baixa em cada cartão.
- Painel de analytics com distribuição de eventos, reputação, taxa de bounce (com filtro por subtipo: suppressed, general, mailbox full, content rejected, undetermined), tempo médio de entrega, último evento recebido, principais provedores, principais motivos de bounce e aplicações/origens.
- Investigação por destinatário, remetente, provedor (domínio do destinatário) ou diagnóstico de bounce.
- Busca global no cabeçalho, disponível em qualquer página, com persistência de texto/modo/filtros entre navegações.
- Detalhes completos do evento com assunto, remetente, destinatário, status e metadados de falha.
- Rastreamento cronológico da mensagem, com os dados mais relevantes de cada evento (origem/IP, tempo de processamento, resposta SMTP, tipo/subtipo de bounce, diagnostic code, feedback de reclamação) exibidos diretamente no card.
- Exportação de relatório em CSV/PDF e por e-mail sob demanda, na Visão geral e na Investigação.
- Relatórios agendados: criação de agendamentos recorrentes (diário/semanal/etc.) com filtros próprios, múltiplos destinatários, histórico de execuções, pausa/retomada, exclusão e disparo manual ("forçar agora"). O disparo periódico roda por um workflow do GitHub Actions a cada 15 minutos, com o cron da Vercel como reforço diário.
- Compartilhamento de dashboard: botão "Compartilhar" na Visão geral gera um link público somente-leitura com os filtros atuais travados, protegido por token de 256 bits, sem exigir conta de quem acessa. A lista de atividade recente é opcional por link. Links criados podem ser listados, regenerados (invalida o link antigo) e revogados na aba "Links compartilhados", em Relatórios Agendados.
- Perfis de acesso (RBAC): contas autenticadas são
vieweroumanager. Viewers só visualizam; managers também criam e editam agendamentos e links compartilhados. Contas novas começam como viewer. - Log de auditoria: registro somente-leitura, visível a qualquer usuário autenticado, de toda ação administrativa — criação, edição, pausa, exclusão e envio forçado de agendamentos, resultado dos envios automáticos, e criação/revogação/regeneração de links compartilhados. Aba "Log de auditoria" em Relatórios Agendados.
- Login por e-mail e senha, exibido sob demanda apenas quando uma consulta é recusada por falta de permissão (contas são criadas por um administrador, sem cadastro aberto).
- Paginação no banco na atividade recente e na investigação por busca.
- Sugestões de e-mails semelhantes quando não há correspondência exata.
- Tema claro/escuro.
- Página de FAQ pesquisável (Central de Ajuda, organizada em abas) para dúvidas operacionais e de uso.
- Página de configurações para ajustar idioma, fuso horário, relógio, intervalo de atualização, conexão com Supabase e token de administração dos relatórios agendados.
/- visão geral/investigate- investigação por busca/events/:eventId- detalhes do evento/scheduled-reports- relatórios agendados/faq- perguntas frequentes e ajuda/settings- configurações do app e do Supabase/share/:token- visão pública somente-leitura de um dashboard compartilhado (fora do shell autenticado)
seslock-holmes/
├── frontend/
│ ├── api/ # funções serverless (Vercel) dos relatórios agendados
│ │ ├── schedules.ts # CRUD de agendamentos via ADMIN_API_TOKEN
│ │ ├── run-schedule-now.ts # disparo manual de um agendamento
│ │ └── send-scheduled-reports.ts # varredura e envio periódico (cron)
│ ├── public/ # robots.txt, theme-init.js, favicon
│ └── src/
│ ├── app/ # shell, rotas e providers
│ ├── assets/ # imagens e favicon
│ ├── components/
│ │ ├── shell/ # header, footer e frame da aplicação
│ │ ├── states/ # loading, empty, error e setup
│ │ └── ui/ # componentes visuais base
│ ├── features/
│ │ ├── dashboard-share/ # geração e gestão de links públicos somente-leitura
│ │ ├── event-detail/ # detalhes do evento
│ │ ├── faq/ # ajuda e perguntas frequentes
│ │ ├── message-trace/ # timeline da mensagem
│ │ ├── overview/ # dashboard principal, analytics e exportação
│ │ ├── recipient-search/ # busca e investigação por e-mail
│ │ ├── scheduled-reports/ # criação, histórico e gestão de relatórios agendados
│ │ └── settings/ # preferências e configuração do Supabase
│ ├── lib/
│ │ ├── audit-log/ # queries e tipos do log de auditoria
│ │ ├── user-roles/ # RBAC leve (viewer/manager)
│ │ ├── dashboard-shares/ # token, link e queries dos links compartilhados
│ │ ├── data/ # listas e opções de filtro
│ │ ├── filters/ # normalização e aplicação de filtros
│ │ ├── formatters/ # formatação de datas, e-mails e eventos
│ │ ├── hooks/ # hooks reutilizáveis
│ │ ├── i18n/ # textos e traduções
│ │ ├── overview/ # analytics e métricas
│ │ ├── recipient-search/ # lógica de busca e sugestões
│ │ ├── scheduled-reports/ # tipos, queries e frequência dos agendamentos
│ │ ├── server/ # autenticação de requests das funções serverless
│ │ ├── supabase/ # client, queries, tipos e settings
│ │ └── time-filters.ts # utilitários de período
│ ├── styles/ # estilos globais
│ └── main.tsx
├── supabase/
│ └── migrations/ # migrations SQL (RLS, RPCs de analytics, schedules, shares)
├── .github/workflows/ # disparo periódico dos relatórios agendados (GitHub Actions)
├── docs/ # documentação técnica (arquitetura e segurança) — leia antes de mexer
├── specs/ # documentação da feature
└── README.md
Na página inicial você pode:
- pesquisar um destinatário rapidamente;
- filtrar por janela de tempo;
- filtrar por status;
- filtrar por origem;
- filtrar por provedor;
- ordenar a atividade recente;
- navegar pelas páginas da atividade recente.
Na tela de investigação você pode escolher o modo de busca:
- destinatário;
- remetente;
- origem.
Se a busca exata não retornar resultado, o sistema mostra sugestões de e-mails semelhantes.
Ao abrir um evento, o sistema exibe:
- assunto do e-mail;
- ID do evento;
- ID da mensagem;
- status de entrega;
- origem da mensagem;
- identidade SMTP;
- e-mail do remetente;
- destinatário;
- detalhes de falha e entrega;
- rastreamento da mensagem.
A página de FAQ ajuda a responder dúvidas operacionais e de uso do painel.
Você pode:
- pesquisar perguntas e respostas;
- navegar por categorias de ajuda;
- encontrar informações sobre dados, uso e suporte sem sair do app.
Na tela de relatórios agendados você pode:
- criar um agendamento com nome, frequência, filtros (janela, status, subtipo de bounce, origem, provedor, assunto) e lista de destinatários;
- editar, pausar/retomar ou excluir um agendamento existente;
- ver o histórico de execuções de um agendamento;
- forçar o disparo imediato de um agendamento ("forçar agora"), fora do horário programado.
O e-mail enviado inclui uma seção "O que fazer com este relatório" quando o filtro cobre eventos de problema, com explicações em linguagem simples e destaque para destinatários com falha recorrente.
Na Visão geral, o botão "Compartilhar" permite:
- gerar um link público que trava os filtros atuais (janela, status, origem, provedor, etc.);
- decidir se a lista de atividade recente entra no link (desligada por padrão, pois expõe e-mail de destinatário);
- definir uma validade para o link (ou nenhuma).
Quem abre /share/:token vê uma versão somente-leitura do dashboard, sem precisar de conta. Os links criados ficam listados na aba "Links compartilhados" em Relatórios Agendados, onde também podem ser regenerados ou revogados a qualquer momento. Só contas manager podem criar ou gerenciar links.
Toda conta autenticada tem um papel:
- viewer: só visualiza — Overview, investigação, detalhes de evento, histórico de agendamentos e links.
- manager: além de visualizar, cria e edita agendamentos e links de compartilhamento.
Contas novas começam como viewer; a promoção para manager é feita diretamente no banco pelo administrador do projeto Supabase.
A aba "Log de auditoria", em Relatórios Agendados, lista (somente leitura, visível a qualquer usuário autenticado) todas as ações administrativas: criação, edição, pausa, exclusão e disparo forçado de agendamentos, o resultado de cada envio automático, e a criação, regeneração ou revogação de links de compartilhamento.
A tela de configurações permite ajustar:
- idioma da interface;
- fuso horário;
- formato do relógio;
- intervalo de atualização;
- URL do Supabase;
- chave pública/publishable do Supabase;
- nome da tabela ou view de eventos;
- token de administração usado para gerenciar os relatórios agendados do projeto padrão.
As configurações podem ser:
- salvas apenas no navegador;
- exportadas como arquivo
.env.local; - gravadas diretamente no projeto local quando o navegador oferecer acesso ao sistema de arquivos.
O app usa o Supabase como fonte de dados. Por padrão, a tabela esperada é aws_sns.
Se a tabela padrão não existir, o painel permite informar o nome correto da tabela ou view de eventos. Esse valor fica salvo localmente no navegador para evitar nova configuração toda vez.
As credenciais usadas no frontend são apenas a URL e a chave pública/publishable, nunca uma chave de serviço.
O frontend aceita as seguintes variáveis:
VITE_SUPABASE_URL=https://seu-projeto.supabase.co
VITE_SUPABASE_ANON_KEY=sua_chave_anon
VITE_SUPABASE_EVENTS_TABLE=aws_snsCompatibilidade adicional:
NEXT_PUBLIC_SUPABASE_URL=https://seu-projeto.supabase.co
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=sua_chave_publica
NEXT_PUBLIC_SUPABASE_EVENTS_TABLE=aws_snsNotas:
VITE_SUPABASE_ANON_KEYtem prioridade quando disponível.VITE_SUPABASE_PUBLISHABLE_KEYeNEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEYtambém são aceitas.- Se
VITE_SUPABASE_EVENTS_TABLEnão for definido, o app tentaaws_sns. - As configurações podem ser preenchidas pela página de settings e ficam armazenadas localmente no navegador.
O envio de relatórios agendados roda em funções serverless (frontend/api/), não no navegador, e precisa de variáveis de ambiente próprias configuradas no provedor de deploy (ex.: Vercel Project Settings):
SUPABASE_SERVICE_ROLE_KEY=sua_chave_service_role # nunca exposta ao frontend
GMAIL_USER=conta@gmail.com # remetente dos e-mails de relatório
GMAIL_APP_PASSWORD=sua_senha_de_app_do_gmail
CRON_SECRET=um_segredo_forte # autentica os disparos periódicos
ADMIN_API_TOKEN=um_segredo_forte # autentica o CRUD de agendamentos no projeto padrãoNotas:
SUPABASE_SERVICE_ROLE_KEYé obrigatória porque o envio roda fora de uma sessão de usuário; nunca deve ser usada no frontend.CRON_SECRETtambém precisa ser cadastrado como secret no GitHub (Settings > Secrets and variables > Actions) para o workflow.github/workflows/scheduled-reports-trigger.yml, que dispara o envio a cada 15 minutos (o cron da Vercel, uma vez por dia, fica só como reforço).ADMIN_API_TOKENé o mesmo valor informado em Configurações no navegador para gerenciar agendamentos do projeto padrão; quem aponta o painel para o próprio Supabase gerencia agendamentos diretamente com a própria chave anon, sem precisar desse token.- Sem essas variáveis, as páginas de leitura do dashboard continuam funcionando normalmente — apenas os relatórios agendados ficam indisponíveis.
O painel trabalha com uma tabela ou view que contenha, no mínimo, campos equivalentes a:
idtimestampoucreated_atmessageIdeventTypeounotificationTypesubjectsourcesourceArnsnsTopicArndestinationbounceTypebounceSubTypebouncedRecipientsdiagnosticCoderemoteMtaIpreportingMtasmtpResponsecomplaintFeedbackTypecomplainedRecipientsuserAgent
O aplicativo mapeia esses campos para uma visão unificada de evento de e-mail.
- O app só faz consultas de leitura sobre a tabela de eventos do SES.
- Consultas aos eventos e aos agendamentos exigem sessão autenticada no Supabase (RLS nega acesso anônimo); o login aparece automaticamente quando uma consulta é recusada por falta de permissão.
- Indicadores do overview (distribuição, reputação, taxa de bounce, tempo médio de entrega, etc.) são calculados no banco via RPC, cobrindo o período filtrado inteiro em vez de uma amostra baixada no navegador.
- A lógica respeita filtros por janela de tempo, status, subtipo de bounce e origem.
- O overview também pode ser refinado por provedor e ordenação da atividade recente.
- A busca por destinatário e remetente usa normalização de texto para reduzir variações de caixa.
- O rastreamento da mensagem usa
messageIdquando disponível.
- Node.js 18+.
- Um projeto Supabase com dados de eventos de e-mail disponíveis.
cd frontend
npm installnpm run devAbra o endereço mostrado pelo Vite no navegador.
Também é possível rodar o frontend em um container, útil para testar em outra máquina:
cd frontend
docker compose upIsso sobe o servidor de desenvolvimento do Vite em http://localhost:5173.
O frontend é compatível com a Vercel.
Deploy em produção:
- Produção: https://seslock-holmes.vercel.app/
Para publicar sua própria instância:
- Faça um fork ou clone do repositório.
- Aplique as migrations em
supabase/migrations/no seu projeto Supabase (RLS dos eventos, RPCs de analytics, tabelas de relatórios agendados e de links compartilhados). - Importe o projeto na Vercel.
- Configure as variáveis de ambiente do frontend (
VITE_SUPABASE_URL,VITE_SUPABASE_ANON_KEYe, opcionalmente,VITE_SUPABASE_EVENTS_TABLE). - Se for usar relatórios agendados, configure também as variáveis de backend (
SUPABASE_SERVICE_ROLE_KEY,GMAIL_USER,GMAIL_APP_PASSWORD,CRON_SECRET,ADMIN_API_TOKEN— veja Relatórios Agendados (backend)) e cadastreCRON_SECRETcomo secret no GitHub para o workflow em.github/workflows/. - Realize o deploy.
Dentro de frontend/:
npm run dev- inicia o servidor de desenvolvimentonpm run build- gera a build de produçãonpm run preview- visualiza a build localmentenpm run test- executa testes com Vitestnpm run typecheck- valida os tipos TypeScript
Para obter a melhor experiência:
- crie uma política RLS que permita
SELECTna tabela de eventos apenas para usuários autenticados esperados (sem acesso anônimo); - crie as contas de acesso pelo painel do Supabase — não há cadastro aberto pela aplicação;
- garanta que a tabela ou view exponha os dados necessários para investigação;
- aplique as migrations de
supabase/migrations/que criam as RPCs de analytics, as tabelas de relatórios agendados (report_schedules) e links compartilhados (dashboard_shares), o RBAC leve (user_roles) e o log de auditoria (audit_log), cada uma com sua própria política de RLS; - mantenha índices nos campos usados com frequência, como:
timestampmessageIdeventTypesource- destinatário ou colunas equivalentes
- O frontend nunca usa a chave de service role do Supabase — apenas URL e chave anon/publishable.
- A chave de service role só é usada no backend (
frontend/api/), fora do navegador, para o envio de relatórios agendados. - O deploy inclui cabeçalhos de segurança HTTP (proteção contra clickjacking, controle de referenciador, HSTS, Permissions-Policy) e uma Content-Security-Policy em modo de observação.
- Links de compartilhamento usam token de 256 bits e não expõem nada além do que o criador do link escolheu incluir.
- O painel não é indexado por buscadores (
robots.txte meta tagnoindex). - Consulte o CHANGELOG para o histórico de correções de segurança já aplicadas, e docs/SECURITY.md para o raciocínio completo por trás delas (RLS, tokens, tela de login, links de compartilhamento).
Verifique:
- se
VITE_SUPABASE_URLestá definida; - se a chave pública está correta;
- se o navegador consegue acessar o projeto Supabase;
- se a tabela configurada existe;
- se a política RLS permite
SELECT.
Se o nome real da tabela ou view for diferente de aws_sns, informe o nome correto na tela de configuração ou em VITE_SUPABASE_EVENTS_TABLE.
O painel pagina a atividade recente e a investigação. Use os botões de próxima/anterior para navegar pelos resultados filtrados.
Isso é esperado quando a política de RLS exige um usuário autenticado: a chave anon sozinha não é mais suficiente para ler os eventos. Faça login com uma conta criada pelo administrador do projeto Supabase.
Isso acontece quando o painel está apontando para o projeto Supabase padrão (não um projeto próprio configurado em Configurações). Informe o ADMIN_API_TOKEN cadastrado no backend na tela de Configurações. Quem conecta o próprio projeto Supabase gerencia agendamentos direto com a própria chave anon, sem precisar desse token.
- O app grava credenciais no GitHub? Não. As configurações ficam no navegador ou em
.env.locallocal, e o arquivo é ignorado pelo Git. - O frontend usa chave secreta do Supabase? Não. Ele usa apenas URL e chave pública/publishable; a chave de service role só existe no backend dos relatórios agendados.
- A página inicial precisa virar
/dashboard? Não necessariamente./já é a rota mais limpa para a home do produto. - Quem abre um link de compartilhamento vê tudo do dashboard? Não. Só vê a visão com os filtros travados pelo criador do link, e a atividade recente só aparece se o criador tiver escolhido incluí-la.
A pasta docs/ explica decisões de código que não são óbvias só de ler os arquivos — leia antes de mexer em autenticação, RLS, tokens, RBAC, compartilhamento ou relatórios agendados:
- docs/ARCHITECTURE.md — como o sistema funciona: fluxo de dados, RPCs de analytics, RBAC, relatórios agendados, compartilhamento, log de auditoria, convenções da Vercel.
- docs/SECURITY.md — modelo de segurança: RLS, dois incidentes reais já corrigidos, tokens de administração, tela de login, tokens de compartilhamento.
A pasta specs/001-ses-investigation/ contém os artefatos de especificação e planejamento do painel:
spec.mdplan.mdresearch.mddata-model.mdquickstart.mdtasks.md
Se você for contribuir, siga o fluxo padrão:
- Crie uma branch.
- Faça as alterações.
- Rode os testes relevantes.
- Abra um PR com uma descrição objetiva do que mudou.
Este projeto está licenciado sob a Licença MIT.