Seu auditor pede evidências do que seus agentes de IA fizeram no último trimestre. Você entrega a ele uma exportação CSV do registro de auditoria. Ele faz uma pergunta: “Você pode provar que isso não foi editado depois dos fatos?” A maioria das equipes não consegue. O registro vive em um banco de dados mutável, exportado pelo mesmo sistema que o escreveu. O auditor tem que confiar em toda a trilha, o que é exatamente a característica que faz com que não seja evidência.
Este post explica como a plataforma constrói evidências de auditoria que se sustentam sem fé. Um post anterior abordou por que a identidade por agente e um livro-razão à prova de violação são importantes para implantações Claude Code e MCP. Este mergulha mais fundo nas três propriedades que tornam as evidências independentemente verificáveis: encadeamento de hashes, assinatura criptográfica por evento e exportação em um formato de conformidade legível por máquina que as ferramentas de um auditor já compreendem.
O encadeamento de hashes: cada evento se compromete com todos os eventos anteriores
O livro-razão é apenas de acréscimo e encadeado por hash por locatário. Cada evento carrega o hash SHA-256 do evento anterior em seu campo prev_hash. O hash da cadeia do evento N é calculado sobre uma pré-imagem binária canônica que inclui: os próprios campos do evento N (locatário, número de sequência, timestamp, ator, ação, alvo, resumo de metadados, hash da carga útil) concatenados com o prev_hash do evento N-1. Na sequência 1, prev_hash é todo zeros — a âncora gênese.
A propriedade crítica: modificar, inserir ou excluir qualquer evento no meio da cadeia muda seu hash, o que quebra o link prev_hash do próximo evento, que quebra o próximo, e assim por diante até o topo. Uma única edição é detectável percorrendo a cadeia e recalculando cada hash.
A pré-imagem é uma codificação binária fixa, com prefixo de comprimento e separador de domínio versionado — não JSON. Esta é uma escolha deliberada contra três superfícies de ataque que um hash baseado em JSON deixaria abertas:
- Ordem das chaves: A ordem das chaves em um objeto JSON não é garantida pela maioria dos serializadores. Uma ordem de chaves diferente produz um hash diferente mesmo para dados semanticamente idênticos, o que cria falsas quebras de cadeia — ou pior, permite que um atacante reordene as chaves para forjar um hash correspondente.
- Espaçamento em branco e formatação de números:
{"seq": 1}e{"seq":1}e{"seq": 1.0}são JSON semanticamente equivalentes, mas produzem diferentes digests SHA-256. - Falsificação por concatenação: sem prefixos de comprimento, dois campos curtos adjacentes podem ser unidos para forjar um terceiro campo longo com o mesmo hash. Prefixar cada campo com seu comprimento em bytes (4 bytes em big-endian) resolve isso.
A implementação em core/internal/store/canon/canon.go é a única fonte de verdade. Tanto Append (escrita) quanto Verify (leitura) chamam a mesma função EventHash. Não há uma segunda implementação que possa divergir:
func EventHash(e Event) []byte {
var buf []byte
buf = lps(buf, domainEvent) // "olivares.audit.v1"
buf = lps(buf, e.TenantID)
var seq [8]byte
binary.BigEndian.PutUint64(seq[:], uint64(e.Seq))
buf = append(buf, seq[:]...)
buf = lps(buf, e.OccurredAt)
buf = lps(buf, e.Actor)
buf = lps(buf, e.ActorKind)
buf = lps(buf, e.Action)
buf = lps(buf, e.TargetKind)
buf = lps(buf, e.TargetID)
buf = append(buf, fixed(e.MetaDigest)...)
buf = append(buf, fixed(e.PayloadHash)...)
buf = append(buf, fixed(e.PrevHash)...)
sum := sha256.Sum256(buf)
return sum[:]
}
O separador de domínio "olivares.audit.v1" vincula o hash ao seu propósito e versão. Um hash de um domínio diferente (um ponto de verificação, um payload, um resumo de metadados) nunca pode colidir com um hash de evento, mesmo que os bytes brutos coincidam por acaso.
Assinatura Ed25519: cada evento é sua própria âncora
Uma cadeia de hash prova consistência interna, mas não autenticidade. Um atacante com acesso bruto para escrever no banco de dados poderia recalcular toda a cadeia do zero com eventos alterados e produzir uma cadeia válida — diferente da original, mas internamente consistente. Cadeias de hash detectam violação; elas não provam origem.
Assinaturas Ed25519 por evento encerram isso gap. Cada evento anexado ao livro-razão é assinado no momento da escrita. A assinatura cobre uma pré-imagem separada por domínio do locatário, número de sequência e o hash da cadeia do evento:
domain ("olivares.audit.event.v1") || tenant || seq (8 bytes, big-endian) || hash
A assinatura é armazenada no evento, mas é excluída da pré-imagem do hash da cadeia por design. Isso não é acidental: se a assinatura fosse incluída no hash, assinar um evento mudaria o hash que ela deve atestar. A assinatura atesta o hash sem alterá-lo.
Um verificador externo que possui apenas a chave pública pode confirmar cada evento individualmente: recomputar o hash da cadeia a partir dos campos do evento, reconstruir a pré-imagem e verificar a assinatura Ed25519. Se algum evento tiver sido alterado após a assinatura, a verificação da assinatura falha para esse evento específico — o verificador não precisa confiar no sistema que produziu a evidência.
A plataforma também suporta rotação de chaves. Uma cadeia cuja chave de assinatura mudou no meio de sua vida é verificada de ponta a ponta fixando a chave atual mais as chaves públicas das gerações anteriores. A função de verificação aceita um conjunto de chaves candidatas e considera um evento válido se qualquer candidata o verificar.
Além das assinaturas por evento, checkpoints periódicos notarializam a ponta da cadeia sob um domínio de assinatura separado (olivares.audit.checkpoint.v1). Para organizações que precisam se defender contra comprometimento a nível de host — não apenas de banco de dados — os checkpoints podem ser assinados por uma chave KMS/HSM fora do host (AWS KMS, GCP Cloud KMS, Azure Key Vault), onde a chave privada nunca reside no host. As assinaturas por evento lidam com o atacante limitado ao banco de dados; os checkpoints fora do host lidam com o atacante em caso de comprometimento do host. Os dois modelos de ameaça são distintos; nenhuma assinatura sozinha cobre ambos.
O contrato do livro-razão: selado na mesma transação
Uma falha comum em sistemas de auditoria é a consistência eventual entre a mutação de estado e o registro de auditoria. O estado muda, a escrita de auditoria é enfileirada ou agrupada, e se a escrita de auditoria falhar, a mudança de estado já foi comprometida. O resultado: mutações não auditadas que existem no sistema, mas não estão na evidência.
A plataforma aplica um contrato mais rigoroso. Tanto a mutação de estado quanto o selo do livro-razão acontecem na mesma transação de banco de dados. Se o selo falhar, toda a transição é revertida — a mutação de estado nunca é comprometida. Isso não é uma tentativa de melhor esforço; é negar-fechado.
O registro de tempo de execução da sessão ilustra isso. Quando uma sessão Claude Code muda de estado (criada, iniciada, parando, parada, falhou), appendRunEvent registra a transição em dois lugares de forma atômica:
- O registro de auditoria global em cadeia de hash via
sc.Audit().Append— a cadeia à prova de adulteração ancorada por umPayloadHash. - O registro consultável por sessão — uma projeção apenas de acréscimo vinculada à cadeia global por
audit_seq.
Ambas as gravações acontecem dentro da transação Mutate do chamador. O comentário no código em runtime_ledger.go afirma o objetivo do design diretamente: “o registro é o sistema de referência, então se o selo falhar toda a transição é revertida — NÃO é tentativa com melhores esforços.”
O próprio PayloadHash compromete-se apenas com fatos de transição canônicos e não sensíveis — a referência de execução, sequência, tipo de evento, transição de estado e carimbo de data/hora. Ele nunca inclui conteúdo de transcrição, prompts, valores do ambiente ou segredos. O livro-razão prova o que aconteceu; ele não armazena o que foi dito.
O mesmo padrão se aplica às mutações de arquivos no espaço de trabalho no workspace_ledger.go. Uma escrita de arquivo, mkdir, mover ou excluir é selada antes da operação do sistema de arquivos ser executada. Se a evidência não puder ser anexada, a mutação não é executada. O selo contém o tipo de operação, o caminho e um SHA-256 do conteúdo escrito — nunca os próprios bytes do conteúdo.
Exportação do OSCAL: evidência legível por máquina que as ferramentas do seu auditor podem ingerir
Um livro-razão à prova de violação é necessário, mas não suficiente para um auditor. Se a evidência estiver em um formato proprietário, o auditor ainda dependerá de suas ferramentas para interpretá-la. OSCAL — a Open Security Controls Assessment Language, mantida pelo NIST — é o formato que fecha este gap.
A plataforma exporta pacotes de evidência selados como um pacote OSCAL contendo três modelos:
- Definição de componente: as capacidades do plano de controle expressas como requisitos implementados contra um framework de conformidade (NIST SP 800-53, ISO 27001, EU AI Act e outros). Cada requisito implementado carrega o ID do controle, as chaves de capacidade que a evidenciam e o status real como uma propriedade personalizada.
- Resultados da avaliação: resultados por controle com um status compatível com OSCAL. Cada resultado tem como alvo
satisfiedounot-satisfied, com o status exato do produto preservado no campo de motivo. - Mapeamento de controle: um mapeamento cruzado de controles do framework para o modelo de referência de capacidades da plataforma, usando o modelo de mapeamento de controle OSCAL 1.2.0. A relação é sempre
intersects-with— as capacidades abordam parte de um controle. Nunca afirma conformidade; essa afirmação existe apenas nos resultados da avaliação, condicionada a evidências operacionais ao vivo.
A restrição de honestidade na exportação OSCAL vale a pena ser declarada explicitamente. O enum de status de descoberta do OSCAL tem exatamente dois valores: satisfied e not-satisfied. Não existe “partial” ou “por design.” Um controle que está parcialmente implementado, tratado por design, com lacunas, ou unmapped mapeia para OSCAL not-satisfied, com o status real do produto sendo registrado em status.reason e uma propriedade personalizada sob o próprio namespace da plataforma (https://olivares.ai/ns/oscal). A exportação nunca transforma um controle parcialmente atendido em satisfied. Apenas controles respaldados por evidências operacionais em tempo de selagem recebem OSCAL satisfied.
Todo documento OSCAL possui propriedades de âncora de livro-razão: o hash do manifesto, o número da sequência do livro-razão no momento do selo, o hash do livro-razão e o resultado da verificação de integridade. Estes são a ponte entre o documento OSCAL que o auditor lê em sua ferramenta GRC e a cadeia à prova de adulteração subjacente que ele pode verificar independentemente.
O que a verificação offline significa concretamente
“Verificação offline” não é uma expressão de marketing. Ela descreve um procedimento técnico específico: o verificador pega a evidência exportada, executa uma ferramenta de verificação em uma máquina isolada da rede e confirma a integridade da evidência sem nenhum acesso à rede do sistema que a produziu.
A exportação arquivística da plataforma escreve uma árvore de diretórios de segmentos JSONL (uma linha por evento, JSON canônico) mais um manifesto por segmento. O manifesto registra a faixa de sequência do segmento, a contagem de eventos, os primeiros e últimos hashes da cadeia, um SHA-256 do arquivo de eventos, e o último hash do segmento anterior para continuidade entre segmentos.
O verificador offline (VerifyArchiveDir) então faz o seguinte, inteiramente em memória constante e sem chamadas de rede:
- Carregar manifestos e emparelhá-los com os arquivos de eventos. Um arquivo de eventos solto (sem manifesto) ou um manifesto cujo arquivo de eventos está ausente é uma falha. A unidade de evidência é o par.
- Transmita cada arquivo de evento linha por linha. Para cada evento, recalcule o hash da cadeia a partir dos campos arquivados usando a mesma função
EventHashque o sistema em produção utiliza. Compare-o com o hash armazenado. Verifique a ligaçãoprev_hashe a ausência de lacunas na sequência. - Verifique a canonicidade. Remarshalize cada linha analisada e confirme que ela produz uma saída idêntica em bytes à dos bytes no disco. Isso evita o contrabando de campos desconhecidos ou ataques de chave duplicada que passariam em uma verificação de hash, mas carregariam dados ocultos.
- Verifique as assinaturas Ed25519 por evento. Para cada evento que não seja um ponto de verificação, reconstrua o pré-imagem da assinatura e verifique em relação à(s) chave(s) pública(s) fixada(s).
- Verificar assinaturas de checkpoint. Para cada evento de checkpoint, verifique a assinatura no domínio do checkpoint em relação à(s) chave(s) de checkpoint fixada(s). Se apenas chaves de evento estiverem fixadas (nenhuma chave de checkpoint), um evento de checkpoint é marcado como “não verificável” — negar-fechado, não pode ser ignorado.
- Verificar continuidade entre segmentos. Confirme que a primeira sequência de cada segmento segue a última sequência do segmento anterior mais um, e que o último hash do segmento anterior corresponde ao
prev_segment_last_hashdo segmento atual. - Verificar o digest do arquivo de eventos. O SHA-256 do arquivo de eventos calculado durante a transmissão deve corresponder ao
events_sha256do manifesto.
O verificador relata a primeira inconsistência que encontra, com o número de sequência específico e um motivo legível por máquina: hash-mismatch, prev-mismatch, seq-gap, event-sig-invalid, event-sig-missing, checkpoint-sig-invalid, count-mismatch, events-sha256-mismatch ou segment-link-mismatch.
Uma limitação honesta: o verificador offline atesta exatamente o intervalo que verificou e nada fora dele. Um prefixo ou sufixo removido é indetectável offline — o diretório não indica onde a cadeia começou ou terminou. O relatório de verificação inclui um campo Ranges por inquilino com uma marcação StartsMidChain para que o auditor saiba exatamente o que foi atestado. Os checkpoints assinados na cadeia do sistema ativo cobrem o final; a exportação offline cobre o intervalo arquivado. Juntos, eles formam a atestação completa.
| Camada | O que prova | O que isso não prova |
|---|---|---|
| Cadeia de hash | Consistência interna; qualquer edição quebra a cadeia | Origem (quem escreveu os eventos) |
| Por evento Ed25519 | Origem; cada evento foi assinado pelo titular da chave | Defesa contra comprometimento no nível do host |
| Checkpoint fora da caixa | Resistência ao comprometimento do host (chave KMS/HSM nunca no host) | Granularidade por evento (cobre apenas checkpoints) |
| Exportação OSCAL | Evidência de conformidade legível por máquina, mapeada no framework | Que cada controle é totalmente atendido (apenas evidência ao vivo conta) |
| Verificação de arquivo | Rederivação offline de tudo o que foi mencionado acima | Eventos antes ou depois do intervalo exportado |
O caminho do código: da mutação do estado à evidência selada
A sequência de uma mudança de estado de sessão Claude Code para evidência verificável abrange três camadas. Em runtime_ledger.go, a função appendRunEvent constrói um PayloadHash através da hash SHA-256 dos campos de transição canônicos com prefixo de comprimento:
func runEventPayloadHash(runRef string, seq int64, event, from, to, detail, atTS string) [32]byte {
h := sha256.New()
for _, part := range []string{
runRef, strconv.FormatInt(seq, 10), event, from, to, detail, atTS,
} {
_, _ = h.Write([]byte(strconv.Itoa(len(part))))
_, _ = h.Write([]byte{':'})
_, _ = h.Write([]byte(part))
}
var sum [32]byte
copy(sum[:], h.Sum(nil))
return sum
}
Essa hash é então passada para sc.Audit().Append, que atribui o próximo número de sequência por inquilino, vincula ao hash do evento anterior, calcula o hash da cadeia deste evento via EventHash, assina com Ed25519 e insere o evento selado — tudo dentro da transação do chamador.
O resultado: no momento em que a transação é confirmada, a alteração de estado da sessão e seu registro de auditoria à prova de violações são ou ambos persistidos ou ambos revertidos. Não há uma janela em que o estado tenha mudado, mas a evidência não.
Links
- Produto: Auditoria — o rastro de auditoria e a exportação de evidências
- Produto: Conformidade — avaliações de framework e OSCAL
- Modelo de segurança — o modelo de confiança que sustenta o ledger