Retour aux mises à jour
New releaseJul 14, 2026

halo-record v0.2.7

Enregistrements d'exécution inviolables pour les agents IA. Chaînés par hachage, sans dépendances, vérifiables par quiconque.

Partager

halo-record

Pistes d'audit inviolables pour les agents IA — des Runtime Records chaînés par hachage, restitués sous forme de Runtime Report que vos clients peuvent vérifier eux-mêmes.

Chaque action effectuée par votre agent (appels d'outils, appels de modèles, accès aux données, approbations) devient un Runtime Record dans un journal en ajout seul, chaîné par hachage ; le Runtime Report est cette chaîne restituée sous forme de page HTML auto-vérifiable. Toute partie détenant un point de contrôle de la chaîne peut vérifier que les enregistrements qui la composent n'ont jamais été altérés, sans faire confiance à celui qui les a produits — ce point de contrôle est la pièce maîtresse : la chaîne seule est inviolable face à tous, sauf face à la partie qui exploite l'enregistreur (LIMITS.md §1). Quand l'équipe sécurité d'un client demande « qu'a fait votre agent avec nos données ? », vous leur 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 de plus en plus, ces questions proviennent de l'ISO 42001, des articles de tenue de registres du règlement européen sur l'IA, et des questionnaires propres aux clients. Aujourd'hui, une assurance écrite suffit encore. Le pari derrière ce projet est que cela ne durera pas.

Présenté dans Help Net Security (août 2026).

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.

Vous utilisez halo-record, ou vous y pensez ? Dites-moi qui vous êtes et pour quoi faire → Qui utilise halo-record ?

Vérifiez-le vous-même

On vous demande d'installer un enregistreur à l'intérieur de votre agent. Vous ne devriez pas accepter cela sur parole :

  • Zéro dépendance à l'exécution. Bibliothèque standard uniquement. pip install halo-record installe exactement un paquet.
  • Aucun appel réseau, sauf trois optionnels — l'ancrage à un témoin (envoie l'identifiant du sujet, un nombre d'enregistrements et deux empreintes de chaîne — la tête et la racine de chaîne), la relecture des points de contrôle d'un témoin (envoie l'identifiant du sujet), et l'horodatage RFC 3161 (envoie uniquement le hachage d'état d'un point de contrôle à une autorité d'horodatage). Tous sont désactivés sauf si vous les invoquez ; le contenu des enregistrements ne quitte jamais votre infrastructure.
  • Les arguments bruts des outils sont hachés, avec un résumé expurgé en complément. Les arguments sont stockés sous forme de hachage canonique accompagné d'un résumé : le texte des arguments avec les motifs connus de secrets et de données personnelles masqués, limité à 200 caractères. Une entrée courte ne correspondant à aucun motif apparaît intégralement dans le résumé ; le mode hachage seul (summaries=False) ne conserve aucun résumé. L'expurgation est effectuée au mieux (regex sur les formats courants de secrets et de données personnelles, plus un attrape-tout par entropie) : considérez-la comme une défense en profondeur, non comme une garantie. Les champs de résultat que vous fournissez au-delà de summary sont scellés tels quels (LIMITS §13).
  • Assez petit pour être audité. ~5 300 lignes de Python (lignes de code, sans compter les lignes vides et les commentaires). Lisez tout en un après-midi.
  • Apache-2.0.
  • La documentation est de premier ordre. LIMITS.md (ce que la chaîne ne peut pas prouver), PRIVACY.md (ce que contiennent les enregistrements et ce qui quitte votre machine), RETENTION.md (fonctionnement sous une politique de rétention), et REVIEWERS.md — la vérification indépendante en quatre commandes, plus un format de citation pour les conclusions de revue.

Ce que chaque couche prouve — la distinction porteuse dans ce projet (LIMITS.md §1) : une chaîne que vous détenez vous-même prouve que les enregistrements n'ont pas été modifiés, par rapport à une tête que quelqu'un détient déjà ; seuls des points de contrôle détenus en dehors de l'opérateur prouvent qu'aucun n'a été supprimé ; et aucun hachage ne prouve que chaque action a été capturée.

AffirmationChaîne auto-détenue+ Points de contrôle externes+ Capture de confiance
Détecter des modifications d'un artefact établi
Détecter la réécriture d'un historique validé
Détecter des points de contrôle manquants/tardifs✔ (cadence convenue)
Prouver que chaque action a été enregistréedépend de la frontière de capture

Voyez-en un avant d'installer : un exemple de Runtime Report — données fictives, chaîne réelle, et il se re-vérifie lui-même dans votre navigateur pendant que vous regardez.

Démo en 60 secondes

Aucun agent requis. Avec uv, rien à installer :``` uvx --from halo-record halo demo --serve

ou de la manière classique :```
pip install halo-record
halo demo --serve

Soit on échafaude un faux fournisseur d'agent de support avec deux clients, on assiste aux chaînes (avec un fichier témoin local tenant lieu de témoin extérieur à l'opérateur — voir LIMITS.md §1), on sert leurs Runtime Reports soumis à condition, et on ouvre la console de l'opérateur 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.

Enregistrez votre propre agent

Une ligne à la frontière :```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

Un shim de commodité `from halo import ...` est également fourni — mais le nom `halo` sur PyPI appartient à un paquet de terminal-spinner sans rapport, et si ce paquet est installé, c'est lui qui remporte l'import. `halo_record` est sans ambiguïté, donc les exemples l'utilisent.

Sans `log=`, les enregistrements vont dans `~/.halo/my-agent.jsonl` (une chaîne par agent). Le wrapper scelle la frontière d'exécution ; les preuves résident dans les enregistrements par appel. Capturez-les avec un adaptateur de framework (matrice ci-dessous) — ou explicitement, ce qui montre aussi comment les liens de délégation fonctionnent :```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)

Ensuite, 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 quickstart se termine lorsque vous consultez le Runtime Report de votre propre agent dans un navigateur. Si vous avez obtenu un fichier JSONL et aucun rapport, quelque chose ne va pas : ouvrez un ticket.

### Le bloc de vérification

Si une couche de garde-fou ou de politique a vérifié l'action, son verdict peut figurer sur l'enregistrement — un bloc optionnel consignant ce que le portail a décidé, scellé dans la chaîne de hachage comme tout autre champ :```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"})

qui scelle dans l'enregistrement comme :```json "verification": {"status": "allowed", "verifier": "gate/1.2", "policy_ref": "sha256:1f3a...", "checked_at": "2026-08-01T12:00:00Z"}

`record_call(...)` accepte le même mot-clé `verification=`. `status` est requis dans le bloc ; `verifier`, `policy_ref` et `checked_at` sont facultatifs. Voici ce que signifie chaque statut :

| Statut | Ce que le gate rapporte | L'action a-t-elle été exécutée ? |
|---|---|---|
| `allowed` | il a autorisé l'action | oui — l'action a été exécutée |
| `blocked` | il a refusé l'action | déterminé par l'intégration, pas par ce champ — un enregistrement peut toujours porter un résultat, et un blocage ne prouve pas à lui seul la non-exécution |
| `modified` | il a modifié l'action avant exécution — `action.input` décrit l'action **telle qu'exécutée**, après modification | oui, sous la forme modifiée |
| `unverified` | il s'est exécuté (ou a été consulté) mais n'a pris aucune décision — distinct d'un bloc absent, qui signifie qu'aucune revendication de vérification n'a été faite du tout | oui — l'action a été exécutée sans verdict |

Le bloc est fourni par le code d'intégration de l'opérateur et enregistre ce qu'il rapporte que le gate a dit — la même posture de confiance que `principal` (voir [LIMITS](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md#11-verification-status-is-the-gates-report-not-halos-finding)). Le scellement prouve que le statut n'a pas été modifié après coup ; il ne prouve pas que la vérification a eu lieu, que le verdict était correct, ni qu'une action bloquée n'a pas été exécutée. Il ne s'agit pas d'une vérification indépendante.

Pour que `policy_ref` soit utilisable comme preuve, utilisez un hash de contenu du jeu de règles et conservez l'artefact du jeu de règles — une étiquette non résolvable rend le champ décoratif.

## Connectez-vous à ce que vous exécutez déjà

| Capturé à la frontière | Ingéré depuis la télémétrie existante |
|---|---|
| Enregistreur natif (`from halo_record import trace`) | Spans OpenTelemetry GenAI |
| Intercepteur MCP | Callbacks LiteLLM |
| Callback LangChain / LangGraph | Export Langfuse |
| Hooks OpenAI Agents SDK | Tout journal de gateway / reverse-proxy |
| Hook Claude Agent SDK | Hooks `PostToolUse` de Claude Code et Codex CLI (déclenchés après l'exécution de l'outil) |

Les adaptateurs de framework et les chemins d'ingestion marquent chaque enregistrement avec un tag `source`, de sorte 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.

Pour LangChain / LangGraph, il s'agit d'un gestionnaire 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

Pour MCP, un appel encapsule la session client — et ensuite tout agent utilisant MCP émet des enregistrements pour chaque appel d'outil, quel que soit le framework qui le pilote :```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

Pour les journaux de passerelle ou de proxy (Cloudflare AI Gateway, Portkey, nginx devant le modèle), mappez une ligne de journal dans la chaîne — explicitement étiquetée comme ingérée, et non capturée à la frontière :```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"})

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 package TypeScript fournit des adaptateurs natifs pour le Vercel AI SDK et l'écosystème d'agents JS. Il manque un adaptateur pour votre stack ? Ouvrez une issue. La plupart des adaptateurs font environ une centaine de lignes.

Enregistrez votre agent de codage (Claude Code ou Codex)

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 :```json { "hooks": { "PostToolUse": [ {"matcher": "*", "hooks": [{"type": "command", "command": "halo hook"}]} ] } }

Ajoutez cela à `~/.claude/settings.json` et les enregistrements arrivent dans `~/.halo/audit.jsonl` (remplacez avec `$HALO_LOG`). Les outils de pure orchestration qui ne touchent à aucune donnée, réseau ou état externe sont ignorés — la chaîne enregistre les actions de 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 version de l'agent qui l'a produit — lorsqu'un auditeur s'interroge sur la version qui était en cours d'exécution dans une fenêtre donnée, l'export répond par colonne plutôt que par souvenir.

Codex CLI fournit les mêmes hooks de cycle de vie avec la même forme d'événement (les hooks sont activés par défaut). Ajoutez ceci à `~/.codex/hooks.json` et les commandes shell de Codex, les modifications `apply_patch` et les appels MCP arrivent dans la même chaîne :```json
{
  "hooks": {
    "PostToolUse": [
      {"matcher": ".*", "hooks": [{"type": "command", "command": "halo hook"}]}
    ]
  }
}

Le hook distingue les deux à partir de l'événement lui-même (Codex ajoute turn_id et model) et étiquette chaque enregistrement claude-code ou codex ; définissez HALO_HOOK_AGENT pour en forcer un. Les deux relèvent du niveau ingéré : un hook PostToolUse se déclenche après l'exécution de l'outil, donc l'enregistrement est construit à partir de ce que le harness rapporte.

Si vous avez besoin que le rapport réponde à « sous quelles règles cette exécution a-t-elle eu lieu ? », définissez HALO_AUTHORITY_FILE sur un instantané JSON de l'autorité effective pour la session. Gardez-le respectueux de la vie privée : des hachages et des références, pas d'invites brutes, de texte de politique privée, de secrets ou de schémas d'outils complets — les formats de secrets connus sont masqués au moment du scellement, mais les hachages et les références passent tels quels et le texte libre n'est pas détecté (voir LIMITES §6). Ne réutilisez un snapshot_id que tant que l'autorité sous-jacente reste inchangée ; les enregistrements consécutifs ayant le même id et un contenu inchangé sont compactés — un id réutilisé sur un contenu modifié est stocké intégralement, avec un avertissement.```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"] }

| `-s` | `--server` | `false` | Démarrer le serveur MCP |
| `-p` | `--port` | `3000` | Port du serveur MCP |
| `-h` | `--help` | | Afficher l'aide |
| `-v` | `--version` | | Afficher la version |

### Exemples

```bash
# Analyser un fichier local
mcp-scan /path/to/config.json

# Analyser un serveur distant
mcp-scan https://example.com/mcp

# Analyser avec un fichier de configuration
mcp-scan --config /path/to/config.json

# Démarrer le serveur MCP
mcp-scan --server --port 3000

Développement

Configuration

# Cloner le dépôt
git clone https://github.com/example/mcp-scan.git
cd mcp-scan

# Installer les dépendances
npm install

# Compiler le projet
npm run build

# Exécuter les tests
npm test

Structure du projet

mcp-scan/
├── src/
│   ├── index.ts          # Point d'entrée principal
│   ├── scanner.ts        # Logique d'analyse
│   ├── server.ts         # Serveur MCP
│   └── types.ts          # Définitions de types
├── tests/
│   └── scanner.test.ts   # Tests unitaires
├── package.json
└── README.md

Contribution

Les contributions sont les bienvenues ! Veuillez consulter les directives de contribution avant de soumettre une pull request.

  1. Forker le dépôt
  2. Créer une branche de fonctionnalité (git checkout -b feature/amazing-feature)
  3. Valider vos modifications (git commit -m 'Add some amazing feature')
  4. Pousser vers la branche (git push origin feature/amazing-feature)
  5. Ouvrir une Pull Request

Licence

Ce projet est sous licence MIT - voir le fichier LICENSE pour plus de détails.

Remerciements

  • Merci à tous les contributeurs qui ont aidé à façonner cet outil
  • Inspiré par les besoins de sécurité croissants dans l'écosystème MCP
  • Construit avec Model Context Protocol

Support


Avertissement : Cet outil est destiné à des fins de test de sécurité et d'éducation uniquement. Utilisez-le de manière responsable et uniquement sur des systèmes que vous possédez ou pour lesquels vous disposez d'une autorisation explicite.```sh HALO_AUTHORITY_FILE=./authority.json halo hook

Le snapshot est scellé dans la même chaîne de hachage que les enregistrements d'actions. Une bonne valeur par défaut est un snapshot au niveau de la session au démarrage, plus un nouveau snapshot lorsque les règles, les Skills, les hooks, les registres d'outils MCP ou la politique de compactage changent. Pour garder les sessions longues légères, les enregistrements consécutifs ayant le même `authority.snapshot_id` sont compactés après le premier snapshot complet : les enregistrements ultérieurs ne conservent que `{"snapshot_id": "...", "same_as_previous": true}`. Le pointeur reste chaîné par hachage, mais le bloc volumineux refs/omissions/stale-if n'est pas répété à chaque action. (Le compactage est propre au processus d'enregistrement : la capture de style hook qui lance un processus par appel d'outil réenregistre le corps complet chaque fois que le snapshot complet précédent n'est pas l'enregistrement de queue, de sorte que les processus de courte durée échangent la taille de la chaîne contre la protection de réutilisation.)

Les utilisateurs du SDK attachent le même bloc directement — `build(..., authority={...})` ou `record_call(..., authority={...})` ; la capture par hachage seul utilise la même interface (`summaries=False` sur l'un ou l'autre) :```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")

Ensuite, comme d'habitude :``` halo verify ~/.halo/audit.jsonl halo report ~/.halo/audit.jsonl -o report.html

N'importe quel 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.

Une chaîne, un seul écrivain à la fois. Une chaîne est une liste chaînée : deux écrivains qui lisent la même tête et ajoutent tous les deux vont la bifurquer (deux enregistrements revendiquant le même prédécesseur), et la vérification nommera les enregistrements concernés. `Recorder` sérialise ses propres ajouts avec un verrou sidecar (POSIX `flock` ici ; un répertoire de verrou dans le paquet TypeScript), et `halo hook` ajoute via `Recorder`, donc la configuration du hook ci-dessus est couverte. Tout ce qui écrit directement dans le fichier de chaîne — un hook artisanal, des workers parallèles, un expéditeur de logs — doit détenir un verrou exclusif équivalent sur toute la séquence lecture-de-la-tête-puis-ajout, ou écrire dans des chaînes par processus. [LIMITS.md section 9](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md#9-single-writer-chains) couvre cela en détail, y compris la frontière inter-langages.

## Quand l'enregistrement échoue

Les deux styles d'intégration échouent dans des directions opposées, à dessein — choisissez celui dont l'échec vous est supportable :

- **Les adaptateurs de framework (LangChain, hooks via gestionnaires de callbacks) échouent en mode ouvert.** Si un enregistrement ne peut pas être écrit (disque plein, permissions), l'action de l'agent se termine normalement et l'enregistrement est perdu. Le gestionnaire LangChain imprime un avertissement bien visible sur stderr et compte la perte (`handler.lost_records`), mais rien dans la chaîne elle-même ne peut montrer un enregistrement qui n'a jamais été écrit — une chaîne bloquée se vérifie quand même. Les points de contrôle témoins à cadence régulière sont ce qui rend une chaîne bloquée visible : un point de contrôle attendu qui n'arrive jamais est l'alarme.
- **Le wrapper natif `trace()` échoue en mode fermé.** Si l'enregistrement ne peut pas être écrit, l'exception se propage dans l'action de l'agent — pas de preuve, pas d'action. Plus strict, et cela peut interrompre votre agent.

Aucun des deux comportements par défaut ne convient à tout le monde ; sachez lequel vous exécutez.

## Intégrité vs. complétude (lisez cette partie)

Soyez précis sur ce que chaque couche prouve — car ce sont des affirmations différentes, et les différences sont le point essentiel :

Une chaîne auto-détenue 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éordonnancement ou suppression dans les enregistrements derrière elle devient détectable. En soi — avant que quiconque en dehors de l'opérateur n'ait vu une tête — une chaîne prouve la cohérence interne, pas l'historique : un opérateur pourrait supprimer un enregistrement et re-scellé, et le nouveau fichier se vérifierait. La chaîne devient **historiquement engagée** au moment où sa tête quitte le contrôle de l'opérateur.

C'est cela, le témoin : une partie extérieure à l'opérateur détenant des points de contrôle périodiques de la chaîne — l'identifiant du sujet, un nombre d'enregistrements, et deux empreintes de chaîne (la tête et la racine de chaîne) ; la charge utile exacte, et 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

Pour time spécifiquement, un horodatage RFC 3161 externe remplace l'horloge auto-déclarée du point de contrôle par une preuve provenant d'une Autorité d'Horodatage que l'opérateur ne contrôle pas — « cette chaîne a atteint cette tête au plus tard à T », vérifiable par un tiers sans infrastructure hébergée. L'ATS par défaut est le gratuit freetsa.org (suffisant pour l'évaluation) ; pointez vers une ATS commerciale (DigiCert / Sectigo / la vôtre) avec --tsa pour la production :``` 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` confirme que le jeton lie cet état de chaîne et lit son horodatage attesté, mais il ne valide **pas** la signature du TSA — cela est délibérément laissé à un outil standard afin qu'un relecteur ne fasse confiance à aucun de nos codes. Pour vérifier l'horodatage de manière indépendante (c'est ce que vous remettez à un relecteur de sécurité) :```
# 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 <the digest printed above> -in token.tsr -CAfile tsa-ca.pem   # → "Verification: OK"

Une limite de plus, énoncée clairement : ni la chaîne ni le témoin ne prouvent que chaque action du monde réel est passée par l'enregistreur. C'est la complétude de capture — une propriété de l'endroit où l'enregistreur se situe dans la pile (instrumentation native, hooks, ingestion par passerelle), et non d'un quelconque hash. Les enregistrements portent une balise source précisément pour cette raison. Le tableau des affirmations sous « Vérifiez par vous-même » en haut de cette page est le résumé de ces trois couches.

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 la manière dont ce projet subviendra à ses besoins. Accès anticipé : [email protected].

Données personnelles dans la chaîne

La chaîne est en ajout seul : tout ce qui est scellé dans un enregistrement y reste, car le retirer briserait la vérification de tout ce qui suit. Les arguments des outils sont déjà traités — stockés sous forme de hash plus un résumé expurgé limité à 200 caractères (le mode hash-only ne conserve aucun résumé).

Notez la limite dans cette phrase : expurgé, pas supprimé. LIMITS.md section 6 précise explicitement qu'un nom ou une adresse postale n'a pas de motif fiable, donc ni l'un ni l'autre n'est détecté et ni l'un ni l'autre n'est masqué. Et subject n'est pas le seul champ qui contient du texte que vous fournissez — principal, approver, session_id, agent, authority, data et les résumés le font tous.

Le motif qui fonctionne : placez un identifiant pseudonyme stable dans la chaîne et conservez la correspondance vers tout individu dans un système que vous pouvez supprimer. Une demande d'effacement est alors satisfaite en supprimant la correspondance. Gardez subject pointant vers l'organisation locataire, pas vers une personne :```python from halo_record import build

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

Aucun paramètre n'impose cela — c'est une discipline dans la façon dont vous appelez l'enregistreur.
Cela rend l'effacement traitable ; ce n'est pas de l'anonymisation, et il n'existe encore
aucune rétention ni élagage intégré.
[LIMITS.md section 13](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md#13-personal-data-and-erasure) contient la liste complète des champs,
explique pourquoi l'empreinte de l'entrée stockée peut confirmer une valeur devinable même
après la disparition de la correspondance, et se termine par les questions qu'un relecteur devrait poser.

Enregistrer un appel de modèle (la première question de l'acheteur : « quel modèle a vu mes données ? ») :```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

Où cela se situe dans une pile de conformité

halo-record est une couche de preuves, pas une certification. Il produit l'artefact que les référentiels d'évaluation ne cessent de réclamer en employant des mots différents. Une note de périmètre qui régit chaque point ci-dessous : ce sont des affirmations d'intégrité concernant l'enregistrement ; l'exhaustivité par rapport à l'opérateur nécessite un témoin externe détenant des points de contrôle (LIMITS.md §1).

  • Questionnaires de sécurité et revues SOC 2 : répondez aux sections IA avec un Runtime Report vérifiable plutôt qu'avec des captures d'écran et de la prose.
  • AIUC-1 : produit les preuves de journalisation à altération détectable (E015.4) et les enregistrements de chaîne d'exécution avec événements d'autorisation (E015.2 — avec une lacune déclarée : les traces de raisonnement ne sont pas capturées) que le contrôle Accountability E015 de la norme nomme. E015 lui-même est obligatoire ; E015.2 et E015.4 constituent son niveau supplémentaire : non requis pour réussir, celui qu'un fournisseur adopte lorsqu'un client ou un régulateur le demande. Une fois la chaîne ancrée à un témoin auquel la partie s'appuyant a des raisons de faire confiance — un témoin que l'opérateur exécute lui-même ne fournit pas cela — il s'agit d'une chaîne continuellement attestée plutôt que reconstruite au moment de l'audit (ce qui entre dans la chaîne reste borné par la surface de capture). Une correspondance des preuves contrôle par contrôle, y compris ce qui est délibérément hors périmètre, se trouve dans AIUC.md.
  • OWASP Top 10 for Agentic Applications 2026 : huit des dix menaces correspondent à des règles de politique déterministes sur l'enregistrement, deux sont marquées hors périmètre avec justifications, et le pack est livré exécutable. Une correspondance communautaire approximative, non un artefact officiel de l'OWASP. Voir OWASP.md.
  • AARM (CSA) : produit le reçu d'action à altération détectable que spécifie AARM — R5, et la moitié scellement de R6 (l'identité est scellée dans le hachage, non authentifiée cryptographiquement). halo-record est la couche de reçus ; associez-le à une passerelle d'application pour un système AARM complet. Voir AARM.md.
  • Agentic Trust Controls : les enregistrements d'exécution derrière les contrôles de preuve de l'ATC — journalisation d'actions à altération détectable (RBM-03) et la moitié enregistrement de l'attestation d'autorité (AID-05 ; la moitié application appartient à la passerelle) dans un seul enregistrement chaîné. Voir ATC.md.
  • CSA AI Controls Matrix (AICM) / STAR for AI : les preuves du domaine LOG — enregistrements d'audit générés, scellés contre toute modification non détectée, événements d'entrée et de sortie journalisés — mappées contrôle par contrôle dans AICM.md. Le propre crosswalk v1.1 de la CSA relie ce domaine à AIUC-1 E015.
  • MITRE ATLAS : l'atténuation de télémétrie d'agent (AML.M0024) mise en œuvre avec une propriété d'intégrité qu'ATLAS lui-même ne demande pas — le journal est vérifiable par quelqu'un d'extérieur à l'opérateur. Voir ATLAS.md.
  • EU AI Act / ISO 42001 / NIST AI RMF : les obligations de tenue d'enregistrements et de journalisation que ces référentiels décrivent relèvent de la même classe d'artefacts — mappées de manière conservatrice dans EU-AI-ACT.md, ISO42001.md et NIST-AI-RMF.md.

Rien de tout cela ne certifie quoi que ce soit en soi. Cela donne à votre évaluateur quelque chose de vérifiable à examiner. Les limites — ce que halo-record ne fait délibérément pas, et quoi dire lorsqu'un réviseur pose la question — sont documentées dans LIMITS.md.

Faire entrer les preuves dans votre plateforme GRC

La plupart des plateformes GRC (Vanta, Drata et similaires) acceptent les fichiers téléversés comme preuves personnalisées au titre d'un contrôle. L'export de halo-record est conçu pour s'intégrer à ce flux :```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

Cela écrit deux fichiers pour la fenêtre d'audit : le CSV (une ligne par action enregistrée, groupées de gauche à droite selon *quand → ce qui s'est passé → qui → sous quelle autorité → ce qui a été signalé → provenance → comment vérifier*, incluant un résumé en langage clair et expurgé de l'appel et de son résultat, la version de l'agent et le modèle qui a produit chacun, l'identité pour le compte de laquelle il a été exécuté, l'enregistrement qui l'a causé, sa décision d'autorisation et sa portée, ainsi que toute catégorie de données personnelles ou indicateur de menace ingéré) et un manifeste (`evidence.csv.manifest.json`) qui lie le CSV à sa source — le hash de tête de la chaîne le relie au journal vérifiable dont il provient, et `csv_sha256` est le hash du fichier exporté lui-même, de sorte qu'un CSV modifié après export ne correspond plus à son manifeste. Restreignez la population avec `--tool` lorsqu'un contrôle ne couvre que certaines actions ; le manifeste enregistre le filtre, de sorte qu'un export restreint indique qu'il s'agit d'un sous-ensemble plutôt que d'être lu comme la population entière. Téléversez les deux au titre de votre contrôle de journalisation ou de surveillance ; joignez le HTML du Runtime Report lorsqu'un réviseur souhaite vérifier la chaîne lui-même. L'export refuse de s'exécuter sur une chaîne qui échoue à la vérification.

Une intégration push native — les preuves arrivant automatiquement dans votre plateforme — est prévue au programme. Le chemin de fichier ci-dessus fonctionne dès aujourd'hui avec toute plateforme qui accepte des preuves téléversées.

## 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

Modèle d'intégrité

Pour calculer le hash d'un enregistrement : prenez l'enregistrement en excluant integrity.hash, avec integrity.prev_hash défini sur le hash de l'enregistrement précédent ; canonicalisez avec RFC 8785 (JSON Canonicalization Scheme) ; appliquez SHA-256 sur les octets. Le prev_hash du premier enregistrement est composé de 64 zéros. La vérification recalcule chaque hash et contrôle chaque lien. Aucun secret n'est requis ; c'est tout l'intérêt.

Vous pensez pouvoir falsifier une chaîne sans que le vérificateur ne s'en aperçoive ? Les tentatives et résultats sont ici.

Référence complète des champs : halo-record.schema.json.

TypeScript

Le même enregistreur est disponible pour Node : halo-record-ts. Même format de chaîne, même protocole de témoin. Les enregistrements écrits dans l'un ou l'autre langage se vérifient avec l'un ou l'autre vérificateur.

Exemples de la communauté

trail-halo-poc — preuve de concept communautaire liant l'autorité principale d'un enregistrement Halo aux identifiants TRAIL : une liaison réciproque organisation–agent et des autorisations de périmètre signées par l'organisation enregistrées dans une chaîne Halo, avec une suite de vérification adversariale.

Contribution

Issues, discussions et pull requests sont les bienvenus — voir CONTRIBUTING.md pour les règles de base (version courte : tests requis, petites PR, les modifications de schéma sont discutées au préalable).

Licence

Apache-2.0

Catégories