Analise documentos de clientes sem nunca armazenar nenhum

Como o sistema é construído, onde ele roda e o que ele deliberadamente não faz.

Toda ferramenta gratuita de PDF na internet abre com “seu arquivo nunca sai do navegador”. Esta não abre, porque não pode, e uma página que fugisse disso seria a página errada para ter num site sobre segurança. Então a versão honesta vem primeiro, e todo o resto é o detalhe por trás dela.

Por que isto roda num servidor

Um verificador que roda só no navegador é um desenho legítimo e bom, e ele compra exatamente uma propriedade: os bytes ficam na sua máquina. O que ele custa é profundidade. A análise aqui percorre as tabelas de referência cruzada, reexecuta os fluxos de conteúdo das páginas para reconstruir a ordem de pintura, decodifica o texto coberto pelo mapa /ToUnicode da fonte e compara o documento com perfis estruturais montados a partir de espécimes legítimos de emissores. Isso é um parser com orçamento rígido de CPU e um botão de desligar, não algo para rodar dentro da aba de quem por acaso abriu a página.

A troca é deliberada: o arquivo é enviado por TLS e, em troca, a resposta é uma que nenhum leitor de metadados no navegador consegue produzir. O que devemos a você por essa troca é que a estadia do arquivo seja a mais curta e a mais contida possível, que é o resto desta página.

Se essa troca é errada para o seu documento, não faça a troca. Um processo sob segredo de justiça, um incidente em andamento, uma peça ainda sem tarja antes do protocolo, para esses, a ferramenta certa é uma que rode na sua própria máquina, e hoje esta não é. Preferimos dizer isso aqui a você descobrir depois.

O que acontece com um documento enviado

A vida inteira de um arquivo enviado, em ordem:

Um documento enviado só existe em memória. A única coisa que chega ao disco é uma linha de medição que não guarda documento nenhum.
EM MEMÓRIA, DENTRO DA REQUISIÇÃO chega por TLS mantido em memória tipo farejado a partir dos bytes thread de trabalho, uma de duas prazo rígido de 15 segundos relatório devolvido a você buffer descartado EM DISCO Nenhum arquivo temporário, nenhum diretório de upload, nenhum object store, nenhuma fila atrás do endpoint. mede a sua cota uma linha de registro id da chave · id da conta · bytes faixa de risco · carimbo de tempo

O buffer nunca vira arquivo: nada do documento sobrevive à resposta, nem os bytes, nem o texto extraído, nem o nome do arquivo. A política de privacidade é a versão coluna por coluna da linha à direita.

  1. Ele chega por TLS e é mantido em memória. Não há arquivo temporário, diretório de upload, object store nem fila atrás do endpoint.
  2. O tipo é decidido farejando os bytes, nunca pelo cabeçalho Content-Type nem pelo nome do arquivo: então um chamador hostil não escolhe qual parser roda na entrada dele.
  3. O buffer vai para uma thread de trabalho, uma de duas, com prazo rígido de 15 segundos. Um arquivo que esgota esse prazo tem sua thread morta e substituída, e o chamador recebe 422 analysis_timeout. Quando as duas estão ocupadas e a fila está cheia, a resposta é 503 busy, e não uma latência que só cresce.
  4. O relatório é serializado e devolvido. O buffer é descartado. Nada do documento sobrevive à resposta, nem os bytes, nem o texto extraído, nem o nome do arquivo.
  5. Uma linha de registro é gravada, e ela não guarda documento nenhum: o id da chave, o id da conta, o tamanho em bytes, a faixa de risco e um carimbo de tempo. É isso que mede a sua cota.

Os envios têm teto de 10 MB. Todas as outras rotas da origem têm teto de 64 KB, então nada além do único endpoint que precisa de folga pode ser levado a bufferizar megabytes antes de o handler rodar.

O texto recuperado, as palavras encontradas embaixo de uma tarja mal aplicada. Volta para você, na resposta, e não é guardado em lugar nenhum. Ele é o achado, então omiti-lo tornaria o relatório inútil; e guardá-lo faria deste o banco de dados mais sensível do produto. Ele não é registrado em log nem persistido.

A política de privacidade é a versão coluna por coluna do que o banco guarda.

Provar depois o que dissemos a você

Nada é armazenado, o que significa que não conseguimos reproduzir um relatório. Se você recusa uma proposta com base nos nossos sinais e, seis meses depois, precisa justificar a decisão, "rode o arquivo de novo" não é resposta: o arquivo é do proponente, o motor mudou, e nós não guardamos nada.

Por isso toda resposta do /api/v1/inspect carrega um receipt: uma assinatura sobre o corpo exato que devolvemos, com a versão do motor e a hora em que foi emitida. O relatório fica com você. Se uma disputa aparecer, o relatório que você apresentar ou verifica contra aquela assinatura, ou não verifica.

"receipt": {
  "version": "v1",
  "issuedAt": "2026-08-15T19:32:25.147Z",
  "engineVersion": "1.37.0",
  "reportHash": "7543fa3f36bc24d0c5588b87025408b8a84fc7f7259d7dea3d9856a707af76ef",
  "redacted": false,
  "signature": "…"
}

Dois limites, e vale conhecer os dois antes de depender disso. A assinatura é um HMAC, então só nós conseguimos verificá-la: um auditor externo precisa passar por nós e confiar que conferimos com honestidade. Uma assinatura de chave publicada removeria essa objeção e é a v2 pretendida. E o recibo cobre o relatório, não o documento: prova o que dissemos, nunca o que o arquivo era.

A referência da API tem o campo e o endpoint de verificação.

Onde ele roda

  • Um servidor, na Finlândia. Uma instância Hetzner em Helsinque (hel1). Computação e banco de dados ficam na UE.
  • Nenhuma porta de entrada é publicada. A origem não é alcançável pela internet. O tráfego chega por um Cloudflare Tunnel, que abre uma conexão de saída a partir da máquina; não há socket escutando para alguém escanear, nem uma regra de firewall entre você e um erro.
  • TLS em todo o caminho. Terminado na borda da Cloudflare e levado até a origem dentro do túnel. HTTP é redirecionado, e o host canônico é tamperlens.com.
  • O banco é um único arquivo SQLite no disco dessa máquina, com contas, hashes de chaves, sessões e o registro de uso. Ele nunca guardou um documento, porque nenhum caminho de código grava um.

Endurecimento da aplicação

  • Uma Content-Security-Policy de verdade. default-src 'self' e nenhum 'unsafe-inline' em lugar algum do site. Toda página que escrevemos carrega script e folha de estilo como arquivos externos, e a CI quebra o build se aparecer script ou estilo embutido em src/web. A única exceção é /docs, onde o Swagger UI injeta o próprio bootstrap e não oferece como desligá-lo; fica restrito a esse único prefixo, que não renderiza conteúdo de usuário.
    A política só permite a nossa própria origem, com exceção da Cloudflare, e só para os dois recursos que precisam dela: challenges.cloudflare.com em script-src e frame-src, para a verificação humana Turnstile, e static.cloudflareinsights.com mais cloudflareinsights.com, para o Web Analytics. Cada entrada só aparece enquanto aquele recurso está ligado, então uma instalação que não rode nenhum dos dois mantém uma política restrita à própria origem, byte a byte. Os dois estão ligados hoje, e a seção 4 da política de privacidade diz o que eles carregam e quando.
  • Envios de outra origem são recusados. Um navegador só pode chamar /inspect a partir das nossas páginas. Requisições sem cabeçalho Origin (curl, SDKs de servidor), não são afetadas, porque gastam o próprio endereço em vez de pegar emprestado o de um visitante.
  • Limites de taxa por rede, não por cabeçalho. Chamadores anônimos têm 10 inspeções por hora e 25 por dia por /64 IPv6 (um cliente é um balde, não 264 deles), sob um teto diário de toda a instalação. Chamadores com chave são medidos pela cota mensal.
  • Outros cabeçalhos: Referrer-Policy: no-referrer, Cross-Origin-Opener-Policy: same-origin, X-Content-Type-Options: nosniff, HSTS em produção e frame-ancestors 'self'.
  • PDFs criptografados são declarados, não atacados. Um arquivo protegido por senha é reportado como “sem análise de conteúdo”, e não varrido como texto cifrado e reportado como limpo. Não tentamos quebrar a proteção de nada que você enviar.

Contas, chaves e segredos

  • Chaves de API são guardadas como hashes SHA-256. A chave em si existe nos nossos sistemas apenas pelo tempo de mostrá-la a você uma vez. É também por isso que não conseguimos reenviá-la: a rotação emite uma nova e invalida a antiga.
  • Senhas são hashes scrypt com sal aleatório por conta. O texto puro nunca é guardado e o hash não é reversível.
  • Sessões são HttpOnly, Secure e SameSite=Lax, expiram em 30 dias e são apagadas no logout. Redefinir a senha invalida todas as outras sessões.
  • Dados de cartão nunca chegam até nós. O checkout e o portal de cobrança são hospedados pela Stripe; guardamos um id opaco de cliente e mais nada.
  • Uma chave, um inquilino. Perfis privados de emissor resolvem apenas contra a conta dona, não existe caminho de código da requisição de uma conta até o perfil de outra.

Disponibilidade e recuperação

Existem sessenta dias de backup, e nenhum deles contém documento, porque o banco que eles copiam nunca guardou um.
o documento descartado com a resposta, não ocupa tempo nenhum uma linha de registro guardada, para medir a cota · id da chave · id da conta · bytes · faixa de risco backups, na máquina 48 cópias, uma a cada seis horas backups, fora dela puxados uma vez por dia para uma máquina do operador, fora do provedor 0 12 dias 60 dias Nenhuma das duas cópias é criptografada em repouso. Isso está em “O que não temos”, abaixo.

As barras são retenção, não volume. Perder o servidor não significa perder também os backups dele, que é a razão inteira de a puxada diária existir.

  • Deploys são condicionados e reversíveis. Um push para main roda typecheck, build e a suíte de testes inteira; só no verde é que um runner na máquina reconstrói, espera o healthcheck do contêiner e reverte automaticamente se o build novo não estiver saudável.
  • Backups do banco a cada seis horas, 48 cópias retidas (doze dias), gravadas na mesma máquina, em um diretório que só a conta de serviço consegue ler, e puxadas uma vez por dia para uma máquina controlada pelo operador, fora do provedor, mantidas lá por sessenta dias, para que perder o servidor não signifique perder também os backups dele. Elas não contêm documentos, porque o banco não contém, e, nos dois lugares, não são criptografadas em repouso, o que está listado abaixo entre as lacunas em vez de maquiado aqui.
  • A disponibilidade é sondada de fora em uma agenda de melhor esforço, e os erros vão para o Sentry com o caminho da requisição, nunca com um documento.
  • O que a sonda encontrou é público, em status.tamperlens.com: 90 dias de disponibilidade diária por componente, todos os incidentes e os dados brutos de onde saem as porcentagens. A sondagem vem de fora da nossa rede e a página é servida de fora dela também, então ela continua no ar quando nós não estamos. O compromisso ali é de 99,5% ao mês, e a própria página declara o limite da medição: um único ponto de observação, em um intervalo que ela mede e publica em vez de afirmar, uma queda mais curta que o intervalo entre duas sondagens pode passar despercebida.

Quem mais encosta nisso

Nenhum deles recebe um documento enviado, porque o documento não sobrevive à requisição que o trouxe. Esta tabela é a mesma lista da seção 6 da política de privacidade, que é a versão que vale, se as duas divergirem, quem obriga é a política.

Nada que saiu do documento cruza a linha. O que os seis suboperadores veem é metadado da requisição, não o arquivo que ela carregou.
DENTRO DO PROCESSO, EM HELSINQUE FORA DELE os bytes enviados o texto extraído deles o texto recuperado sob uma tarja Os três são descartados com a resposta. Nenhum caminho de código grava qualquer um deles. o relatório volta para você, na resposta a fronteira de confiança metadado da requisição Cloudflare metadado e IP, em trânsito Hetzner só a máquina física Stripe seus dados de cobrança Resend seu endereço de e-mail Sentry stack traces e caminhos GitHub nada sobre você nenhum deles recebe um documento

As mesmas seis linhas da tabela abaixo, que por sua vez é a seção 6 da política de privacidade. A versão que vale se as duas divergirem.

Provedor O que faz O que enxerga
Cloudflare DNS, TLS, entrada por túnel, a verificação humana Turnstile, Web Analytics Metadados da requisição e endereço IP, em trânsito
Hetzner O servidor e o disco dele A máquina física; nenhum acesso em nível de aplicação
Stripe Checkout, assinaturas, portal de cobrança Seus dados de cobrança. Não vemos dados de cartão
Resend E-mail de confirmação e de redefinição de senha Seu endereço de e-mail e a mensagem
Sentry Relato de erros Stack traces e caminhos de requisição
GitHub Código, CI, disparo do deploy Nada sobre você; nenhum dado de produção

Respondendo ao seu questionário de segurança

Se você está avaliando o Tamperlens para uma empresa que revisa seus fornecedores, é quase certo que exista uma planilha para preencher. Duas coisas existem para que você não precise marcar uma call para preenchê-la.

Uma autoavaliação MVSP. O MVSP é uma lista de controles mínimos mantida abertamente por um grupo que inclui Google, Salesforce, Okta, Slack e Netflix. Respondemos todos os controles, inclusive aqueles em que falhamos, e as falhas são as mesmas listadas na seção seguinte, não um conjunto diferente e mais gentil. É uma autoavaliação: ninguém a auditou, e ela não atesta nada.

Uma resposta CAIQ Lite. O questionário curto da Cloud Security Alliance, respondido do mesmo jeito, para quem tem um processo que espera esse formato como arquivo anexável a um chamado.

Os dois estão disponíveis mediante pedido em [email protected], em geral no mesmo dia útil, porque são documentos escritos e não um projeto. Quando uma resposta depende de algo que você mesmo pode conferir, ela aponta para a página que mostra: esta, a política de privacidade ou a página de status.

Se um relatório SOC 2 concluído for requisito obrigatório, não somos compatíveis hoje, e preferimos dizer isso por escrito a gastar um trimestre do seu tempo até você descobrir. As autoavaliações acima são o que um fornecedor de uma pessoa só pode oferecer com honestidade; elas não substituem um relatório, e não as apresentamos como se substituíssem.

O que nunca consultamos

O Tamperlens lê identificadores brasileiros: CPF, CNPJ, a chave de acesso de uma NF-e, a linha digitável de um boleto, um payload PIX, um número de processo no padrão CNJ, e confere cada um do único jeito que sabe: com aritmética. Todos eles carregam dígitos verificadores calculados por uma identidade publicada, módulo 10, módulo 11 ou ISO 7064. Recalculamos esses dígitos a partir dos números já impressos na página e reportamos os que discordam de si mesmos.

A conferência inteira é aritmética sobre dígitos que você já enviou. Não há nela um ramo que chegue a um birô, porque esse ramo nunca foi construído.
dígitos já impressos CPF · CNPJ · chave da NF-e linha digitável · PIX · CNJ recalcular o dígito módulo 10 · módulo 11 · ISO 7064 inteiros, na mesma análise discorda de si mesmo → reportado concorda → fica em silêncio perguntar a um birô o que se sabe da pessoa fotos · endereços · parentes · empresas não existe esse caminho nenhuma requisição sai do processo Um dígito que passa não prova praticamente nada, então só as falhas são reportadas.

Seis famílias de identificador, tudo aritmética de inteiros e nenhuma chamada de rede no módulo. Isto descreve o que o código faz, não uma política que poderia ser afrouxada.

Nada é consultado, porque não há o que consultar. Sem birô, sem base de dossiês, sem busca por CPF e sem nenhuma requisição saindo do processo. A conferência é aritmética de inteiros sobre dígitos que você já nos enviou, e roda dentro da mesma análise em memória de todo o resto desta página. Vale dizer isso com todas as letras, porque o caminho óbvio para montar um produto de documentos no Brasil é o contrário: pegar um identificador e devolver o que se sabe sobre a pessoa por trás dele, fotos, endereços, parentes, empresas. Esse produto carrega a exposição à LGPD de um birô de dados, o custo de um birô de dados e a superfície regulatória de um birô de dados. Este aqui não guarda dado pessoal nenhum porque não obtém nenhum.

Isso compra menos do que uma consulta compraria, e o relatório diz isso. Um dígito verificador que falha é boa evidência de erro de digitação, de erro de OCR ou de número inventado. Um dígito que passa não prova praticamente nada. Um CPF válido são onze dígitos que qualquer um gera em um laço, e todo preenchedor de template já gera, então essa família reporta as falhas e fica calada nos acertos. Não é verificação de identidade, não é KYC e não é consulta cadastral, e não vendemos como se fosse.

O que não temos

Uma página de segurança que lista só qualidades é publicidade. Estas são as perguntas de uma análise de fornecedor cuja resposta hoje é não.

  • Sem SOC 2, ISO 27001 ou atestado HIPAA. A Tamperlens é um produto pequeno e independente, e não foi auditada contra nenhum deles. Se o seu processo exige fornecedor certificado, este ainda não é um.
  • Sem redundância entre regiões. Um servidor, uma região. Uma falha de hardware é indisponibilidade, não perda de dados, mas é indisponibilidade.
  • Sem chaves gerenciadas pelo cliente e sem versão on-premise. Hoje não há como rodar o motor dentro da sua própria rede.
  • Backups não são criptografados em repouso. Os snapshots de seis em seis horas ficam no disco do mesmo servidor, em um diretório que só a conta de serviço que os grava consegue ler, e dentro dos backups que a própria Hetzner faz da máquina. Isso é permissão de arquivo, não criptografia. Eles não guardam documentos, mas guardam linhas de conta e hashes de chave.
  • Sem relatório de teste de intrusão para compartilhar. O endurecimento acima é real e boa parte dele é garantida por testes na CI; nada disso foi revisado por avaliador externo.
  • Forense de metadados é indício, não prova. Um PDF regerado do zero lava todos os sinais de nível de bytes, e exiftool -all= remove todo achado de identidade em imagens. Um acerto é indício forte; uma ausência não é indício nenhum, e todo achado diz isso no próprio texto.

Como relatar uma vulnerabilidade

Escreva para [email protected] com detalhe suficiente para reproduzir. Você receberá resposta de uma pessoa. Não há programa de recompensa e não há ameaça jurídica: relate de boa-fé, dê uma janela razoável para a correção, e damos o crédito a você se quiser o crédito.

Por favor, não rode scanners automáticos contra o host de produção. Os limites de taxa acima simplesmente vão recusar você, e o barulho enterra justamente o relato que você queria fazer.