# Preuves d’autorisation : prouver que les actions d’un agent IA sont terminées

Un enregistrement d’autorisation vous indique qu’une personne a permis à un agent d’essayer quelque chose. Il ne dit pas si la requête a atteint le service, si l’hôte distant a accepté la commande ou si la modification prévue a réellement eu lieu. Considérer l’autorisation comme une preuve d’exécution produit des pistes d’audit rassurantes jusqu’au moment où l’examinateur d’un incident demande : « Que s’est-il réellement passé ? »

Cette distinction devient urgente avec les agents IA, car ils peuvent effectuer de nombreuses actions réelles au cours d’une tâche de développement ordinaire. Une personne peut autoriser une fois un processus d’agent, puis celui-ci créer un ticket, modifier un paramètre de déploiement, interroger une API de production ou exécuter une commande distante. Chaque action exige des preuves dont le sens diffère. La décision humaine et le résultat obtenu doivent apparaître dans la même investigation, mais ils ne sont pas interchangeables.

## Une autorisation consigne une permission, pas une exécution

Les preuves d’autorisation indiquent si un décideur reconnu a permis une action définie à un moment donné. Les preuves d’exécution indiquent ce que la passerelle d’action a tenté et ce que la cible a renvoyé. Une conception d’audit qui ne conserve que la première information laisse une faille importante.

Prenons le cas d’un agent autorisé à appeler une API qui crée un jeton d’accès. La passerelle envoie la requête, mais la connexion est interrompue après le traitement par le service et avant que l’appelant ne reçoive une réponse. L’autorisation reste valide. Un journal indiquant « autorisé » ne permet pas de savoir si le jeton existe. Un journal affirmant « terminé » parce que la passerelle a envoyé des octets est encore plus problématique : il transforme une incertitude en faux fait.

La même erreur apparaît avec SSH. Une personne autorise `deploy.sh`. Le client SSH s’authentifie, le shell distant démarre, puis la connexion réseau disparaît pendant l’exécution du script. Le script s’est-il terminé ? A-t-il modifié partiellement un système ? L’enregistrement de la connexion ne peut pas répondre. Il faut le code de sortie distant, si le client l’a reçu, et un enregistrement clair d’issue inconnue dans le cas contraire.

Gardez ces questions séparées :

- Un humain ou un contrôle approuvé a-t-il autorisé ce processus d’agent ?
- Quelle capacité exacte l’autorisation couvrait-elle ?
- La passerelle a-t-elle envoyé l’action à la destination ?
- Quel résultat la destination ou le transport a-t-il renvoyé ?
- Les examinateurs pourront-ils vérifier ultérieurement que personne n’a modifié l’historique ?

De nombreuses équipes regroupent ces cinq questions dans un seul événement nommé `agent_action`. C’est pratique pour un tableau de bord et inutile lorsque les faits divergent. Stockez des événements liés, puis affichez-les ensemble dans l’interface.

La publication spéciale NIST 800-53, révision 5, formule une exigence pratique similaire dans le contrôle AU-3. Le contenu des enregistrements d’audit doit notamment préciser ce qui s’est passé, quand et où, sa source, son résultat et l’identité associée à l’événement. L’intérêt ne réside pas dans le vocabulaire de la liste de contrôle, mais dans l’idée que le résultat doit figurer dans l’enregistrement. Une décision d’autorisation ne peut pas remplir ce champ.

## Les deux enregistrements ont besoin de champs différents

Un modèle d’audit clair utilise un événement d’autorisation et un ou plusieurs événements d’exécution pour une même action demandée. Ils partagent un identifiant de corrélation, mais chacun conserve les champs nécessaires à sa propre affirmation.

L’enregistrement d’autorisation doit identifier la requête avant le début de l’exécution. Capturez l’identité du processus appelant, la décision humaine, la référence de l’identifiant, le canal prévu, la destination et l’opération demandée. Décrivez la portée de l’autorisation avec assez de précision pour qu’un examinateur puisse vérifier que l’appel ultérieur la respecte.

Une structure utile ressemble à ceci :

```json
{
  "event_type": "authorization.granted",
  "action_id": "act_01JX7K8N4Q",
  "session_id": "ses_01JX7JYQ2M",
  "time": "2025-03-08T14:21:18Z",
  "agent_process": {
    "pid": 4812,
    "code_signing_authority": "Example Development Team"
  },
  "human_decision": {
    "method": "local_confirmation",
    "actor": "local_user"
  },
  "requested_action": {
    "channel": "http",
    "credential_ref": "billing-api-prod",
    "method": "POST",
    "destination": "api.internal.example",
    "path": "/v1/refunds"
  }
}
```

Par défaut, cet enregistrement ne doit pas contenir le secret API, la clé privée SSH, l’en-tête d’autorisation ni le corps brut de la requête. Un magasin d’audit rempli d’identifiants devient une seconde voie d’attaque. Si les examinateurs doivent distinguer les requêtes contenant des données privées, conservez un condensat cryptographique de la partie protégée ainsi qu’un bref résumé expurgé, selon des règles documentées.

L’enregistrement d’exécution commence lorsque la passerelle tente l’action. Il doit préciser si l’action a atteint une frontière de protocole et ce qui a été renvoyé. Pour HTTP, capturez la méthode, la destination, le statut de réponse, l’erreur de transport, la taille de la réponse et une classification limitée de celle-ci. Pour SSH, capturez le compte, l’identité de l’hôte, la représentation de la commande, le code de sortie, le signal éventuel et le résultat de connexion côté client.

```json
{
  "event_type": "execution.finished",
  "action_id": "act_01JX7K8N4Q",
  "time": "2025-03-08T14:21:20Z",
  "channel": "http",
  "attempt": 1,
  "delivery": "response_received",
  "result": {
    "http_status": 201,
    "response_bytes": 428,
    "response_digest": "sha256:..."
  }
}
```

N’écrivez pas `success: true` comme seul champ de résultat. La notion de succès varie selon les protocoles et les produits. HTTP 201 signifie que le serveur indique qu’une ressource a été créée. HTTP 202 signifie qu’il a accepté une tâche qui sera traitée plus tard. Le code de sortie SSH 0 signifie que la commande distante a signalé sa réussite, mais un script peut avoir ignoré une erreur interne. Un résultat typé donne aux examinateurs des faits plutôt qu’une vague étiquette verte.

## La frontière d’action fiable doit créer la corrélation

Le composant qui détient les identifiants et exécute l’action doit générer l’identifiant d’action. L’agent peut demander un travail, mais il ne doit pas être la source de référence pour savoir ce qu’il était autorisé à faire ou ce que la passerelle a envoyé.

Cela compte lorsqu’un processus d’agent est défectueux, compromis ou simplement désorienté par une longue conversation avec des outils. S’il crée ses propres identifiants, il peut rattacher un résultat ultérieur à une requête antérieure, omettre des appels gênants ou annoncer un statut inventé. Même un agent bien intentionné peut perdre son contexte après un redémarrage. La frontière d’action voit au même endroit la requête, la sélection de l’identifiant, la tentative sortante et le résultat renvoyé.

Utilisez un identifiant sans signification métier et ne le réutilisez jamais. Un identifiant aléatoire ou unique et triable par date convient. Ajoutez-le à chaque enregistrement local. Lorsque le protocole le permet, transmettez-le aussi vers l’extérieur comme identifiant de requête. Les opérateurs du service pourront ainsi relier leurs journaux à celui de la passerelle sans exposer d’identifiant secret.

Pour une requête HTTP, utilisez un en-tête dédié que le système cible accepte de journaliser, comme `X-Action-ID`. Ne confondez pas cet en-tête avec une clé d’idempotence. Un identifiant d’action facilite l’investigation. Une clé d’idempotence demande à un serveur compatible de reconnaître une mutation en double. Une même valeur peut parfois jouer les deux rôles, mais seulement après confirmation par le propriétaire du service de sa sémantique et de sa durée de conservation.

```text
Authorization: action_id=act_01JX7K8N4Q
Outbound request: POST /v1/refunds
Request header: X-Action-ID: act_01JX7K8N4Q
Response: 201 Created
Execution record: action_id=act_01JX7K8N4Q, delivery=response_received
```

Ne vous fiez pas uniquement aux horodatages pour corréler les enregistrements. Les horloges dérivent, les requêtes se chevauchent et les agents peuvent effectuer des appels identiques durant la même seconde. Les horodatages aident à reconstruire l’ordre. Ils ne prouvent pas la relation parent-enfant.

Un identifiant de session compte aussi, mais il répond à une question plus large : quelle exécution du processus d’agent a effectué cette requête ? Conservez les deux identifiants. La session indique quel processus a reçu une permission permanente. L’identifiant d’action indique quelle opération précise a obtenu quel résultat.

## Les codes de statut HTTP sont des preuves limitées

Une réponse HTTP fournit des preuves d’exécution plus solides qu’une autorisation locale, mais elle doit encore être interprétée. Journalisez le statut final et assez de contexte pour l’expliquer. Ne transformez pas automatiquement tout statut hors de la plage 200 en échec.

Un `200 OK` ou `201 Created` est une réponse explicite du serveur. Un `204 No Content` indique souvent une opération réussie sans corps de réponse. Un `202 Accepted` est différent : la cible a reçu la requête pour un traitement asynchrone, mais le travail final peut échouer plus tard. L’enregistrement d’exécution doit indiquer `accepted_for_async_processing`, puis relier une vérification d’état ou un rappel ultérieur à la même action lorsque le service prend en charge ce modèle.

Les redirections demandent de la prudence. Si une passerelle les suit, journalisez la destination initiale et la destination finale, en précisant si la règle d’injection des identifiants s’est appliquée à chaque étape. Transmettre un en-tête d’autorisation à un hôte inattendu est une fuite d’identifiant, pas une redirection ordinaire. En pratique, refusez les redirections entre hôtes, sauf si un opérateur a explicitement configuré cette relation.

Les erreurs client et serveur apportent elles aussi des informations utiles. Un `403` prouve que le service a refusé la requête. Un `409` peut indiquer qu’un état en double ou en conflit existe déjà. Un `429` signifie que la cible refuse le traitement pour le moment. Un `500` signifie que le serveur a signalé une erreur, pas qu’aucune modification n’a eu lieu. Le corps de la réponse peut clarifier le résultat, mais ne le stockez qu’après expurgation et limitation de taille. Les réponses d’erreur contiennent souvent davantage de détails opérationnels que les réponses positives.

Les échecs de transport doivent avoir leurs propres valeurs de résultat. Distinguez notamment `dns_failure`, `tls_validation_failure`, `connect_timeout`, `write_interrupted`, `response_timeout` et `connection_reset`. Ces résultats indiquent à l’examinateur où s’arrête la certitude.

La catégorie la plus dangereuse est celle d’une requête interrompue après le début de son envoi par la passerelle. Le service a peut-être agi. Marquez l’état `unknown_remote_outcome` ; ne le qualifiez pas d’échec simplement parce que le client n’a pas reçu de réponse. Si l’appel modifie un état, l’agent doit s’arrêter et utiliser une méthode de rapprochement sûre. Il peut s’agir d’interroger le service avec une clé d’idempotence, de lire une ressource au moyen d’un identifiant de requête ou de demander à une personne d’inspecter la cible.

## SSH a besoin de preuves du processus distant

Les journaux d’audit SSH s’arrêtent souvent à « connecté à l’hôte ». Cela prouve seulement qu’un client a établi une session SSH. Rien ne permet de connaître le résultat de la commande, et l’hôte exact qui a répondu peut même rester incertain si vous n’enregistrez pas la vérification de sa clé.

Pour chaque action SSH, enregistrez le nom d’hôte de destination, l’adresse résolue si elle est disponible, l’empreinte de la clé d’hôte vérifiée, le compte demandé, la référence de la méthode d’authentification, la commande normalisée et le résultat final côté client. Si la commande distante démarre, capturez son code de sortie et le signal d’arrêt éventuel. Considérez la sortie standard et la sortie d’erreur comme des données opérationnelles sensibles, pas comme du contenu à journaliser automatiquement.

Une commande normalisée conserve la valeur d’examen tout en limitant l’exposition. Par exemple, au lieu de conserver une commande contenant un argument secret, gardez l’exécutable, les options fixes, les positions des arguments sensibles et un condensat des valeurs protégées.

```text
requested_command: /usr/local/bin/rotate-service-token --project payments --token [redacted]
command_digest: sha256:...
remote_exit_status: 0
remote_signal: null
connection_result: clean_close
```

Le code de sortie 0 est une preuve fournie par la commande distante, pas la preuve que tous les effets métier ont eu lieu. Un script shell mal écrit peut lancer `curl`, ignorer son erreur et tout de même se terminer avec le code 0. Si vous contrôlez le script, faites-le échouer clairement et renvoyer une valeur non nulle lorsqu’une opération nécessaire échoue. Sinon, consignez précisément le résultat de la commande et n’affirmez rien de plus.

Les sessions SSH interactives méritent une attention particulière. Elles créent un écart considérable entre « permission accordée » et « commandes exécutées ». Une commande distante limitée produit de meilleures preuves qu’un shell interactif général accordé à un agent. Si une tâche nécessite plusieurs commandes, utilisez un script examiné avec des sorties explicites ou créez un enregistrement d’action pour chaque commande. C’est moins spectaculaire qu’un agent qui tape librement dans un terminal, mais bien plus facile à examiner.

La vérification de l’hôte doit rester activée. Un enregistrement indiquant qu’un agent a exécuté une commande sur `build-01` a peu de valeur si le client a accepté un hôte non vérifié pendant une attaque réseau ou après un remplacement accidentel de la machine. Conservez l’empreinte de clé d’hôte vérifiée dans l’événement d’exécution. Les examinateurs pourront alors distinguer l’étiquette de nom d’hôte de l’identité cryptographique.

## Un délai d’attente doit mener à l’incertitude, pas à une avalanche de nouvelles tentatives

L’échec qui piège le plus souvent les équipes commence par un appel API mutant et se termine par un délai d’attente. L’agent a reçu l’autorisation. La passerelle a ouvert une connexion et envoyé la requête. La cible a effectué la modification, mais la réponse a disparu, ou la cible n’a jamais reçu les derniers octets. Pour l’appelant, les deux situations se ressemblent.

Imaginez qu’un agent crée un ticket d’incident de production via une API. Il envoie :

```text
POST /v1/incidents
Idempotency-Key: inc_72f9c
X-Action-ID: act_01JX7K8N4Q
```

Le client attend, puis enregistre `response_timeout`. Un journal limité à l’autorisation indique que la personne a approuvé un ticket. Un journal d’action simpliste indique que la création a échoué. Aucune de ces affirmations n’est sûre.

L’enregistrement d’exécution correct indique que la passerelle a tenté la requête et n’a reçu aucune réponse. Il conserve l’identifiant d’action et la référence de la clé d’idempotence. L’agent interroge ensuite le service sur l’état associé à `inc_72f9c`, si le service propose cette recherche. Si le service renvoie un ticket existant, consignez un événement de rapprochement qui pointe vers l’action initiale. S’il ne renvoie aucun enregistrement et que son contrat d’idempotence permet une nouvelle tentative, réessayez une fois avec la même clé d’idempotence, et non avec une nouvelle clé.

La séquence contient trois faits distincts :

1. Une personne a approuvé la requête initiale.
2. La passerelle n’a pas pu confirmer le résultat initial.
3. Une lecture ultérieure ou une nouvelle tentative idempotente a établi l’état final.

N’effacez pas le délai d’attente après le rapprochement. Il explique pourquoi l’action ultérieure a eu lieu. Il montre aussi si l’équipe rencontre un problème de transport récurrent qu’un simple compteur de réussites masquerait.

SSH connaît un échec équivalent. Une commande distante peut continuer après la perte de la session du client local. Évitez les nouvelles tentatives automatiques, sauf si la commande est manifestement idempotente. Préférez un identifiant d’opération distant, un fichier d’état dont les permissions sont contrôlées ou une API cible qui indique l’état. Si rien de tout cela n’existe, consignez l’issue inconnue et exigez un examen humain. Les commandes de déploiement en double ont déjà causé assez de dégâts sans qu’un agent ne les répète à la vitesse d’une machine.

## L’autorisation par session et l’autorisation par appel répondent à des risques différents

L’autorisation par session établit qu’un processus d’agent donné peut utiliser des canaux d’action approuvés pendant son exécution. Elle réduit la fatigue liée aux autorisations lorsqu’une personne s’attend à une courte série d’opérations peu risquées. Elle ne transforme pas les appels suivants en décisions examinées individuellement.

L’autorisation par appel consigne une décision humaine pour chaque utilisation d’un identifiant sélectionné. Utilisez-la pour les opérations dont chaque destination, mutation ou commande distante mérite une nouvelle attention. L’enregistrement d’exécution reste indispensable. Une personne peut approuver un appel API destructeur alors que le service le refuse, ne le traite que partiellement ou dépasse le délai d’attente.

La différence devient nette lorsque les examinateurs demandent : « Qui a approuvé cette modification ? » Un enregistrement de session peut répondre : « Ce processus d’agent signé avait la permission d’appeler ce canal. » Un enregistrement par appel peut répondre : « Une personne a approuvé cette requête exacte à ce moment précis. » Aucun des deux ne répond à la question « Est-ce arrivé ? » Seules les preuves d’exécution obtenues peuvent y répondre.

Les demandes d’autorisation doivent montrer assez d’informations pour permettre une décision humaine réelle : identité du processus appelant, référence de l’identifiant, destination, méthode ou commande et indication d’une modification d’état. Une demande disant seulement « Autoriser l’accès de l’agent ? » enregistre un clic, mais presque aucune intention exploitable.

Ne cherchez pas à résoudre ce problème avec un langage de politiques gigantesque avant de produire des preuves cohérentes. Les équipes se tournent souvent vers les règles parce qu’elles veulent réduire le nombre de demandes d’autorisation. Les règles peuvent limiter les actions, mais elles ne remplacent pas un modèle d’audit qui distingue l’autorisation, la tentative, la réponse et l’issue inconnue. Commencez par rendre les faits lisibles. Décidez ensuite où l’automatisation est sûre.

## Les chaînes de hachage protègent l’historique, pas la vérité d’une affirmation

Un journal append-only chaîné par hachage rend les manipulations ultérieures détectables. Chaque événement inclut ou contribue à un condensat qui dépend des enregistrements précédents. Si quelqu’un modifie une ancienne autorisation, supprime une requête échouée ou réordonne une séquence, la vérification échoue, à moins de réécrire la chaîne depuis le point modifié et de remplacer la tête de chaîne fiable.

Cette propriété compte pour l’activité des agents, car les enregistrements les plus embarrassants sont souvent ceux que quelqu’un voudrait supprimer : une action refusée, un déploiement échoué, une requête envoyée au mauvais service ou une session qui a duré plus longtemps que prévu. Un journal que les administrateurs peuvent modifier discrètement ne résistera pas à un examen sérieux.

Mais le chaînage par hachage ne rend pas vraie une entrée fausse. Si un agent non fiable fournit « HTTP 201 » et que le journal chaîne fidèlement ce mensonge, la chaîne prouve seulement que le mensonge a persisté. Le collecteur doit observer l’événement à la frontière d’action. La passerelle doit créer le résultat d’exécution après réception de la réponse ou détection de l’échec de transport.

Le NIST SP 800-92, Guide to Computer Security Log Management, avertit que les journaux doivent être protégés pendant leur génération, leur transmission, leur stockage, leur analyse et leur suppression. La leçon pratique dépasse la centralisation de fichiers texte. Conservez la source brute de l’événement, protégez la séquence, contrôlez qui peut modifier les enregistrements et rendez la vérification possible sans donner un accès étendu aux secrets utilisés par les actions.

Sallyport projette les exécutions d’agents et les appels individuels dans un même journal d’audit chiffré, chaîné par hachage et insensible en écriture. Sa commande `sp audit verify` vérifie la chaîne hors ligne sur le texte chiffré, sans nécessiter la clé du coffre-fort. Cette conception sépare l’examinateur qui vérifie l’intégrité de l’historique du processus qui peut utiliser les identifiants de production.

Une commande de vérification doit produire un résultat sans ambiguïté et identifier la plage contrôlée. Par exemple :

```text
$ sp audit verify
verified: 1847 records
chain: valid
first_record: 2025-03-01T08:15:02Z
last_record: 2025-03-08T14:21:20Z
```

Une chaîne valide ne permet pas de conclure qu’une API externe a tenu sa promesse. Elle établit que le récit conservé par la passerelle sur l’autorisation et le résultat reçu n’a pas été modifié sans détection.

## Examinez les relations, pas les compteurs d’événements isolés

L’examen d’audit échoue lorsque les équipes comptent les autorisations, les appels réussis et les appels refusés dans des graphiques séparés sans jamais inspecter les relations entre eux. L’unité d’examen utile est une chronologie d’action : requête, décision d’autorisation, tentatives d’exécution, résultat et éventuel rapprochement.

Commencez par les enregistrements auxquels il manque un événement associé. Une autorisation sans événement d’exécution peut correspondre à une requête annulée, à un plantage de la passerelle ou à une erreur de journalisation. Un événement d’exécution sans autorisation préalable peut révéler un contournement. Une issue distante inconnue sans rapprochement est un travail opérationnel inachevé, pas un échec clôturé.

Utilisez quelques requêtes ou rapports qui imposent les questions suivantes :

- Quelles actions mutantes approuvées se sont terminées par une issue distante inconnue ?
- Quels appels ont reçu une réponse hors de la portée de l’autorisation ?
- Quelles commandes SSH se sont terminées sans code de sortie ?
- Quelles autorisations par session ont produit des actions après la fin prévue de la tâche ?
- Quels identifiants d’action apparaissent dans les enregistrements locaux, mais pas dans les journaux du service cible ?

La dernière requête doit être interprétée avec prudence. L’absence d’un identifiant de requête dans un journal cible peut signifier que la cible ne l’a pas conservé, que la fenêtre temporelle est incorrecte ou qu’une autre équipe possède les journaux. Considérez-la comme un signal d’investigation, pas comme une preuve de faute.

Le journal des sessions et le journal d’activité de Sallyport rendent cette séparation concrète : une vue montre l’exécution de l’agent et son état de révocation, tandis que l’autre affiche les appels individuels. Les examinateurs doivent passer de l’un à l’autre grâce aux identifiants de session et d’action, au lieu de considérer l’un ou l’autre comme l’ensemble du récit.

Conservez un vocabulaire explicite de la certitude. `authorized`, `attempted`, `response_received`, `remote_exit_received`, `denied`, `failed_before_send` et `unknown_remote_outcome` sont plus faciles à défendre qu’un indicateur unique de réussite. Lorsqu’une équipe utilise ces termes de manière cohérente, les cas difficiles cessent de disparaître dans des tableaux de bord aux indicateurs verts.

## Constituez les preuves avant d’élargir l’accès des agents

Vous n’avez pas besoin d’un plan de contrôle complexe pour commencer. Placez l’utilisation des identifiants derrière une frontière d’action fiable, créez l’identifiant d’action à cet endroit, enregistrez séparément la décision humaine et le résultat, puis conservez les issues inconnues sans les réécrire comme des échecs. Les personnes chargées des incidents disposeront ainsi d’une séquence qu’elles pourront comparer aux enregistrements des services et des hôtes.

Testez ensuite les enregistrements en situation d’échec, pas seulement dans un scénario nominal. Autorisez une requête et coupez la connexion après l’envoi. Exécutez une commande SSH qui renvoie un code non nul. Provoquez une erreur DNS. Révoquez une session d’agent et vérifiez que les appels suivants reçoivent des enregistrements de refus. Vérifiez qu’un examinateur peut expliquer chaque événement sans ouvrir un fichier source ni demander à l’agent ce qu’il voulait dire.

Un agent peut être autorisé à agir sans détenir d’identifiant. Il ne doit pas pouvoir redéfinir ce qui s’est passé. Conservez la décision, conservez le résultat d’exécution et laissez l’incertitude visible lorsque le réseau refuse de coopérer.
