
Preuves d'exécution inviolables pour les agents IA : Runtime Records chaînés par hachage, sans dépendance, vérifiables par tous.
Enregistrements d'exécution infalsifiables pour agents IA : la piste d'audit que le fournisseur exécute mais ne peut pas modifier.
Chaque action de votre agent (appels d'outils, appels de modèles, accès aux données, approbations) devient un enregistrement dans un journal en ajout uniquement, chaîné par hachage. Toute partie détenant un point de contrôle de la chaîne peut vérifier que les enregistrements qui le précèdent n'ont jamais été modifiés, sans avoir à faire confiance à celui qui les a produits. Quand l'équipe sécurité d'un client demande « Qu'est-ce que votre agent a fait avec nos données ? », vous lui donnez un lien au lieu d'un paragraphe. Les revues de sécurité posent déjà des questions sur l'IA à côté de la checklist SOC 2, et aujourd'hui une assurance écrite est encore acceptée. Le pari derrière ce projet est que cela ne sera bientôt plus le cas.
Le format d'enregistrement est ouvert et libre à implémenter. Ce paquet est l'implémentation de référence : enregistreur, vérificateur, client témoin et serveur de rapports.
On vous demande d'intégrer un enregistreur dans votre agent. Vous ne devriez pas l'accepter sans preuve :
pip install halo-record installe exactement un paquet.Aucun agent requis. Avec uv, rien à installer :
uvx --from halo-record halo demo --serve
ou la méthode classique :
pip install halo-record
halo demo --serve
Chacune des deux méthodes génère un fournisseur fictif d'agent de support avec deux clients, atteste les chaînes, sert leurs rapports d'exécution à accès contrôlé et ouvre la console d'exploitation dans votre navigateur. Ensuite, essayez le test d'altération : supprimez une ligne de l'un des fichiers .jsonl et rechargez. Le rapport le détecte.
Une ligne à la frontière :
from halo import trace
agent = trace(run_my_agent, profile="my-agent", log="audit.jsonl") # wraps your entrypoint; records every tool call to ./audit.jsonl
Sans log=, les enregistrements vont dans ~/.halo/my-agent.jsonl (une chaîne par agent). Ou utilisez l'adaptateur pour ce que vous exécutez déjà (voir la matrice ci-dessous). Puis générez le rapport :
halo report audit.jsonl -o report.html # one chain -> self-verifying HTML
halo serve ./records --port 8721 # all tenants, gated per customer
Le démarrage rapide se termine lorsque vous consultez le Rapport d'exécution de votre propre agent dans un navigateur. Si vous obtenez un fichier JSONL et aucun rapport, quelque chose ne va pas : ouvrez une issue.
| Capturé à la frontière |
|---|
Chaque enregistrement porte une étiquette source, afin que le rapport divulgue comment chaque élément de preuve a été collecté. Les enregistrements capturés et ingérés vivent dans la même chaîne.
Tout ce qui émet des spans OpenTelemetry GenAI (CrewAI, LlamaIndex et la plupart des frameworks d'agents avec instrumentation OTel) atterrit dans la chaîne via l'adaptateur OTel, et le paquet TypeScript fournit des adaptateurs natifs pour le Vercel AI SDK et l'écosystème d'agents JS. Un adaptateur manque pour votre pile ? Ouvrez une issue. La plupart des adaptateurs font environ une centaine de lignes.
Claude Code déclenche un hook PostToolUse après chaque appel d'outil. Pointez-le vers halo hook et chaque action — écritures de fichiers, commandes shell, appels de connecteurs MCP — devient un enregistrement dans une chaîne locale. Aucune modification de code ; une seule entrée de configuration :
{
"hooks": {
"PostToolUse": [
{"matcher": "*", "hooks": [{"type": "command", "command": "halo hook"}]}
]
}
}
Ajoutez cela à ~/.claude/settings.json et les enregistrements atterrissent dans ~/.halo/audit.jsonl (surchargeable via $HALO_LOG). Les outils de pure orchestration qui ne touchent ni données, ni réseau, ni état externe sont ignorés — la chaîne enregistre les actions à la frontière de confiance, pas la réflexion. Définissez HALO_HASH_ONLY=1 pour enregistrer les hachages de contenu sans résumés. Définissez HALO_AGENT_VERSION (et éventuellement HALO_AGENT_MODEL) pour lier chaque enregistrement à la build d'agent qui l'a produit — quand un auditeur demande quelle version tournait pendant une période donnée, l'export fournit la réponse dans une colonne, plutôt que de compter sur la mémoire.
Si vous avez besoin que le rapport réponde à « selon quelles règles cette exécution a-t-elle eu lieu ? », définissez HALO_AUTHORITY_FILE sur un instantané JSON de l'autorité effective de la session. Gardez-le respectueux de la vie privée : hachages et références, pas de prompts bruts, de textes de politique privés, de secrets ni de schémas d'outils complets.
{
"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"]
}
HALO_AUTHORITY_FILE=./authority.json halo hook
L'instantané est scellé dans la même chaîne de hachage que les enregistrements d'actions. Une bonne valeur par défaut est un instantané au niveau de la session au démarrage, plus un nouvel instantané lorsque les règles, les Skills, les hooks, les registres d'outils MCP ou la politique de compactage changent. Pour garder les longues sessions légères, les enregistrements consécutifs ayant le même authority.snapshot_id sont compactés après le premier instantané complet : les enregistrements suivants ne conservent que {"snapshot_id": "...", "same_as_previous": true}. Le pointeur reste chaîné par hachage, mais le volumineux bloc refs/omissions/stale-if n'est pas répété à chaque action. Puis, la méthode habituelle :
halo verify ~/.halo/audit.jsonl
halo report ~/.halo/audit.jsonl -o report.html
Tout runtime d'agent qui expose un hook post-action peut alimenter la même commande — le hook lit un événement en JSON sur stdin et ajoute un enregistrement.
Soyez précis sur ce que chaque couche prouve — car ce sont des affirmations différentes, et c'est là que réside l'intérêt :
Une chaîne détenue en interne prouve l'intégrité relative à une tête établie : étant donné une tête de chaîne que quelqu'un détient déjà, toute modification, réorganisation ou suppression dans les enregistrements qui la précèdent devient détectable. À elle seule — avant que quiconque en dehors de l'exploitant n'ait vu une tête — une chaîne prouve la cohérence interne, pas l'historique : un exploitant pourrait supprimer un enregistrement et resceler, et le nouveau fichier passerait la vérification. La chaîne devient engagée historiquement à l'instant où sa tête quitte le contrôle de l'exploitant.
C'est là qu'intervient le témoin : une partie extérieure à l'exploitant qui détient des empreintes périodiques de la chaîne (un compteur et un hachage de tête, rien d'autre). Les points de contrôle rendent détectable la réécriture d'un historique engagé, et un point de contrôle manqué est lui-même un événement visible :
halo anchor audit.jsonl witness.jsonl # anchor a checkpoint to a local witness
halo anchor audit.jsonl witness.jsonl --check # completeness verdict against it
Une dernière limite, énoncée clairement : ni la chaîne ni le témoin ne prouvent que chaque action réelle est passée par l'enregistreur. C'est l'exhaustivité de capture — une propriété de l'emplacement de l'enregistreur dans la pile (instrumentation native, hooks, ingestion par passerelle), et non d'un quelconque hachage. Les enregistrements portent une étiquette source précisément pour cette raison.
N'importe qui peut exécuter un témoin. Un témoin que vous exécutez vous-même engage l'historique envers vous ; l'engager envers votre client nécessite un témoin auquel il a des raisons de faire confiance. Le protocole est ouvert dans les deux cas.
Un témoin hébergé et reconnu est ce qui permettra à ce projet de se pérenniser. Accès anticipé : [email protected].
halo-record est une couche de preuve, pas une certification. Il produit l'artefact que les cadres d'évaluation ne cessent de réclamer en des termes différents :
AARM.md.ATC.md.Rien de tout cela ne certifie quoi que ce soit par lui-même. Cela donne à votre évaluateur quelque chose de vérifiable à examiner. Les limites — ce que halo-record ne fait volontairement pas, et que dire lorsqu'un examinateur demande — sont documentées dans LIMITS.md.
halo verify validate schema + hash chain (non-zero exit on failure; 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 serve serve per-tenant reports over HTTP, access-scoped per customer
halo grant designate a report recipient (email or domain)
halo anchor witness a chain head, or --check completeness
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
Pour calculer le hachage d'un enregistrement : prenez l'enregistrement en excluant integrity.hash, avec integrity.prev_hash défini sur le hachage de l'enregistrement précédent ; canonicalisez avec RFC 8785 (JSON Canonicalization Scheme) ; appliquez SHA-256 aux octets. Le prev_hash du premier enregistrement est 64 zéros. La vérification recalcule chaque hachage et vérifie chaque lien. Aucun secret requis ; c'est tout l'intérêt.
Vous pensez pouvoir altérer une chaîne sans que le vérificateur ne s'en aperçoive ? Tentatives et résultats disponibles ici.
Référence complète des champs : halo-record.schema.json.
Le même enregistreur est fourni pour Node : halo-record-ts. Même format de chaîne, même protocole témoin. Les enregistrements écrits dans l'un ou l'autre langage se vérifient avec l'un ou l'autre vérificateur.
Issues, discussions et pull requests bienvenues — voir CONTRIBUTING.md pour les règles de base (version courte : tests requis, petites PR, les modifications de schéma sont d'abord discutées).
Apache-2.0.
| Ingéré depuis la télémétrie existante |
|---|
Enregistreur natif (from halo import trace) | Spans OpenTelemetry GenAI |
| Intercepteur MCP | Callbacks LiteLLM |
| Callback LangChain / LangGraph | Export Langfuse |
| Hooks OpenAI Agents SDK | Tout journal de passerelle / proxy inverse |
| Hook Claude Code / Claude Agent SDK |
| Affirmation | Chaîne détenue en interne | + Points de contrôle externes | + Capture de confiance |
|---|
| Détecter les modifications d'un artefact établi | ✔ | ✔ | ✔ |
| Détecter la réécriture d'un historique engagé | — | ✔ | ✔ |
| Détecter les points de contrôle manquants/en retard | — | ✔ (cadence convenue) | ✔ |
| Prouver que chaque action a été enregistrée | — | — | dépend de la frontière de capture |