Voltar às atualizações
New releaseJul 14, 2026

halo-record v0.2.7

Registros de execução à prova de adulteração para agentes de IA. Encadeados por hash, sem dependências, verificáveis por qualquer pessoa.

Compartilhar

halo-record

Trilhas de auditoria à prova de adulteração para agentes de IA — Runtime Records encadeados por hash, renderizados como um Runtime Report que seus clientes podem verificar por conta própria.

Cada ação que seu agente executa (chamadas de ferramentas, chamadas de modelo, acesso a dados, aprovações) torna-se um Runtime Record em um log somente-anexável, encadeado por hash; o Runtime Report é essa cadeia renderizada como uma página HTML autoverificável. Qualquer parte que detenha um checkpoint da cadeia pode verificar que os registros por trás dele nunca foram alterados, sem confiar em quem os produziu — esse checkpoint é a peça fundamental: a cadeia sozinha é à prova de adulteração contra todos, exceto a parte que opera o gravador (LIMITS.md §1). Quando a equipe de segurança de um cliente pergunta "o que seu agente fez com nossos dados?", você entrega um link em vez de um parágrafo. As revisões de segurança já fazem perguntas sobre IA ao lado do checklist SOC 2 — e, cada vez mais, essas perguntas vêm da ISO 42001, dos artigos de manutenção de registros do EU AI Act e dos próprios questionários dos clientes. Hoje, uma garantia por escrito ainda passa. A aposta por trás deste projeto é que isso não durará muito.

Destaque em Help Net Security (agosto de 2026).

O formato de registro é aberto e livre para implementar. Este pacote é a implementação de referência: gravador, verificador, cliente de testemunha e servidor de relatórios.

Usando o halo-record, ou pensando em usá-lo? Diga quem você é e para quê → Quem está usando o halo-record?

Verifique você mesmo

Estão pedindo que você coloque um gravador dentro do seu agente. Você não deveria aceitar isso pela fé:

  • Zero dependências de runtime. Apenas biblioteca padrão. pip install halo-record instala exatamente um pacote.
  • Nenhuma chamada de rede, exceto três opcionais — ancoragem a uma testemunha (envia o id do sujeito, uma contagem de registros e duas impressões digitais da cadeia — o head e a raiz da cadeia), leitura dos checkpoints de uma testemunha (envia o id do sujeito) e o timestamp RFC 3161 (envia apenas o hash de estado de um checkpoint para uma Timestamp Authority). Todas ficam desativadas a menos que você as invoque; o conteúdo dos registros nunca sai da sua infraestrutura.
  • Argumentos brutos de ferramentas são transformados em hash, com um resumo redigido ao lado. Os argumentos são armazenados como um hash canônico mais um resumo: o texto do argumento com padrões conhecidos de segredos e PII mascarados, limitado a 200 caracteres. Uma entrada curta que não corresponde a nenhum padrão aparece integralmente no resumo; o modo somente-hash (summaries=False) não mantém resumo algum. A redação é feita na melhor medida do possível (regex sobre formatos comuns de segredos e PII, mais uma captura genérica por entropia): trate-a como defesa em profundidade, não como garantia. Campos de resultado que você fornece além de summary são selados como estão (LIMITS §13).
  • Pequeno o suficiente para auditar. ~5.300 linhas de Python (linhas de código, sem contar linhas em branco e comentários). Leia tudo em uma tarde.
  • Apache-2.0.
  • A documentação é de primeira classe. LIMITS.md (o que a cadeia não pode provar), PRIVACY.md (o que os registros contêm e o que sai da sua máquina), RETENTION.md (operar sob uma política de retenção) e REVIEWERS.md — a verificação independente de quatro comandos mais um formato de citação para achados de revisão.

O que cada camada prova — a distinção fundamental neste projeto (LIMITS.md §1): uma cadeia que você mesmo detém prova que os registros não foram editados, em relação a um head que alguém já detém; apenas checkpoints mantidos fora do operador provam que nenhum foi removido; e nenhum hash prova que toda ação foi capturada.

AlegaçãoCadeia autodetuída+ Checkpoints externos+ Captura confiável
Detectar edições em um artefato estabelecido
Detectar reescrita de histórico confirmado
Detectar checkpoints ausentes/atrasados✔ (cadência acordada)
Provar que toda ação foi registradadepende do limite de captura

Veja um antes de instalar: um Runtime Report de exemplo — dados fictícios, cadeia real, e ele se reverifica no seu navegador enquanto você observa.

Demonstração de 60 segundos

Nenhum agente necessário. Com uv, nada para instalar:``` uvx --from halo-record halo demo --serve

ou da forma clássica:```
pip install halo-record
halo demo --serve

Qualquer um dos dois cria um fornecedor fictício de agente de suporte com dois clientes, testemunha as cadeias (com um arquivo de testemunha local representando uma fora do operador — consulte LIMITS.md §1), serve seus Runtime Reports com acesso restrito e abre o console do operador no seu navegador. Em seguida, experimente o teste de adulteração: exclua uma linha de um dos arquivos .jsonl e recarregue. O relatório detecta isso.

Registre seu próprio agente

Uma linha no limite:```python from halo_record import trace

agent = trace(run_my_agent, profile="my-agent", log="audit.jsonl") # wraps your entrypoint; records the run boundary to ./audit.jsonl — add record_call() or a framework adapter at each tool boundary to capture individual calls

Um shim de conveniência `from halo import ...` também é fornecido — mas o nome `halo` no PyPI pertence a um pacote de spinner de terminal não relacionado, e se esse pacote estiver instalado ele vence a importação. `halo_record` é inequívoco, então os exemplos o utilizam.

Sem `log=`, os registros vão para `~/.halo/my-agent.jsonl` (uma cadeia por agente). O wrapper sela o limite da execução; a evidência vive nos registros por chamada. Capture-os com um adaptador de framework (matriz abaixo) — ou explicitamente, o que também mostra como a delegação se encadeia:```python
from halo_record import Recorder, record_call

rec = Recorder("audit.jsonl")

with record_call(rec, "crm.lookup", {"account": "acct-9"}) as call:            # one sealed record per tool call
    call.result = crm.lookup("acct-9")

with record_call(rec, "payments.refund", {"amount": 120},
                 parent_id=rec.last_record_id()) as call:                      # child links to the action that spawned it
    call.result = payments.refund(120)

Em seguida, renderize o relatório:``` halo report audit.jsonl -o report.html # one chain -> self-verifying HTML halo serve ./records --port 8721 # all tenants, gated per customer

O quickstart termina quando você está olhando o Runtime Report do seu próprio agente em um navegador. Se você obteve um arquivo JSONL e nenhum relatório, algo está errado: abra uma issue.

### O bloco de verificação

Se uma camada de guardrail ou de política verificou a ação, seu veredito pode acompanhar o registro — um bloco opcional que registra o que o gate decidiu, selado na cadeia de hash como qualquer outro campo:```python
from halo_record import build

build("tool_call", "security", tool="payments.refund",
      verification={"status": "allowed", "verifier": "gate/1.2",
                    "policy_ref": "sha256:1f3a...",
                    "checked_at": "2026-08-01T12:00:00Z"})

que sela no registro como:```json "verification": {"status": "allowed", "verifier": "gate/1.2", "policy_ref": "sha256:1f3a...", "checked_at": "2026-08-01T12:00:00Z"}

`record_call(...)` aceita a mesma palavra-chave `verification=`. `status` é obrigatório dentro do bloco; `verifier`, `policy_ref` e `checked_at` são opcionais. O que cada status significa:

| Status | O que o gate relata | A ação foi executada? |
|---|---|---|
| `allowed` | permitiu a ação | sim — a ação prosseguiu |
| `blocked` | negou a ação | determinado pela integração, não por este campo — um registro ainda pode carregar um desfecho, e um bloqueio por si só não prova a não execução |
| `modified` | alterou a ação antes da execução — `action.input` descreve a ação **como executada**, pós-modificação | sim, na forma alterada |
| `unverified` | foi executado (ou consultado) mas não fez nenhuma determinação — distinto de um bloco ausente, que significa que nenhuma alegação de verificação foi feita | sim — a ação prosseguiu sem um veredito |

O bloco é fornecido pelo código de integração do operador e registra o que ele relata que o gate disse — a mesma postura de confiança que `principal` (veja [LIMITS](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md#11-verification-status-is-the-gates-report-not-halos-finding)). A selagem prova que o status não foi editado após o fato; não prova que a verificação ocorreu, que o veredito estava correto, ou que uma ação bloqueada não foi executada. Isto não é verificação independente.

Para que `policy_ref` seja utilizável como evidência, use um hash de conteúdo do conjunto de regras e retenha o artefato do conjunto de regras — um rótulo não resolvível torna o campo decorativo.

## Conecte ao que você já executa

| Capturado na fronteira | Ingerido de telemetria existente |
|---|---|
| Gravador nativo (`from halo_record import trace`) | Spans OpenTelemetry GenAI |
| Interceptor MCP | Callbacks LiteLLM |
| Callback LangChain / LangGraph | Exportação Langfuse |
| Hooks do OpenAI Agents SDK | Qualquer log de gateway / proxy reverso |
| Hook do Claude Agent SDK | Hooks `PostToolUse` do Claude Code e Codex CLI (disparam após a ferramenta ser executada) |

Adaptadores de framework e caminhos de ingestão marcam cada registro com uma tag `source`, para que o relatório divulgue como cada peça de evidência foi coletada. Registros capturados e ingeridos vivem na mesma cadeia.

Para LangChain / LangGraph, é um manipulador de callback:```python
from halo_record import Recorder
from halo_record.integrations.langchain import HaloCallbackHandler

recorder = Recorder("audit.jsonl")
result = my_chain.invoke(inputs, config={"callbacks": [HaloCallbackHandler(recorder)]})   # every tool call becomes a record

Para MCP, uma chamada envolve a sessão do cliente — e então qualquer agente que use MCP emite registros para cada chamada de ferramenta, independentemente de qual framework o conduz:```python from halo_record.integrations.mcp import instrument_client_session

instrument_client_session(session, Recorder("audit.jsonl"), server="stripe") # every session.call_tool() is now recorded

Para logs de gateway ou proxy (Cloudflare AI Gateway, Portkey, nginx na frente do modelo), mapeie uma linha de log para a cadeia — explicitamente marcada como ingerida, não capturada na fronteira:```python
from halo_record.integrations.gateway import record_log

record_log(Recorder("audit.jsonl"), {"tool": "gen_ai:gpt-4o", "model": "gpt-4o", "status": 200, "subject": "acme-corp"})

Qualquer coisa que emita spans OpenTelemetry GenAI (CrewAI, LlamaIndex e a maioria dos frameworks de agentes com instrumentação OTel) entra na cadeia através do adaptador OTel, e o pacote TypeScript fornece adaptadores nativos para o Vercel AI SDK e o ecossistema de agentes JS. Falta um adaptador para a sua stack? Abra uma issue. A maioria dos adaptadores tem cerca de cem linhas.

Registre seu agente de codificação (Claude Code ou Codex)

O Claude Code dispara um hook PostToolUse após cada chamada de ferramenta. Aponte-o para halo hook e cada ação — gravações de arquivos, comandos de shell, chamadas de conector MCP — torna-se um registro em uma cadeia local. Sem alterações de código; uma entrada de configuração:```json { "hooks": { "PostToolUse": [ {"matcher": "*", "hooks": [{"type": "command", "command": "halo hook"}]} ] } }

Adicione isso a `~/.claude/settings.json` e os registros chegam em `~/.halo/audit.jsonl` (substitua com `$HALO_LOG`). Ferramentas de pura orquestração que não tocam em dados, rede ou estado externo são ignoradas — a cadeia registra ações de limite de confiança, não pensamento. Defina `HALO_HASH_ONLY=1` para registrar hashes de conteúdo sem resumos. Defina `HALO_AGENT_VERSION` (e opcionalmente `HALO_AGENT_MODEL`) para vincular cada registro à build do agente que o produziu — quando um auditor perguntar sobre a versão que estava em execução em uma determinada janela, a exportação responde por coluna em vez de por recordação.

O Codex CLI traz os mesmos hooks de ciclo de vida com o mesmo formato de evento (os hooks vêm ativados por padrão). Adicione isto a `~/.codex/hooks.json` e os comandos de shell do Codex, as edições do `apply_patch` e as chamadas MCP chegam à mesma cadeia:```json
{
  "hooks": {
    "PostToolUse": [
      {"matcher": ".*", "hooks": [{"type": "command", "command": "halo hook"}]}
    ]
  }
}

O hook distingue os dois do próprio evento (o Codex adiciona turn_id e model) e rotula cada registro como claude-code ou codex; defina HALO_HOOK_AGENT para forçar um deles. Ambos são a camada ingerida: um hook PostToolUse é disparado após a execução da ferramenta, portanto o registro é construído a partir do que o harness reporta.

Se você precisa que o relatório responda "sob quais regras esta execução aconteceu?", defina HALO_AUTHORITY_FILE como um snapshot JSON da autoridade efetiva para a sessão. Mantenha-o seguro para privacidade: hashes e refs, não prompts brutos, texto de política privada, segredos ou esquemas de ferramentas completos — formatos de segredos conhecidos são mascarados no momento do selo, mas hashes e refs passam intactos e texto de formato livre não é detectado (veja LIMITS §6). Reutilize um snapshot_id apenas enquanto a autoridade subjacente permanecer inalterada; registros consecutivos com o mesmo id e conteúdo inalterado são compactados — um id reutilizado sobre conteúdo alterado é armazenado por completo, com um aviso.```json { "snapshot_id": "auth_2026_07_08T1100Z", "captured_at": "2026-07-08T11:00:00Z", "scope": "session", "workspace": {"path_hash": "sha256:...", "git_commit": "abc1234"}, "refs": [ {"kind": "project_rules", "id": "CLAUDE.md", "hash": "sha256:...", "loaded": true, "truncated": false}, {"kind": "mcp_tool_registry", "id": "filesystem", "hash": "sha256:..."} ], "omissions": [{"kind": "private_policy", "reason": "customer_secret", "hash": "sha256:..."}], "stale_if": ["project_rules_hash_changed", "mcp_tool_registry_hash_changed"] }

| | | |
|---|---|---|
| `-h`, `--help` | Mostrar ajuda e sair | |
| `-v`, `--version` | Mostrar versão e sair | |
| `-c`, `--config` | Caminho para o arquivo de configuração | |
| `-o`, `--output` | Caminho do arquivo de saída | |
| `-f`, `--format` | Formato de saída (json, yaml, xml) | |
| `-q`, `--quiet` | Modo silencioso | |
| `-d`, `--debug` | Ativar saída de depuração | |
| `-t`, `--timeout` | Tempo limite em segundos | |
| `-r`, `--retry` | Número de tentativas | |
| `-p`, `--proxy` | URL do proxy | |
| `-u`, `--user-agent` | String do User-Agent | |
| `-k`, `--insecure` | Ignorar erros de certificado SSL | |
| `-n`, `--no-color` | Desativar saída colorida | |
| `-l`, `--log` | Caminho do arquivo de log | |
| `-s`, `--silent` | Modo silencioso | |
| `-V`, `--verbose` | Saída detalhada | |
| `-a`, `--api-key` | Chave de API | |
| `-e`, `--endpoint` | URL do endpoint | |
| `-m`, `--method` | Método HTTP | |
| `-H`, `--header` | Cabeçalho HTTP | |
| `-b`, `--body` | Corpo da requisição | |
| `-x`, `--proxy-auth` | Autenticação de proxy | |
| `-z`, `--compress` | Ativar compressão | |
| `-i`, `--include` | Incluir cabeçalhos na saída | |
| `-I`, `--head` | Enviar requisição HEAD | |
| `-X`, `--request` | Especificar método de requisição | |
| `-L`, `--location` | Seguir redirecionamentos | |
| `-A`, `--user-agent` | String do User-Agent | |
| `-O`, `--remote-name` | Salvar com nome remoto | |
| `-R`, `--remote-time` | Definir hora do arquivo remoto | |
| `-S`, `--show-error` | Mostrar erros | |
| `-T`, `--upload-file` | Enviar arquivo | |
| `-w`, `--write-out` | Formato de saída | |
| `-y`, `--speed-time` | Tempo de velocidade | |
| `-Y`, `--speed-limit` | Limite de velocidade | |
| `-0`, `--http1.0` | Usar HTTP 1.0 | |
| `-1`, `--tlsv1` | Usar TLSv1 | |
| `-2`, `--sslv2` | Usar SSLv2 | |
| `-3`, `--sslv3` | Usar SSLv3 | |
| `-4`, `--ipv4` | Usar IPv4 | |
| `-6`, `--ipv6` | Usar IPv6 | |
| `-#`, `--progress-bar` | Mostrar barra de progresso | |
| `-Z`, `--parallel` | Transferência paralela | |
| `-J`, `--remote-header-name` | Usar nome do cabeçalho remoto | |
| `-K`, `--config` | Ler configuração do arquivo | |
| `-N`, `--no-buffer` | Desativar buffer de saída | |
| `-P`, `--ftp-port` | Porta FTP | |
| `-Q`, `--quote` | Enviar comando FTP | |
| `-U`, `--proxy-user` | Usuário do proxy | |
| `-W`, `--wtf` | Modo WTF | |
| `-E`, `--cert` | Certificado do cliente | |
| `-F`, `--form` | Dados de formulário | |
| `-G`, `--get` | Enviar dados com GET | |
| `-D`, `--dump-header` | Despejar cabeçalhos | |
| `-C`, `--continue-at` | Continuar transferência | |
| `-B`, `--use-ascii` | Usar transferência ASCII | |
| `-M`, `--manual` | Mostrar manual | |
| `-O`, `--remote-name` | Salvar com nome remoto | |
| `-Q`, `--quote` | Enviar comando FTP | |
| `-R`, `--remote-time` | Definir hora do arquivo remoto | |
| `-S`, `--show-error` | Mostrar erros | |
| `-T`, `--upload-file` | Enviar arquivo | |
| `-U`, `--proxy-user` | Usuário do proxy | |
| `-V`, `--version` | Mostrar versão e sair | |
| `-W`, `--wtf` | Modo WTF | |
| `-X`, `--request` | Especificar método de requisição | |
| `-Y`, `--speed-limit` | Limite de velocidade | |
| `-Z`, `--parallel` | Transferência paralela | |
| `-a`, `--append` | Anexar à saída | |
| `-b`, `--cookie` | String de cookie | |
| `-c`, `--cookie-jar` | Arquivo de cookie jar | |
| `-d`, `--data` | Dados HTTP POST | |
| `-e`, `--referer` | Referer | |
| `-f`, `--fail` | Falhar silenciosamente em erros HTTP | |
| `-g`, `--globoff` | Desativar interpretação de glob | |
| `-h`, `--help` | Mostrar ajuda e sair | |
| `-i`, `--include` | Incluir cabeçalhos na saída | |
| `-j`, `--junk-session-cookies` | Descartar cookies de sessão | |
| `-k`, `--insecure` | Ignorar erros de certificado SSL | |
| `-l`, `--list-only` | Listar apenas nomes | |
| `-m`, `--max-time` | Tempo máximo de transferência | |
| `-n`, `--netrc` | Ler .netrc | |
| `-o`, `--output` | Arquivo de saída | |
| `-p`, `--proxytunnel` | Usar túnel HTTP proxy | |
| `-q`, `--disable` | Desativar .curlrc | |
| `-r`, `--range` | Recuperar intervalo de bytes | |
| `-s`, `--silent` | Modo silencioso | |
| `-t`, `--telnet-option` | Opção Telnet | |
| `-u`, `--user` | Usuário e senha | |
| `-v`, `--verbose` | Saída detalhada | |
| `-w`, `--write-out` | Formato de saída | |
| `-x`, `--proxy` | Usar proxy | |
| `-y`, `--speed-time` | Tempo de velocidade | |
| `-z`, `--time-cond` | Condição de tempo | |
| `-0`, `--http1.0` | Usar HTTP 1.0 | |
| `-1`, `--tlsv1` | Usar TLSv1 | |
| `-2`, `--sslv2` | Usar SSLv2 | |
| `-3`, `--sslv3` | Usar SSLv3 | |
| `-4`, `--ipv4` | Usar IPv4 | |
| `-6`, `--ipv6` | Usar IPv6 | |
| `-#`, `--progress-bar` | Mostrar barra de progresso | |
| `-Z`, `--parallel` | Transferência paralela | |
| `-J`, `--remote-header-name` | Usar nome do cabeçalho remoto | |
| `-K`, `--config` | Ler configuração do arquivo | |
| `-N`, `--no-buffer` | Desativar buffer de saída | |
| `-P`, `--ftp-port` | Porta FTP | |
| `-Q`, `--quote` | Enviar comando FTP | |
| `-U`, `--proxy-user` | Usuário do proxy | |
| `-W`, `--wtf` | Modo WTF | |
| `-E`, `--cert` | Certificado do cliente | |
| `-F`, `--form` | Dados de formulário | |
| `-G`, `--get` | Enviar dados com GET | |
| `-D`, `--dump-header` | Despejar cabeçalhos | |
| `-C`, `--continue-at` | Continuar transferência | |
| `-B`, `--use-ascii` | Usar transferência ASCII | |
| `-M`, `--manual` | Mostrar manual | |```sh
HALO_AUTHORITY_FILE=./authority.json halo hook

O snapshot é selado na mesma cadeia de hash que os registos de ação. Uma predefinição adequada é um snapshot ao nível da sessão no início, mais um novo snapshot quando as regras, Skills, hooks, registos de ferramentas MCP ou política de compactação mudam. Para manter sessões longas leves, registos consecutivos com o mesmo authority.snapshot_id são compactados após o primeiro snapshot completo: os registos posteriores mantêm apenas {"snapshot_id": "...", "same_as_previous": true}. O ponteiro permanece encadeado por hash, mas o bloco volumoso de refs/omissions/stale-if não é repetido em cada ação. (A compactação é por processo de registo: a captura ao estilo hook que gera um processo por chamada de ferramenta volta a armazenar o corpo completo sempre que o snapshot completo anterior não é o registo final, pelo que processos de curta duração trocam o tamanho da cadeia pela proteção de reutilização.)

Os utilizadores do SDK anexam o mesmo bloco diretamente — build(..., authority={...}) ou record_call(..., authority={...}); a captura apenas de hash é a mesma superfície (summaries=False em qualquer um deles):```python from halo_record import Recorder, record_call

rec = Recorder("audit.jsonl") with record_call(rec, "crm.lookup", {"account": "acct-9"}, authority={"snapshot_id": "auth_1", "rules_hash": "sha256:..."}, summaries=False) as call: # hash-only: no summaries, no excerpts call.result = crm.lookup("acct-9")

Em seguida, o habitual:```
halo verify ~/.halo/audit.jsonl
halo report ~/.halo/audit.jsonl -o report.html

Qualquer runtime de agente que exponha um hook pós-ação pode alimentar o mesmo comando — o hook lê um evento como JSON no stdin e acrescenta um registro.

Uma cadeia, um escritor de cada vez. Uma cadeia é uma lista encadeada: dois escritores que leem a mesma cabeça e ambos acrescentam vão bifurcá-la (dois registros reivindicando o mesmo predecessor), e a verificação nomeará os registros afetados. O Recorder serializa seus próprios acréscimos com um lock sidecar (POSIX flock aqui; um diretório de lock no pacote TypeScript), e halo hook acrescenta através do Recorder, então a configuração de hook acima está coberta. Qualquer coisa que escreva diretamente no arquivo da cadeia — um hook feito à mão, workers paralelos, um log shipper — deve manter um lock exclusivo equivalente durante toda a sequência de ler-cabeça-e-depois-acrescentar, ou escrever em cadeias por processo. LIMITS.md seção 9 cobre isso em detalhe, incluindo a fronteira entre linguagens.

Quando a gravação falha

Os dois estilos de integração falham em direções opostas, de propósito — escolha aquele cuja falha você consegue tolerar:

  • Adaptadores de framework (LangChain, hooks via callback managers) falham abertos. Se um registro não pode ser gravado (disco cheio, permissões), a ação do agente é concluída normalmente e o registro é perdido. O handler do LangChain imprime um aviso alto no stderr e conta a perda (handler.lost_records), mas nada na própria cadeia pode mostrar um registro que nunca foi gravado — uma cadeia paralisada ainda verifica. Checkpoints de testemunha em uma cadência são o que torna uma cadeia paralisada visível: um checkpoint esperado que nunca chega é o alarme.
  • O wrapper nativo trace() falha fechado. Se o registro não pode ser gravado, a exceção se propaga para a ação do agente — sem evidência, sem ação. Mais estrito, e pode interromper seu agente.

Nenhum dos padrões é certo para todos; saiba qual você está executando.

Integridade vs. completude (leia esta parte)

Seja preciso sobre o que cada camada prova — porque são afirmações diferentes, e as diferenças são o ponto:

Uma cadeia mantida pelo próprio operador prova integridade em relação a uma cabeça estabelecida: dada uma cabeça de cadeia que alguém já possui, qualquer edição, reordenação ou exclusão nos registros por trás dela torna-se detectável. Por si só — antes que alguém fora do operador tenha visto uma cabeça — uma cadeia prova consistência interna, não histórico: um operador poderia descartar um registro e re-selar, e o novo arquivo verificaria. A cadeia torna-se historicamente comprometida no momento em que sua cabeça sai do controle do operador.

Essa é a testemunha: uma parte fora do operador mantendo checkpoints periódicos da cadeia — o id do sujeito, uma contagem de registros e duas impressões digitais da cadeia (a cabeça e a raiz da cadeia); o payload exato, e nada mais. Checkpoints tornam a reescrita de histórico comprometido detectável, e um checkpoint perdido é ele próprio um evento visível:``` halo anchor audit.jsonl witness.jsonl # anchor a checkpoint to a local witness halo anchor audit.jsonl witness.jsonl --check # completeness verdict against it

Para *time* especificamente, um carimbo de data/hora RFC 3161 externo substitui o relógio autodeclarado do checkpoint por uma prova de uma Timestamp Authority que o operador não controla — "esta cadeia atingiu este cabeçalho não depois de T", verificável por terceiros sem infraestrutura hospedada. A TSA padrão é a freetsa.org gratuita (adequada para avaliação); aponte para uma TSA comercial (DigiCert / Sectigo / a sua própria) com `--tsa` para produção:```
halo anchor audit.jsonl witness.jsonl --timestamp          # attach a TSA time proof to the checkpoint
halo anchor audit.jsonl witness.jsonl --check              # reads the token's claimed time

--check confirma que o token vincula este estado da cadeia e lê o seu tempo atestado, mas não valida a assinatura da TSA — isso é deliberadamente deixado para uma ferramenta padrão, para que um revisor não precise confiar em nenhum código nosso. Para verificar o tempo de forma independente (isto é o que você entrega a um revisor de segurança):```

tsa.token_b64 lives in the witness log; decode the latest one to a standard .tsr file

python3 -c 'import json,base64; cps=[json.loads(l) for l in open("witness.jsonl") if l.strip()]; t=[c["tsa"] for c in cps if c.get("tsa")][-1]; open("token.tsr","wb").write(base64.b64decode(t["token_b64"])); print(t["digest"])' curl -s -o tsa-ca.pem https://freetsa.org/files/cacert.pem # CA for the default TSA (a commercial TSA publishes its own) openssl ts -verify -digest -in token.tsr -CAfile tsa-ca.pem # → "Verification: OK"

Mais um limite, dito de forma clara: nem a cadeia nem a testemunha provam que toda ação do mundo real passou pelo gravador. Isso é **completude de captura** — uma propriedade de onde o gravador se situa na pilha (instrumentação nativa, hooks, ingestão via gateway), não de qualquer hash. Os registros carregam uma tag `source` exatamente por esse motivo. A tabela de afirmações em "Verifique você mesmo" no topo desta página é o resumo dessas três camadas.

Qualquer pessoa pode executar uma testemunha. Uma testemunha que você mesmo executa compromete o histórico com *você*; comprometê-lo com *seu cliente* exige uma testemunha na qual ele tenha motivos para confiar. O protocolo é aberto de qualquer forma.

Uma testemunha hospedada e reconhecida é como este projeto se sustentará. Acesso antecipado: [email protected].

## Dados pessoais na cadeia

A cadeia é somente-anexo: qualquer coisa selada em um registro permanece lá, porque
removê-la quebraria a verificação de tudo que vem depois. Os argumentos de ferramentas já
são tratados — armazenados como um hash mais um resumo redigido limitado a 200 caracteres
(o modo somente-hash não mantém resumo).

Observe o limite nessa frase: *redigido*, não removido. [LIMITS.md](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md)
a seção 6 é explícita que um nome ou um endereço postal não tem padrão confiável, então
nenhum dos dois é detectado e nenhum dos dois é mascarado. E `subject` não é o único campo que
carrega texto que você fornece — `principal`, `approver`, `session_id`, `agent`,
`authority`, `data` e os resumos também carregam.

O padrão que funciona: coloque um id pseudônimo estável na cadeia e mantenha o
mapeamento para qualquer indivíduo em um sistema do qual você possa excluir. Uma solicitação de apagamento é
então satisfeita excluindo o mapeamento. Mantenha `subject` apontando para a organização locatária, não para uma pessoa:```python
from halo_record import build

build("tool_call", "privacy", subject={"id": "acme", "name": "Acme Corp"})

Nenhuma configuração impõe isto — é uma disciplina na forma como chamas o gravador. Torna o apagamento tratável; não é anonimização, e ainda não existe retenção ou eliminação incorporada. A secção 13 do LIMITS.md tem a lista completa de campos, explica porque é que a impressão digital do input armazenada pode confirmar um valor adivinhável mesmo depois de o mapeamento desaparecer, e termina com perguntas que um revisor deve fazer.

Registar uma chamada a um modelo (a primeira pergunta do comprador: "que modelo viu os meus dados?"):```python from halo_record import record_model_call

record_model_call(rec, provider="anthropic", model="claude-sonnet-4-6", zdr=True, purpose="draft support reply", subject="acme") # tool=model.generate, scope=model:anthropic

## Onde isto se encaixa em uma pilha de conformidade

halo-record é uma camada de evidências, não uma certificação. Ele produz o artefato que os frameworks de avaliação continuam pedindo com palavras diferentes. Uma nota de escopo que rege cada item abaixo: estas são alegações de integridade sobre o registro; a completude em relação ao operador exige uma testemunha externa mantendo checkpoints ([LIMITS.md §1](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md)).

- **Questionários de segurança e revisões SOC 2:** responda às seções de IA com um Runtime Report verificável em vez de capturas de tela e texto corrido.
- **AIUC-1:** produz a evidência de logging à prova de adulteração (E015.4) e registros de cadeia de execução com eventos de autorização (E015.2 — com uma lacuna declarada: traces de raciocínio não são capturados) que o controle de Accountability E015 do padrão nomeia. O próprio E015 é obrigatório; E015.2 e E015.4 são seu nível suplementar: não exigidos para aprovação, o que um fornecedor adota quando um cliente ou regulador o solicita. Uma vez que a cadeia esteja ancorada a uma testemunha em que a parte confiante tenha razão para confiar — uma testemunha que o próprio operador executa não fornece isso —, isso é uma cadeia continuamente testemunhada em vez de uma reconstruída no momento da auditoria (o que entrou na cadeia ainda é limitado pela superfície de captura). Um mapeamento de evidências controle a controle, incluindo o que está deliberadamente fora de escopo, está em [`AIUC.md`](https://github.com/bkuan001/halo-record/blob/main/AIUC.md).
- **OWASP Top 10 for Agentic Applications 2026:** oito das dez ameaças mapeiam para regras de política determinísticas sobre o registro, duas estão marcadas como fora de escopo com justificativas, e o pacote é entregue executável. Um mapeamento aproximado da comunidade, não um artefato oficial da OWASP. Veja [`OWASP.md`](https://github.com/bkuan001/halo-record/blob/main/OWASP.md).
- **AARM (CSA):** produz o recibo de ação à prova de adulteração que o AARM especifica — R5, e a metade de selagem do R6 (a identidade é selada no hash, não autenticada criptograficamente). halo-record é a camada de recibos; combine-o com um gateway de enforcement para um sistema AARM completo. Veja [`AARM.md`](https://github.com/bkuan001/halo-record/blob/main/AARM.md).
- **Agentic Trust Controls:** os registros de runtime por trás dos controles de evidência do ATC — logging de ações à prova de adulteração (RBM-03) e a metade de registro da atestação de autoridade (AID-05; a metade de enforcement pertence ao gate) em um único registro encadeado. Veja [`ATC.md`](https://github.com/bkuan001/halo-record/blob/main/ATC.md).
- **CSA AI Controls Matrix (AICM) / STAR for AI:** a evidência do domínio LOG — registros de auditoria gerados, selados contra modificação não detectada, eventos de entrada e saída registrados — mapeada controle a controle em [`AICM.md`](https://github.com/bkuan001/halo-record/blob/main/AICM.md). O próprio crosswalk v1.1 da CSA vincula esse domínio ao AIUC-1 E015.
- **MITRE ATLAS:** a mitigação de telemetria de agentes (AML.M0024) implementada com uma propriedade de integridade que o próprio ATLAS não exige — o log é verificável por alguém fora do operador. Veja [`ATLAS.md`](https://github.com/bkuan001/halo-record/blob/main/ATLAS.md).
- **EU AI Act / ISO 42001 / NIST AI RMF:** as obrigações de manutenção de registros e logging que esses frameworks descrevem são a mesma classe de artefato — mapeadas de forma conservadora em [EU-AI-ACT.md](https://github.com/bkuan001/halo-record/blob/main/EU-AI-ACT.md), [ISO42001.md](https://github.com/bkuan001/halo-record/blob/main/ISO42001.md) e [NIST-AI-RMF.md](https://github.com/bkuan001/halo-record/blob/main/NIST-AI-RMF.md).

Nada disso certifica coisa alguma por si só. Dá ao seu avaliador algo verificável para examinar. Os limites — o que o halo-record deliberadamente não faz, e o que dizer quando um revisor pergunta — estão documentados em [`LIMITS.md`](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md).

### Levando a evidência para sua plataforma de GRC

A maioria das plataformas de GRC (Vanta, Drata e similares) aceita arquivos enviados como evidência personalizada contra um controle. A exportação do halo-record é feita para se encaixar nesse fluxo:```bash
halo export audit.jsonl --from 2026-06-01 --to 2026-06-30 -o evidence.csv

# scope the export to the actions a control covers
halo export audit.jsonl --from 2026-06-01 --to 2026-06-30 --tool email.send --tool db.query -o evidence.csv

Isso grava dois arquivos para a janela de auditoria: o CSV (uma linha por ação registrada, agrupada da esquerda para a direita como quando → o que aconteceu → quem → sob qual autoridade → o que foi sinalizado → proveniência → como verificar, incluindo um resumo em linguagem simples e redigido da chamada e seu resultado, o build do agente e o modelo que produziu cada um, a identidade em nome de quem ele foi executado, o registro que o causou, sua decisão de autorização e escopo, e quaisquer categorias de dados pessoais ou sinalizações de ameaças ingeridas) e um manifesto (evidence.csv.manifest.json) que vincula o CSV à sua origem — o hash de cabeçalho da cadeia o liga ao log verificável de onde veio, e csv_sha256 é o próprio hash do arquivo exportado, de modo que um CSV editado após a exportação não corresponde mais ao seu manifesto. Restrinja a população com --tool quando um controle cobre apenas determinadas ações; o manifesto registra o filtro, de modo que uma exportação com escopo divulga que é um subconjunto em vez de ser lida como a população inteira. Faça upload de ambos em seu controle de logging ou monitoramento; anexe o HTML do Runtime Report quando um revisor quiser verificar a cadeia por conta própria. A exportação se recusa a ser executada em uma cadeia que falha na verificação.

Uma integração de push nativa — evidências chegando automaticamente à sua plataforma — está no roadmap. O caminho de arquivo acima funciona hoje com qualquer plataforma que aceite evidências enviadas por upload.

CLI```

halo verify validate schema + hash chain (exit 1 broken, 3 empty chain; CI-friendly) halo report render a chain as a self-verifying HTML Runtime Report (--from/--to: a date-windowed report covering only the review period) halo policy corroborate a chain against a declarative policy pack (per-rule pass / violation / evidence-gap; exit 1 violated, 3 nothing in scope) halo serve serve per-tenant reports over HTTP, access-scoped per customer halo grant designate a report recipient (email or domain) halo viewers list who has unlocked a gated report halo anchor witness a chain head, or --check completeness (exit 1 incomplete, 3 unwitnessed) halo witness-serve run a witness over HTTP: vendors anchor chain heads, viewers fetch checkpoints halo demo scaffold the full vendor demo (record -> witness -> gated report) halo export date-bounded evidence export: CSV + manifest tied to the chain head halo sample emit a valid example log halo hash canonical sha256 of a JSON value halo hook Claude Code PostToolUse hook

## Modelo de integridade

Para calcular o hash de um registro: pegue o registro excluindo `integrity.hash`, com `integrity.prev_hash` definido como o hash do registro anterior; canonize com RFC 8785 (JSON Canonicalization Scheme); aplique SHA-256 aos bytes. O `prev_hash` do primeiro registro é 64 zeros. A verificação recalcula cada hash e confere cada link. Nenhum segredo é necessário; esse é o ponto.

Acha que consegue adulterar uma cadeia sem que o verificador perceba? [Tentativas e resultados estão aqui](https://github.com/bkuan001/halo-record/discussions/2).

Referência completa de campos: [`halo-record.schema.json`](https://github.com/bkuan001/halo-record/blob/main/src/halo_record/halo-record.schema.json).

## TypeScript

O mesmo gravador é distribuído para Node: [`halo-record-ts`](https://github.com/bkuan001/halo-record-ts). Mesmo formato de cadeia, mesmo protocolo de testemunha. Registros gravados em qualquer uma das linguagens são verificados por qualquer um dos verificadores.

## Exemplos da Comunidade

[trail-halo-poc](https://github.com/AmeyParle/trail-halo-poc) — prova de conceito da comunidade que vincula a autoridade principal de um registro Halo a credenciais TRAIL: uma vinculação recíproca org–agente e concessões de escopo assinadas pela org registradas em uma cadeia Halo, com uma suíte de verificação adversarial.

## Contribuindo

Issues, discussões e pull requests são bem-vindos — consulte [CONTRIBUTING.md](https://github.com/bkuan001/halo-record/blob/main/CONTRIBUTING.md) para as regras básicas (versão curta: testes obrigatórios, PRs pequenos, mudanças de schema são discutidas primeiro).

## Licença

Apache-2.0

Categorias