# Scopes API pour agents autonomes de programmation : contenir le risque

Un agent autonome de programmation doit recevoir un identifiant pour une tâche définie, pas un identifiant qui fait simplement disparaître les erreurs. La difficulté ne consiste pas à trouver un scope nommé `write`. Il faut prouver que le jeton peut accomplir une tâche tout en échouant lors d'actions voisines qui n'en font pas partie.

J'ai vu des équipes commencer avec un jeton personnel parce que l'agent devait « simplement démarrer ». Quelques semaines plus tard, ce jeton pouvait lire tous les dépôts auxquels le développeur avait déjà touché, modifier les paramètres de déploiement et envoyer des requêtes destructrices sans rapport avec la tâche initiale. Le risque ne venait pas de l'agent. Il venait d'une limite de permissions conçue avec négligence.

## Une tâche est plus précise qu'un rôle

Une tâche d'agent décrit un résultat et un ensemble limité de changements d'état. Un rôle décrit largement une personne ou un service. Si vous accordez l'accès à partir du rôle, vous accordez presque toujours plus que ce que la tâche exige.

Prenons une demande comme celle-ci : « Mettre à jour une dépendance dans le service de paiement, exécuter la suite de tests et ouvrir une pull request. » L'agent peut avoir besoin de lire un dépôt, de créer une branche, d'y pousser des commits et de créer une pull request. Il peut aussi avoir besoin d'un accès en lecture aux journaux de compilation si le service de test les expose par une API. En revanche, il n'a pas besoin d'administrer l'organisation, de modifier les règles des branches protégées, de renouveler les identifiants de déploiement ni de fusionner son propre travail.

Rédigez les contrats de tâche sous forme de verbes associés à des ressources nommées. N'écrivez pas « écriture sur le dépôt ». Décrivez ce que l'agent peut réellement faire :

- Lire le code source, les tickets et les pull requests existantes dans `payments-service`.
- Créer et mettre à jour les branches dont le nom commence par `agent/`.
- Créer une pull request de cette branche vers la branche de base désignée.
- Lire l'état et les journaux du workflow lancé par cette pull request.
- Publier un commentaire contenant le résultat des tests.

Ce n'est pas de la bureaucratie. Cette liste fait apparaître les décisions manquantes. L'agent peut-il fermer un ticket ? Peut-il modifier la pull request de quelqu'un d'autre ? Peut-il relancer un workflow coûteux ? Doit-il récupérer un paquet depuis un registre privé ? Chaque verbe justifie une permission ou est supprimé.

Le contrat de tâche distingue aussi un effet secondaire nécessaire d'un effet secondaire pratique. L'agent peut vouloir mettre à jour l'étiquette d'un ticket après avoir ouvert une pull request. C'est peut-être utile, mais cette action ne rend pas possible la mise à jour de la dépendance. Laissez-la de côté dans le premier jeu de permissions. Ajoutez-la ensuite uniquement si quelqu'un accepte cet effet et teste la limite.

Traitez les tâches récurrentes comme des tâches distinctes, même lorsqu'un seul processus d'agent les exécute. Une vérification nocturne des dépendances, une promotion de version et un retour arrière en production n'ont pas les mêmes conséquences. Une identité unique avec un ensemble de permissions accumulées rend ces trois opérations plus difficiles à examiner et impossibles à révoquer proprement.

## Les noms de scopes ne sont pas des limites de permission

Une chaîne de scope est une entrée de l'autorisation, pas la preuve qu'un appel API est sûr. Les fournisseurs utilisent le mot « scope » pour plusieurs mécanismes différents : chaînes OAuth, permissions de dépôt, rôles de projet, autorisations d'installation et jetons limités à une liste de ressources. Ces mécanismes ne sont pas interchangeables.

OAuth 2.0 RFC 6749 définit le scope comme un ensemble de chaînes séparées par des espaces qui limite l'accès d'un jeton. La norme laisse volontairement au serveur d'autorisation le soin de définir la signification de chaque chaîne. Cette souplesse est utile aux fournisseurs, mais elle signifie que `repo:write`, `projects.write` et `api` ne vous apprennent presque rien tant que vous n'avez pas examiné la documentation des endpoints du fournisseur et testé le jeton.

RFC 8707 ajoute les indicateurs de ressources. Un client peut demander un jeton pour une ressource protégée précise, au lieu de traiter tous les endpoints situés derrière un serveur d'autorisation comme une seule cible. Cela aide lorsque l'émetteur le prend en charge. En revanche, cela ne corrige pas un fournisseur qui associe un scope large unique à tous les projets ou à tous les endpoints destructeurs d'une ressource.

Gardez ces trois niveaux distincts dans vos notes de conception :

| Niveau | Question à laquelle il répond | Échec en cas de confusion |
|---|---|---|
| Scope du jeton | Quelles étiquettes de permission l'émetteur a-t-il placées dans ce jeton ? | Vous supposez qu'une étiquette claire correspond à une action précise. |
| Autorisation de ressource | À quels dépôts, projets, comptes ou environnements cette identité peut-elle accéder ? | Le jeton peut agir sur une ressource voisine. |
| Règle d'endpoint | Quelle méthode et quel chemin l'API acceptera-t-elle pour cette requête ? | Une permission d'écriture autorise la suppression ou l'administration. |

La mauvaise recommandation la plus courante est « utilisez read only plus write ». Elle est populaire parce qu'elle tient dans un guide de configuration et fonctionne souvent du premier coup. Elle est erronée parce que write couvre souvent plusieurs verbes sans rapport. Créer une pull request, supprimer un dépôt, modifier un webhook et changer le contrôle d'accès peuvent tous dépendre de la même autorisation large.

Lorsqu'un fournisseur ne propose qu'un scope large, ne prétendez pas avoir appliqué le moindre privilège en le nommant soigneusement. Limitez plutôt la couche des ressources. Créez un dépôt, un projet, un environnement ou un compte de service dédié, avec un accès limité à la cible. Si l'agent doit effectuer une seule action en production, donnez-lui une identité distincte pour cette action et exigez une approbation explicite. Le modèle grossier du fournisseur reste grossier, mais l'identifiant peut atteindre moins de ressources.

## Créez un registre des endpoints avant d'émettre un jeton

Un registre des endpoints transforme une demande vague en conception de permissions vérifiable. Il consigne chaque appel que l'agent est autorisé à effectuer, la raison de cet appel, la ressource qu'il peut toucher et la permission exacte qui l'active.

Commencez par la séquence d'actions, pas par la page des permissions du fournisseur. Un agent qui ouvre une pull request a souvent besoin de plus d'appels qu'on ne le pense : il lit la révision de base, crée une référence, crée ou met à jour des fichiers, récupère l'état du workflow et soumet une pull request. Une page de permissions indique rarement lequel de ces appels est essentiel pour le workflow choisi.

Utilisez un registre comme celui-ci. Remplacez les chemins indicatifs par ceux documentés par votre fournisseur.

```yaml
task: update dependency and open pull request
resource: org/payments-service
calls:
  - method: GET
    path: /repos/org/payments-service/contents/package-lock.json
    purpose: read current dependency lockfile
    permission: contents:read

  - method: POST
    path: /repos/org/payments-service/git/refs
    constraint: "ref starts with refs/heads/agent/"
    purpose: create working branch
    permission: contents:write

  - method: PUT
    path: /repos/org/payments-service/contents/package-lock.json
    constraint: "branch starts with agent/"
    purpose: commit updated lockfile
    permission: contents:write

  - method: POST
    path: /repos/org/payments-service/pulls
    constraint: "base is main; head starts with agent/"
    purpose: request review
    permission: pull_requests:write

forbidden_calls:
  - DELETE /repos/org/payments-service
  - PATCH /repos/org/payments-service/branches/main/protection
  - POST /repos/org/organization-hooks
  - GET /repos/org/another-service/contents/secrets.yml
```

Le champ `constraint` est important, car les permissions d'endpoint s'arrêtent souvent avant les permissions de tâche. Une API peut autoriser la création de branches sans proposer de restriction native au préfixe `agent/`. Notez cette lacune. Vous aurez peut-être besoin d'un service d'action intermédiaire, d'un dépôt séparé ou d'une étape de revue, car un scope ne peut pas appliquer la règle de branche souhaitée.

Ne comptez pas sur le prompt de l'agent pour maintenir ces contraintes. Un prompt peut décrire le préfixe de branche prévu, mais il ne peut pas refuser une requête envoyée vers `main`. Le point d'application doit être le fournisseur de l'API, les paramètres de la ressource cible ou une passerelle d'action qui vérifie la requête avant de l'envoyer.

Le registre doit traiter les appels de lecture avec autant de sérieux que les appels d'écriture. Lire un secret de déploiement, un export client, un avis de sécurité ou un second dépôt peut exposer davantage qu'un mauvais commit. La plupart des revues de permissions se concentrent sur les écritures parce qu'elles sont visibles. La fenêtre de contexte de l'agent rend aussi les lectures trop larges dangereuses.

## Séparez l'accès à la découverte de l'accès aux modifications

L'accès à la découverte et l'accès aux modifications devraient généralement utiliser des identifiants différents, car un agent a plus souvent besoin d'un contexte large que d'une autorité large pour changer l'état du système.

Un agent de planification peut devoir rechercher du code, examiner des tickets, consulter les résultats de compilation et comparer des versions dans plusieurs dépôts. Un agent de correction peut n'avoir besoin d'écrire que dans une branche d'un seul dépôt. Si les deux tâches partagent un jeton, l'agent de correction hérite de la large surface de lecture du planificateur, et le planificateur reçoit une capacité d'écriture dont il n'a jamais besoin.

Séparez le travail en étapes lorsque le fournisseur le permet. L'étape de découverte produit un plan limité ou une proposition de correctif. Un second processus reçoit cet artefact et un identifiant plus restreint pour effectuer la modification demandée. Une personne peut examiner le transfert lorsque la modification touche une zone protégée.

Cette séparation permet de détecter un problème concret que les prompts ne peuvent pas résoudre. Supposons qu'un planificateur recherche dans une organisation les références à un paquet et trouve un ancien dépôt interne contenant des notes de déploiement. Si le même jeton peut pousser dans chaque résultat consulté, un appel d'outil ultérieur mal choisi peut modifier le mauvais dépôt. Le modèle peut avoir parfaitement compris la tâche et sélectionner malgré tout le mauvais identifiant. Limiter l'écrivain au dépôt prévu transforme cette erreur en requête refusée.

Ne séparez pas les jetons uniquement pour en créer davantage. Séparez-les lorsque l'ensemble des ressources ou des verbes autorisés diffère. Un seul jeton de lecture peut suffire à une investigation cohérente. Un seul jeton d'écriture peut suffire à des modifications étroitement liées dans une cible unique. L'objectif est que la réponse à « que peut faire ce processus ? » soit assez courte pour qu'un ingénieur puisse la vérifier sans deviner.

Pour le contrôle de version, séparez l'autorité d'écriture sur une branche de l'autorité de fusion lorsque le fournisseur le permet. Une branche est une modification proposée. Une fusion change la base partagée et déclenche souvent des déploiements, des versions ou des automatisations en aval. L'agent peut ouvrir une pull request utile sans recevoir le droit de la fusionner.

## Limitez la ressource avant d'affiner le scope

Un scope précis attaché à un identifiant valable pour toute une organisation est souvent pire qu'un scope large attaché à une cible isolée et facilement remplaçable. Le scope contrôle les verbes. Les limites de ressources contrôlent l'endroit où ces verbes s'appliquent. Vous avez besoin des deux, mais les limites de ressources rendent généralement les erreurs supportables.

Donnez aux agents autonomes des identités de service plutôt que des jetons personnels. Les jetons d'accès personnels héritent souvent d'anciennes appartenances, d'autorisations d'administration temporaires et d'un accès à des projets oubliés lors de la configuration. En révoquer un peut aussi interrompre un travail sans rapport, ce qui pousse les équipes à repousser la révocation. C'est ainsi que les exceptions temporaires deviennent des accès permanents.

Une identité dédiée doit commencer sans accès et recevoir uniquement les autorisations de ressources listées dans le registre des endpoints. Si un agent travaille sur un dépôt, accordez-lui ce dépôt plutôt que toute l'organisation. S'il met à jour un déploiement de staging, donnez-lui accès à l'environnement de staging plutôt qu'à tous les environnements. S'il écrit des enregistrements pour un compte client, limitez-le à ce compte plutôt que de lui donner un identifiant API global.

Utilisez des cibles hors production distinctes pour tester les permissions. Tester un jeton en effectuant de vraies écritures en production vous apprend si le jeton fonctionne, mais ne montre pas qu'il est correctement limité. Un dépôt ou un projet de test permet d'exercer la création, la mise à jour, l'échec, la révocation et l'audit sans laisser de travail de nettoyage dans un système actif.

L'isolation des ressources compense aussi les modèles de scopes mal conçus. Certains services émettent un jeton doté d'un seul scope `api`, sans granularité au niveau des endpoints. Vous pouvez malgré tout créer un projet dédié contenant uniquement les ressources que l'agent peut utiliser, lui refuser l'administration de l'organisation et utiliser une identité distincte pour chaque environnement. C'est moins élégant qu'une API précise, mais bien préférable à la remise d'un jeton universel à un processus qui construit dynamiquement ses requêtes.

Ne donnez pas à un agent accès à la production simplement parce que le code qu'il modifie finira par y arriver. Le système de mise en production doit gérer cette transition par une voie approuvée et autorisée séparément. Si la tâche comprend réellement une opération de production, rédigez un contrat distinct pour cette opération. Il doit nommer l'environnement cible, la méthode autorisée, les paramètres acceptables, le comportement de retour arrière et la personne qui l'approuve.

## Testez la réussite et le refus dans un même contrat

Un jeu de permissions est incomplet tant que vous n'avez pas démontré deux choses : l'agent peut terminer le travail qui lui est confié et les actions voisines qui ne lui sont pas confiées échouent. Tester uniquement le parcours nominal prouve la commodité, pas le confinement.

Utilisez une identité de test propre pour chaque modification de permission. Les identifiants existants ont souvent des autorisations mises en cache, des rôles hérités ou un second chemin d'authentification qui fait réussir le test pour la mauvaise raison. Avant l'exécution, consignez le sujet du jeton, les ressources prévues, les scopes émis et la date d'expiration.

Une séquence de test pratique ressemble à ceci :

1. Créez une ressource cible temporaire et un identifiant avec les autorisations proposées.
2. Faites passer l'agent ou un jeu de requêtes déterministe par chaque appel autorisé du registre.
3. Vérifiez l'état attendu, par exemple une branche, une pull request, un commentaire ou un enregistrement mis à jour.
4. Envoyez chaque appel interdit avec le même identifiant et attendez-vous à un refus.
5. Supprimez l'identifiant ou révoquez la session, puis relancez un appel auparavant autorisé et attendez-vous à un refus.

Utilisez des requêtes directes en parallèle d'une exécution de l'agent. Elles éliminent l'incertitude liée au choix des outils et montrent si le fournisseur applique lui-même la limite. Cet exemple shell illustre la forme du test. Il suppose une API qui renvoie du JSON et utilise `403` pour une identité authentifiée qui ne possède pas la permission.

```sh
base="https://api.example.internal"
auth="Authorization: Bearer $AGENT_TOKEN"

curl -sS -o allowed.json -w "%{http_code}\n" \
  -H "$auth" \
  -X POST "$base/repos/acme/payments-service/pulls" \
  -H "Content-Type: application/json" \
  -d '{"head":"agent/dependency-bump","base":"main","title":"Update parser"}'
# Expected output: 201

curl -sS -o denied.json -w "%{http_code}\n" \
  -H "$auth" \
  -X DELETE "$base/repos/acme/payments-service"
# Expected output: 403

cat denied.json
# Expected shape: {"message":"Resource not accessible by integration"}
```

Ne vérifiez pas uniquement le code de statut. Inspectez l'état produit par les opérations autorisées. Certaines API acceptent une requête et la traitent de manière asynchrone, ou renvoient un succès tout en ignorant un champ dont dépendait l'agent. Pour les refus, distinguez `401` et `403`. Un `401` peut signifier que l'identifiant de test est mal formé ou expiré. Un `403` après une authentification réussie montre mieux que l'autorisation a bloqué l'appel. Les fournisseurs diffèrent, alors documentez leur comportement dans votre jeu de tests.

Conservez un test négatif pour chaque limite de permission dangereuse. Si un agent peut créer un déploiement, vérifiez qu'il ne peut pas en promouvoir un. S'il peut commenter un ticket, vérifiez qu'il ne peut pas modifier les étiquettes ou les responsables. S'il peut écrire la valeur d'un secret de staging, vérifiez qu'il ne peut pas la relire si l'API autorise l'écriture sans la lecture. Ces tests empêchent une modification ultérieure du scope d'élargir discrètement l'accès.

## Un 403 doit modifier la tâche ou l'autorisation

Une réponse `403 Forbidden` apporte un élément de preuve sur le contrat. Elle doit déclencher une décision, pas une demande automatique de la permission la plus large du fournisseur.

J'ai souvent vu ce scénario. Un agent crée une branche et valide un correctif, puis reçoit un refus lorsqu'il tente d'ouvrir une pull request. Quelqu'un découvre que la permission des pull requests autorise aussi le rejet d'une revue ou des modifications plus larges des discussions. L'équipe l'accorde parce que l'agent doit terminer. Quelques jours plus tard, le même agent commence à « nettoyer » les pull requests obsolètes et modifie du travail qui ne faisait pas partie de sa mission.

Le premier refus soulevait une question de conception : l'ouverture d'une pull request exige-t-elle réellement cette capacité plus large, et l'équipe accepte-t-elle ses effets secondaires ? Plusieurs réponses sont honnêtes.

- Accorder la permission après avoir testé toute sa surface d'endpoints et consigné le risque accepté.
- Modifier la tâche afin que l'agent prépare une branche et qu'une personne ouvre la pull request.
- Utiliser une autre identité du fournisseur ou une autre ressource où l'autorisation large n'atteint que le dépôt prévu.
- Placer devant l'API du fournisseur un service d'action limité qui n'accepte qu'une demande de création de pull request avec des contraintes fixes de ressource et de branche.

La mauvaise réponse consiste à ajouter tous les scopes qui transforment les réponses rouges en réponses vertes. Cela convertit les erreurs d'autorisation en incidents différés.

Les corps de requête méritent eux aussi un examen. De nombreux modèles de permissions autorisent un endpoint sans distinguer les valeurs sûres des valeurs dangereuses. `POST /deployments` peut accepter `staging` et `production` avec la même permission. `PATCH /projects/{id}` peut autoriser une modification anodine de description et une modification dangereuse de visibilité. Si le fournisseur ne peut pas séparer ces opérations, votre limite doit se situer au-dessus de l'endpoint. Exigez une approbation humaine, utilisez une cible dédiée ou exposez une opération conçue pour cet usage plutôt qu'un accès brut à l'API.

Consignez l'appel refusé dans le registre, avec la raison pour laquelle vous l'avez ajouté ou rejeté. Dans six mois, cette trace expliquera pourquoi l'agent peut créer une branche mais ne peut pas renommer un dépôt. Sans elle, quelqu'un jugera la limite arbitraire et l'élargira lors d'un correctif urgent.

## Les étapes d'approbation gèrent les appels les plus lourds de conséquences

Des permissions précises chez le fournisseur réduisent ce qu'un agent peut tenter. Les étapes d'approbation aident à gérer les actions autorisées qui méritent malgré tout une décision humaine, comme une opération de paiement externe, une commande SSH sur un hôte important ou une écriture dans un service de production.

N'utilisez pas l'approbation comme prétexte pour donner à l'agent des identifiants trop puissants. Une confirmation en un clic peut arrêter une mauvaise requête évidente, mais les personnes approuvent rapidement les demandes répétitives, surtout lorsqu'un agent doit effectuer plusieurs appels ordinaires pour terminer une tâche. La limite de permissions doit refuser les catégories entières d'actions avant même qu'une demande d'approbation apparaisse.

Utilisez l'approbation lorsque le contexte humain change la décision. Un déploiement peut être techniquement autorisé mais inopportun pendant un incident. Une demande de suppression de branche peut être permise mais incorrecte si un autre ingénieur l'utilise. Une demande d'approbation peut afficher la cible réelle et l'action demandée au moment où la personne peut les évaluer.

Sallyport conserve les identifiants API et SSH dans son coffre macOS chiffré et exécute l'action demandée sans exposer le secret à l'agent. Son autorisation de session et ses contrôles d'approbation par identifiant peuvent placer une personne sur le chemin des appels qui le méritent, mais la conception des scopes côté fournisseur détermine toujours ce qu'un identifiant approuvé peut atteindre.

Conservez des journaux d'audit qui répondent à deux questions distinctes : quel processus d'agent a reçu l'autorisation d'agir, et quels appels API individuels a-t-il effectués ? Ce sont deux enregistrements différents. L'approbation d'un processus prouve qu'une personne a autorisé cette exécution à utiliser un identifiant. Elle n'explique pas si l'exécution a créé une pull request, modifié une variable d'environnement ou tenté une suppression refusée. Examinez les deux après une modification de permission et après un incident.

## Les revues de scopes ont besoin d'un déclencheur, pas d'une promesse calendaire

Les revues de permissions sont efficaces lorsqu'un événement d'ingénierie les déclenche. Un vague rappel trimestriel retrouve généralement de vieux jetons dont plus personne ne connaît la raison d'être. Liez la revue aux changements de tâche, aux nouveaux endpoints, à l'élargissement des ressources, aux modifications des permissions du fournisseur et aux changements du workflow de l'agent.

Conservez le registre des endpoints avec le code qui appelle l'agent. Lorsqu'une pull request modifie les instructions des outils de l'agent ou ajoute un appel API, exigez la mise à jour du registre et de ses tests positifs et négatifs. La décision de permission se trouve ainsi à côté du comportement qui la nécessite.

Vérifiez la révocation avant d'en avoir besoin. Supprimez un identifiant de test et confirmez qu'une requête auparavant autorisée échoue. Désactivez l'identité de service et confirmez qu'un agent en cours d'exécution ne peut pas continuer grâce à une session mise en cache. Vérifiez si le fournisseur a émis des jetons de renouvellement ou des identifiants en double qui maintiennent le même accès. Les équipes découvrent souvent ces chemins pendant un incident, lorsque la réponse est la moins utile.

Surveillez l'augmentation progressive des permissions dans les petites modifications. Une demande de lecture des journaux de workflow peut devenir une permission de relancer des tâches. Une demande de mise à jour d'un ticket peut devenir une administration des tickets à l'échelle de l'organisation. Une demande d'accès à un environnement peut devenir un accès de secours à la production « au cas où ». Chaque nouvel appel doit répondre à la même question : l'agent peut-il terminer sa tâche déclarée sans lui ?

Si la réponse est non, ajoutez la plus petite autorisation qui permet l'appel et ajoutez un test de refus autour de son voisin dangereux le plus proche. Si la réponse est oui, laissez-la de côté. Cette discipline rend les échecs des agents plus visibles à court terme. Elle empêche aussi une instruction mal formée, un modèle désorienté ou un processus compromis d'hériter d'une autorité que personne n'avait l'intention de lui donner.
