fieli flow

Não peça para confiar na nossa auditoria.
Confira.

O verificador abaixo é Rust compilado para WebAssembly. Ele roda no seu navegador, contra uma chave pública que qualquer um pode ler, e não fala com nenhum servidor nosso.

Log editável não é evidência

Toda plataforma tem um log. E todo log pertence a quem o guarda.

Se o registro de que uma mensagem tinha consentimento mora numa tabela que o fornecedor administra, o que você tem não é prova — é a palavra dele, escrita num lugar que ele pode reescrever. Isso basta na maior parte dos dias, e deixa de bastar exatamente no dia em que alguém precisa da prova.

A pergunta que separa as duas coisas é simples e desconfortável:se o registro tivesse sido alterado, você teria como saber? Um log, sozinho, responde não. É por isso que o produto não se apoia num log — se apoia numa cadeia encadeada, numa árvore e numa assinatura, que é o que a próxima seção descreve.

Todas as plataformas registram. Nenhuma prova.

Da ação à raiz assinada, em cinco passos

Cada passo consome o anterior. É por isso que mexer em qualquer um deles aparece.

  1. A ação vira um evento canônico

    O que aconteceu é serializado com as chaves em ordem e sem espaço. Duas máquinas que serializem o mesmo evento produzem os mesmos bytes — sem isso, nada depois é comparável.

  2. O evento vira um hash

    SHA-256 daqueles bytes. É esse resumo que o banco guarda ao lado do evento, e é ele que o verificador recalcula: quem confere não acredita no hash declarado, refaz a conta.

  3. O hash entra numa cadeia

    Cada evento carrega o hash do anterior. O primeiro aponta para o genesis — trinta e dois bytes zero. Reescrever um evento antigo quebra todos os elos seguintes de uma vez.

  4. A janela vira uma árvore

    Periodicamente, os eventos de uma janela viram uma árvore de Merkle. Cada folha e cada nó carregam um prefixo de domínio distinto, como manda a RFC 6962 — é o que impede alguém forjar uma folha que colida com um nó interno.

  5. A raiz é assinada

    A raiz da janela recebe uma assinatura ECDSA-P256. Ela é o único ponto em que a nossa palavra entra — e é justamente o ponto que a chave pública torna conferível por terceiro.

O verificador

A prova de exemplo, e a sua.

Abaixo, uma prova de exemplo: cinco eventos sintéticos, com hashes, árvore e assinatura de verdade. Clique em verificar e o cálculo roda aqui. Clique em adulterar e ele falha — e o modo como ele falha é o que interessa.

Este registro tem uma prova. Confira você mesmo.

O verificador roda no seu navegador, em WebAssembly — com o JavaScript desligado ele não tem como rodar. A anatomia acima continua valendo.

Ou confira a SUA prova

Se você já é cliente, exporte a prova de um registro e solte o arquivo aqui. Ele não sai do seu computador: o navegador o lê, o WebAssembly o confere, e nenhuma requisição é feita.

O formato é o mesmo do exemplo acima — a página o descreve campo a campo em Como a prova é montada.

Este cálculo rodou no seu computador. Nenhum dado foi enviado para nós — o binário é baixado uma vez e roda no seu navegador.

O botão de exportar ainda não existe no painel. O formato abaixo é o que o produto vai emitir, e é o que este verificador já aceita — está escrito aqui antes de existir para que ninguém precise adivinhá-lo depois.

Como a prova é montada

Oito campos. Nenhum deles é opcional, e o verificador diz qual falta.

Os campos do arquivo de prova
CampoTipoO que ele é
payloadHasheslista de hexadecimais de 64 dígitoso hash de cada evento da cadeia, em ordem cronológica
prevHasheslista, do mesmo tamanhoo encadeamento declarado. O primeiro tem de ser o genesis; cada seguinte, o hash do evento anterior
provadonúmeroqual evento da cadeia esta prova demonstra pertencer à janela
caminholista de hexadecimaisos nós irmãos que levam a folha até a raiz, da base para o topo
caminhoNaDireitalista de booleanos, do mesmo tamanhopara cada irmão, se ele fica à direita. O lado é informação: trocá-lo faz o caminho não fechar
raizhexadecimal de 64 dígitosa raiz da janela. O verificador a RE-DERIVA do caminho e compara — não confia nesta
assinaturaDerhexadecimala assinatura ECDSA-P256 da raiz, em DER
chavePublicaPemtexto em PEMa chave que confere a assinatura

O verificador recusa um arquivo mal formado dizendo qual campo falta, e nunca chama isso de falha de verificação. A distinção não é preciosismo: “falhou” é uma acusação de adulteração, e um verificador que a usa para dedo trocado não serve para nada sério.

A chave pública

É o único ponto em que a nossa palavra entra — e é por isso que ela é pública.

A assinatura da raiz é feita com uma chave privada que só o nosso processo de auditoria tem. A metade pública dela é publicada, e é com ela que o seu navegador confere a assinatura. Se as duas metades não casarem, a verificação falha — e é assim que deve ser.

Há duas chaves nesta página, e elas não são a mesma. A de produção é a abaixo, e é a única que vale para conferir uma prova de verdade. A prova de exemplo logo acima carrega outra, gerada e descartada no momento em que ela foi montada: essa prova que o verificador funciona, e não prova nada sobre o produto.

Como conferir que é esta a chave

Ela é servida em https://flow.fieli.app/.well-known/audit-pubkey.json, com o identificador v_64v-D_WwxUYkq-. Compare o que está lá com o que a sua prova carrega: se divergirem, alguma das duas não é a nossa.

-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEi+z95BlJiLFJEfwIsa24fKxrPA1q
3icwQ8zUOtjErHvISnOkKDi0d8ui430xo3cfQ4FhSkOKfuk/FmJ5N8eiCQ==
-----END PUBLIC KEY-----

Por que assim, e não de outro jeito

Três decisões que parecem detalhe e não são.

Prefixo de domínio nas folhas e nos nós

A folha é o hash de um byte zero seguido do conteúdo; o nó interno, de um byte um seguido dos dois filhos. Sem essa separação, alguém pode forjar uma folha cujo conteúdo coincide com o hash de um nó interno e provar inclusão de algo que nunca esteve na árvore. É a mesma construção dos registros de transparência de certificados.

A raiz é re-derivada, nunca aceita

O verificador não confere a raiz que veio no arquivo contra a assinatura e para por aí. Ele reconstrói a raiz a partir da folha e do caminho de irmãos, e só então compara. Uma raiz forjada casaria com a própria assinatura e mesmo assim seria recusada.

A cadeia existe além da árvore

A árvore prova que um evento pertence a uma janela. A cadeia prova que os eventos vieram na ordem em que dizem ter vindo. São garantias diferentes, e o verificador confere as duas — remover um evento do meio passaria pela primeira e não passa pela segunda.

Roda no seu computador, e você pode conferir isso também

Nenhuma requisição sai da sua máquina durante a verificação.

O verificador é um binário de pouco mais de oitenta kilobytes, compilado de Rust para WebAssembly. Ele é baixado uma vez, quando você clica, e a partir daí o cálculo é local. Abra as ferramentas de desenvolvedor do seu navegador, vá à aba de rede e clique em verificar de novo: você não vai ver requisição nenhuma.

O binário é o mesmo que o painel do produto usa, e a procedência dele está versionada neste repositório junto com o comando que o compila — se algum dia ele for trocado, o nosso próprio processo de publicação reprova antes de ir ao ar.

O que isso muda numa fiscalização

Em linguagem de quem vai responder, e não de quem escreveu o código.

Numa fiscalização, a pergunta que chega não é “vocês têm log?”. É alguma variação de mostre que esta mensagem específica tinha base legal, e que este registro não foi produzido depois do fato.

Com um log comum, a resposta é uma exportação que o próprio fornecedor gerou. Ela é aceita ou não conforme a confiança que a autoridade deposite em quem a gerou — e essa confiança não é sua, é dele.

Com uma prova, a resposta é um arquivo que qualquer um verifica sem pedir nada a ninguém, incluindo o fiscal, incluindo o juiz, incluindo o perito da outra parte. O que muda não é o rigor do seu processo: é quem precisa acreditar em quem.

Log imutável é promessa. Prova de Merkle é fato.