Si deux actions d’agent sont identiques en tout point sauf leur identifiant, une approbation humaine accordée à l’une peut-elle autoriser l’autre ?
Pour Authority, la réponse dépend de la question posée. Pour les questions qui portent sur une action précise, la réponse est non : une approbation couvre exactement l’instance d’action qui l’a demandée. Une autre vue, qui répond à une question sur l’empreinte plutôt que sur une instance, continue volontairement à répondre oui. Cet article montre les deux, et pourquoi la différence compte.
Le scénario
Il s’agit d’un scénario de régression ajouté après l’audit dans le commit 46e1321. Il ne fait pas partie des quatre constats consignés par la revue adversariale antérieure. Un même historique canonique d’événements contient, dans cet ordre :
- Une délégation racine
D1, accordée par un humain, permet à un agent de créer des bons de commande. Les montants compris entre 100 EUR et 1 000 EUR exigent une approbation humaine. - L’agent demande l’action
A1: un bon de commande de 500 EUR, en invoquantD1. - L’agent demande l’action
A2: une action distincte avec le même demandeur, la même capacité, les mêmes paramètres, donc la même empreinte, et la même délégation invoquéeD1. Seul l’action_iddiffère. - Une approbation
P1est demandée pourA1. P1est accordée, pourA1uniquement.
seq 1 DELEGATION_CREATED D1 human → agent, approval band 100–1,000 EUR
seq 2 ACTION_REQUESTED A1 500 EUR via D1
seq 3 ACTION_REQUESTED A2 500 EUR via D1 (same fingerprint as A1)
seq 4 APPROVAL_REQUESTED P1 for A1
seq 5 APPROVAL_GRANTED P1 for A1Aucune exécution n’existe encore : P1 n’est donc pas consommée.
Ce que répond chaque vue
Les vues ci-dessous sont décrites dans Qu’est-ce que la délégation IA ?. Les deux premières lignes sont évaluées à la séquence 5. La dernière ligne compare deux historiques alternatifs : dans l’un, A1 est exécutée à la séquence 6 en citant P1 ; dans l’autre, A2 est exécutée à la séquence 6 en citant P1.
| Question posée | Vue | A1 | A2 | Raison |
|---|---|---|---|---|
| Une chaîne détenue par l’agent autoriserait-elle maintenant cette capacité avec ces paramètres ? | AVAILABLEauthorityAt, currentAuthority | AUTHORIZED, en citant P1 | AUTHORIZED, en citant P1 | La question porte sur une empreinte, pas sur une instance. Une approbation valide, non consommée et de même empreinte suffit selon la spécification. |
| La délégation nommée par cette action autoriserait-elle maintenant cette instance d’action ? | INVOKEDinvokedAuthorityNow | AUTHORIZED, en citant P1 | REQUIRES_APPROVAL, via D1 | P1 est liée à l’action_id de A1. La même empreinte, le même demandeur et la même délégation ne suffisent pas (I6). |
| La chaîne enregistrée par l’exécution valide-t-elle cette action à son point de décision ? | RECORDEDexecution.recordedValidation | AUTHORIZED, P1 consommée | REQUIRES_APPROVAL, P1 citée mais non consommée | Citer P1 dans la chaîne enregistrée ne la lie pas à A2 ; la consommation exige aussi le même action_id. |
Il serait donc inexact de résumer le résultat par « Authority refuse A2 ». Dans les vues INVOKED et RECORDED, A2 est REQUIRES_APPROVAL : elle a encore besoin de sa propre approbation. Dans la vue AVAILABLE, l’empreinte de A2 est AUTHORIZED, parce que cette vue pose une autre question.
Pourquoi la vue fondée sur l’empreinte répond encore oui
authorityAt est prospective. Elle ne lit jamais un ACTION_REQUESTED antérieur et n’a donc aucune instance à laquelle se lier : selon la spécification, une approbation valide et non consommée dont l’empreinte correspond à la capacité et aux paramètres demandés suffit à produire AUTHORIZED. La même question fondée sur l’empreinte est traitée par currentAuthority et execution.authorityAtDecision dans explainAction.
Cette réponse est utile pour une question, « existe-t-il une autorité disponible pour ce type d’action ? », et trompeuse si on la lit comme « cette action a été approuvée ». Depuis le correctif, la commande authority explain affiche une note chaque fois que la ligne fondée sur l’empreinte vaut AUTHORIZED alors que la ligne de la délégation invoquée ne le vaut pas, afin que la première ne soit jamais lue comme une preuve sur la seconde.
Ce qui a changé
L’exigence existait avant le correctif
L’invariant I6 de SPEC.md exigeait déjà cette liaison avant le correctif : un APPROVAL_GRANTED “covers exactly the action that requested it, identified by its action_id and by its exact action_fingerprint”. L’implémentation ne l’appliquait pas complètement. La PR #9 a comblé cet écart : avant elle, un octroi correspondant à l’empreinte, au demandeur et à la délégation invoquée pouvait autoriser une autre instance d’action d’empreinte identique dans les résolutions propres à une action.
Le correctif
Le commit 46e1321 transmet l’action_id interrogé à la recherche d’approbation utilisée par les trois résolutions qui concernent une action précise : la validation de la délégation invoquée, la validation de la chaîne enregistrée et la sélection d’octroi utilisée par l’émission de capacités. Un octroi lié à un autre action_id est ignoré. La résolution fondée sur l’empreinte ne reçoit volontairement aucun action_id.
Le scénario et sa documentation sont venus ensuite
Le scénario détaillé et son explication ont été ajoutés ou renforcés après le correctif : la section “I6 — action-instance approval binding” de SPEC.md n’existe pas dans la spécification avant le correctif (elle apparaît plus tard, au commit 95cabde), alors que l’exigence I6 elle-même existe. Les liens de cet article pointent vers le merge commit 8e37af7, qui porte la version actuelle et nettoyée du code, des tests et de la documentation.
Preuves
- Test de régression post-audit dans
tests/adversarial/independent-audit.test.ts:A1estAUTHORIZEDavecP1etA2estREQUIRES_APPROVALviaD1. Il a été ajouté après l’audit, dans46e1321, et il est identifié comme tel dans le fichier. - Test CLI dans
tests/cli/invoked-authority-rendering.test.ts: lorsqueA2est exécutée en citantP1, ses lignes invoquée et enregistrée valent toutes deuxREQUIRES_APPROVAL, la sortie porte la note sur la portée de l’empreinte et n’affirme jamais queP1a été consommée ; lorsqueA1est exécutée,P1est signalée comme consommée. - Un test de
tests/adversarial/unit-binding.test.ts, “A2/A3: a newaction_id, in EUR or USD, gets nothing from P1”. Il a été ajouté plus tard, avec la PR #10 sur la liaison à la devise, et non avec la PR #9.
Contrôle par mutation
Supprimer la comparaison de l’action_id dans la recherche d’approbation de src/engine/evaluateConstraints.ts fait échouer exactement les trois tests ci-dessus ; les 291 autres tests exécutés continuent de passer. Ce résultat a été reproduit dans une copie temporaire du dépôt à 8e37af7, sans modifier le dépôt lui-même.
Limites de ce résultat
- Authority est un projet en V0.
- Chaque événement est
ASSERTED_UNVERIFIED. La V0 ne fournit ni signature ni vérification d’identité : le résultat ne dit rien de l’émission réelle des événements par les principaux qu’ils nomment. - Le résultat porte sur les règles déterministes du moteur, évaluées sur l’historique d’événements qu’il reçoit.
- Nous ne connaissons aucun système en production dans lequel cet écart aurait été exploité.
- Aucune affirmation n’est faite concernant les performances ou les tests de charge.
- I6 exigeait déjà la liaison à l’instance ; la PR #9 a corrigé un écart d’implémentation, pas la spécification.
- La vue fondée sur l’empreinte conserve volontairement une autre sémantique, décrite ci-dessus.
Questions ouvertes
- Une vue disponible fondée sur l’empreinte est-elle utile à exposer, ou dangereuse parce qu’elle peut être lue comme l’approbation d’une action précise ?
- Quelles propriétés, en plus de l’instance d’action, une approbation devrait-elle lier ?
- Avez-vous rencontré un rejeu d’approbation similaire dans un système réel ?
- Existe-t-il une séquence d’événements dans laquelle
A2peut encore bénéficier de l’approbation deA1?
Les contre-exemples qui ne décrivent pas une faille exploitable non corrigée peuvent être discutés publiquement sur le dépôt. Tout élément exploitable doit être signalé en privé : consultez la page sécurité.