8 min de lecture

Isolation des identifiants dans les pools de connexions HTTP pour les agents IA

L’isolation des identifiants dans les pools de connexions HTTP empêche les cookies, défis et états de compte de suivre une socket réutilisée vers la mauvaise action d’agent.

Isolation des identifiants dans les pools de connexions HTTP pour les agents IA

Une connexion HTTP réutilisée n’entraîne pas automatiquement une fuite d’identifiants. La considérer comme inoffensive pousse les équipes à manquer la véritable défaillance : un objet client accumule des cookies, des réponses à des défis, des en-têtes de redirection, une identité de proxy ou une identité TLS, puis une requête ultérieure réutilise cet état avec un autre identifiant.

L’isolation des identifiants dans les pools de connexions HTTP consiste à décider précisément quel état peut passer d’une requête à l’autre et à rendre le reste impossible à partager. Le seul hôte de destination constitue généralement une frontière trop large pour un exécuteur d’actions ou un agent qui peut agir pour plusieurs comptes. Un client rapide qui envoie parfois le cookie du mauvais compte est pire qu’un client plus lent, car l’erreur ressemble à une requête normale réussie.

Une socket transporte des données, elle n’appartient pas à un compte

Une connexion TCP transporte des octets pour une origine. Elle ne fournit pas à l’application une frontière de compte fiable. HTTP/1.1 réutilise la connexion de manière séquentielle, et HTTP/2 peut exécuter plusieurs flux en même temps, mais aucun de ces protocoles n’indique que toutes les requêtes d’une connexion appartiennent au même utilisateur ou au même compte de service.

Cette distinction compte, car les bibliothèques clientes placent souvent des sujets sans rapport derrière une interface pratique appelée session, agent, client ou transport. Les développeurs créent alors un de ces objets par hôte et ajoutent un jeton bearer avant chaque appel. L’en-tête bearer peut être correct à chaque fois, tandis qu’un stockage de cookies, un cache de défis Digest ou un contexte de proxy appartient discrètement au dernier compte qui a utilisé l’objet.

La RFC 9110 traite l’authentification comme un comportement de requête et d’espace de protection. Un client répond à un défi pour une origine, un schéma et un domaine d’authentification précis. Elle ne dit pas qu’une connexion ouverte possède un humain ou un compte de service. Si votre client fait cette hypothèse de propriété, elle vient de votre code ou de sa bibliothèque, pas de HTTP.

La réutilisation des connexions reste souhaitable. Elle évite des négociations répétées, réduit l’épuisement des ports et aide un service distant sous charge. La bonne règle est plus précise : réutilisez une connexion uniquement entre des requêtes dont l’état lié à la connexion et géré par le client est volontairement compatible.

Les états à séparer se répartissent en deux groupes :

  • L’état de requête comprend Authorization, Cookie, les en-têtes de compte, les corps de requête et les valeurs d’idempotence. L’appelant doit les reconstruire pour chaque action.
  • L’état de connexion et du client comprend une entrée de pool, une session de proxy, la sélection du certificat client, le comportement de redirection, les cookies, les caches de défis et les paramètres du protocole. Le client doit délimiter ou désactiver chacun d’eux volontairement.

Un identifiant bearer utilisé uniquement dans un en-tête Authorization peut partager une connexion TCP avec un autre identifiant bearer si la bibliothèque envoie les en-têtes pour chaque requête et ne conserve aucun état de compte. C’est une affirmation limitée, pas une autorisation générale. Dès que le service définit aussi un cookie de session, redirige vers un autre hôte ou demande un certificat client, la conception du pool doit être réexaminée.

Les cookies sont un état de compte, même quand leur nom paraît anodin

Un stockage de cookies est la source la plus fréquente de passage involontaire entre comptes. Les équipes voient une API à jeton bearer et supposent que les cookies ne comptent pas, jusqu’à ce qu’un répartiteur de charge, un point de terminaison de connexion interactif ou un service historique ajoute Set-Cookie à une réponse. Un client HTTP généraliste le stocke habituellement, sauf si vous lui demandez de ne pas le faire.

La RFC 6265 définit comment un agent utilisateur sélectionne les cookies selon le domaine, le chemin, les attributs de sécurité et des règles associées. Son modèle de stockage ne comporte aucun champ pour « l’enregistrement d’identifiant qui a reçu ce cookie ». Deux identifiants bearer appelant le même hôte et le même chemin peuvent donc correspondre au même cookie stocké. Le protocole suit parfaitement ses règles tandis que votre application mélange les comptes.

Voici une trace issue d’un service de test. Le compte alpha utilise un jeton bearer et reçoit un cookie d’affinité :

GET /v1/whoami HTTP/1.1
Host: api.example.test
Authorization: Bearer alpha-token

HTTP/1.1 200 OK
Set-Cookie: route=alpha-node; Path=/; Secure; HttpOnly
Content-Type: application/json

{"account":"alpha"}

L’action suivante sélectionne beta. Si un stockage partagé envoie le cookie conservé, la requête porte deux signaux de propriété :

GET /v1/whoami HTTP/1.1
Host: api.example.test
Authorization: Bearer beta-token
Cookie: route=alpha-node

Le meilleur scénario est un service qui rejette cette incohérence. Des services moins rigoureux font passer la requête de beta par la session persistante d’alpha, l’attachent au mauvais contexte côté serveur ou acceptent l’en-tête qui l’emporte. Le client ne peut pas compter sur le service distant pour sauver une requête mélangée.

Pour les API entre machines, ma règle par défaut est directe : désactivez le stockage ambiant des cookies. Si une API exige réellement des cookies, créez un stockage de cookies pour un enregistrement d’identifiant et un contexte de service prévu. Ne le partagez pas simplement parce que la chaîne de l’hôte correspond.

Vérifiez aussi les entrées de la requête avant d’injecter l’identifiant. Un agent, un plugin ou un appelant ne doit pas pouvoir fournir son propre en-tête Cookie et le voir survivre dans un appel avec identifiant. Supprimez les en-têtes d’authentification et les cookies ambiants, puis ajoutez uniquement les en-têtes permis par la définition de l’action. Sinon, vous avez construit une isolation autour d’une porte que les appelants peuvent contourner.

Les défis d’authentification nécessitent un appelant actuel

Une réponse 401 n’est pas qu’un code d’erreur. Elle peut inviter le client à réessayer après avoir choisi des identifiants, et cette logique de sélection vit souvent hors du code qui a créé la requête initiale. Les gestionnaires d’authentification Basic et Digest sont particulièrement sujets à ce schéma, mais un middleware personnalisé peut faire la même erreur avec des jetons bearer.

Une mauvaise implémentation conserve un currentCredential mutable sur un client partagé. La requête A reçoit un défi, le gestionnaire charge le secret d’alpha et le client réessaie. La requête B démarre avant la fin de cette nouvelle tentative et remplace currentCredential par beta. Le gestionnaire de défis se trouve alors confronté à une condition de concurrence que les suites de tests manquent, car elles exécutent les requêtes une par une.

Ne corrigez pas cela en plaçant un verrou autour d’un champ global d’identifiant. Le verrou sérialise le travail et laisse tout de même le mauvais objet responsable de l’identité. Attachez l’enregistrement d’identifiant au contexte de la requête, faites-le suivre à chaque nouvelle tentative et rejetez toute nouvelle tentative dont le contexte est absent.

L’authentification Digest mérite une vigilance particulière. Elle implique des nonces, des domaines d’authentification, des compteurs et une réponse calculée. Ces valeurs sont liées à l’espace de protection qui a émis le défi, pas à un client partagé générique. L’authentification Basic est plus simple sur le réseau, mais un cache qui l’ajoute automatiquement aux requêtes ultérieures doit respecter la même portée d’identifiant.

Utilisez un point de terminaison de test qui renvoie un domaine d’authentification ou un défi distinct pour chaque compte. Exécutez ensuite des requêtes simultanées et vérifiez que chaque nouvelle tentative utilise l’enregistrement d’identifiant associé à sa propre action. Un test qui vérifie seulement le statut final 200 est trop faible. Capturez l’identité de la requête côté serveur et échouez si le défi d’alpha produit une tentative d’autorisation de beta.

Ne confondez pas une 403 avec un défi. Les serveurs utilisent 403 pour de nombreuses décisions d’autorisation, et les clients ne doivent pas la traiter comme une permission de chercher un autre identifiant. Le basculement automatique entre identifiants transforme un échec d’accès en exploration de comptes, ce qui est dangereux et difficile à auditer.

HTTP/2 rend les erreurs de propriété plus faciles à dissimuler

HTTP/2 permet à plusieurs flux de partager une connexion TLS. C’est efficace, mais cela fait disparaître l’ancien repère visuel d’une requête qui attend derrière une autre. Un client peut envoyer des actions pour alpha et beta au même instant, et leurs en-têtes ne restent distincts que si la bibliothèque les modélise comme des requêtes séparées jusqu’au bout.

La RFC 9113 interdit les champs d’en-tête propres aux connexions HTTP/1.1, comme Connection, dans les requêtes HTTP/2. Cette règle ne donne pas une portée de compte aux cookies ou à l’authentification. Elle empêche seulement qu’une catégorie de comportement entre sauts soit exprimée sous forme d’en-têtes de requête ordinaires. Votre client reste responsable de la sélection des cookies, des nouvelles tentatives, des redirections et de tout middleware partagé.

La coalescence des connexions HTTP/2 ajoute une autre subtilité. Certains clients peuvent utiliser une connexion sécurisée pour plusieurs origines lorsque le certificat et les contrôles de nom l’autorisent. La coalescence ne rejoue pas d’elle-même un en-tête Authorization. Elle signifie qu’une identité de pool basée sur une comparaison d’hôte approximative peut ne pas décrire la connexion réellement choisie par le client. Séparez les décisions d’autorisation d’origine de l’optimisation du transport, et testez le comportement de la bibliothèque déployée.

La compression des en-têtes inquiète aussi les gens pour de mauvaises raisons. HPACK compresse les en-têtes au sein d’une connexion HTTP/2, et QPACK fait un travail similaire pour HTTP/3. Un client conforme ne décode pas la valeur Authorization d’une requête précédente dans une requête ultérieure. Le danger vient de la réutilisation directe d’état dans votre code client, ainsi que d’éventuels enjeux de métadonnées dans des modèles de menace inhabituels. Marquez les en-têtes sensibles comme jamais indexés lorsque votre bibliothèque offre ce contrôle, mais n’y voyez pas un remplacement de l’isolation des requêtes.

HTTP/3 remplace TCP par QUIC pour le transport. Il ne change pas la règle de propriété. Un pool qui laisse un stockage de cookies ou un cache d’identifiants circuler librement entre les actions reste erroné avec QUIC.

Les certificats clients sont différents. Un certificat client TLS est choisi lors de l’établissement de la connexion, il est donc réellement lié à la connexion. Ne multiplexez jamais différentes identités de certificat client sur la même connexion ou la même entrée de pool. Intégrez l’enregistrement du certificat à l’identité du pool, ou donnez à chaque certificat une instance cliente distincte. La même prudence vaut pour un proxy qui authentifie une connexion avant de transférer les requêtes.

Rendez l’identité du pool explicite dans le code

Verrouillez la passerelle d’actions
Le coffre chiffré reste verrouillé derrière Secure Enclave et Touch ID, et bloque les actions jusqu’à son déverrouillage.

L’identité du pool doit inclure toute valeur qui change ce qu’une nouvelle connexion peut signifier sans danger. Pour une API utilisant uniquement des jetons bearer, avec cookies désactivés et sans identité de proxy, il peut s’agir de l’origine et d’un identifiant d’enregistrement d’identifiant. Pour un certificat client, un chemin authentifié par proxy ou un profil TLS particulier, incluez aussi ces identifiants.

N’utilisez pas le texte du secret comme identifiant de carte. Cela crée des copies inutiles du secret en mémoire et dans les journaux, complique le renouvellement et incite quelqu’un à afficher la carte pendant le débogage. Utilisez un identifiant opaque d’enregistrement d’identifiant, dont le cycle de vie est contrôlé par votre coffre ou votre registre d’actions.

Cette esquisse TypeScript montre la forme à adopter. Elle utilise le Pool d’Undici, mais la frontière s’applique à toute bibliothèque cliente. Le credentialId est un identifiant, pas la valeur bearer.

import { Pool } from "undici";

type Boundary = {
  origin: string;
  credentialId: string;
  proxyId?: string;
  clientCertificateId?: string;
};

const pools = new Map<string, Pool>();

function poolId(boundary: Boundary): string {
  return [
    boundary.origin,
    boundary.credentialId,
    boundary.proxyId ?? "direct",
    boundary.clientCertificateId ?? "none"
  ].join("\u001f");
}

function poolFor(boundary: Boundary): Pool {
  const id = poolId(boundary);
  let pool = pools.get(id);
  if (!pool) {
    pool = new Pool(boundary.origin);
    pools.set(id, pool);
  }
  return pool;
}

function headersFor(token: string, input: HeadersInit = {}): Headers {
  const headers = new Headers(input);
  headers.delete("authorization");
  headers.delete("cookie");
  headers.delete("proxy-authorization");
  headers.set("authorization", `Bearer ${token}`);
  return headers;
}

L’esquisse empêche un en-tête Authorization ou Cookie fourni par l’appelant d’accompagner l’identifiant injecté. Elle n’implémente pas de stockage de cookies, de redirections, de répartition par proxy ni de configuration de certificat. Cette omission est volontaire : chacun de ces éléments exige une décision consciente plutôt qu’un réglage par défaut accidentel.

Si tous les identifiants parlent au même point de terminaison public anonyme, des pools séparés peuvent être inutiles. Si le point de terminaison voit différents comptes, utilisez des entrées de pool séparées jusqu’à disposer d’une raison précise et de preuves pour les partager. Un pool coûte peu face à une confusion de comptes.

Le renouvellement exige sa propre règle. Quand un enregistrement d’identifiant change, cessez d’attribuer de nouvelles actions à son ancien pool. Laissez les requêtes déjà envoyées se terminer sous leur enregistrement initial si la sémantique de vos actions le permet, puis fermez ce pool. Un renouvellement change l’autorité, il ne corrige pas le sens d’un travail déjà commencé.

Une recommandation populaire conseille de désactiver keep-alive partout. Cela réduit le nombre d’objets de transport partagés, ce qui paraît sûr après un incident. Cela masque aussi la question de savoir si les cookies, le code de redirection et les caches de défis sont correctement délimités, tout en créant une nouvelle charge de négociation pour chaque requête. Conservez les pools, rendez leur propriété explicite et testez les parcours difficiles.

Les redirections peuvent envoyer une requête propre au mauvais endroit

Arrêtez un agent en cours
Révoquez instantanément une exécution d’agent depuis le journal des sessions si son travail ne semble plus correct.

La gestion des redirections est un second client caché dans le premier. Une bibliothèque reçoit une réponse 301, 302, 303, 307 ou 308, construit une autre requête et décide quels en-têtes survivent. Si cette décision intervient après votre code de frontière d’identifiant, elle peut transporter un en-tête de compte ou un cookie vers une destination imprévue.

Pour les actions avec identifiants injectés, commencez avec des redirections désactivées ou manuelles. Examinez l’origine cible avant d’en suivre une. N’autorisez les redirections que lorsque la définition de l’action les prévoit, que la destination appartient à son ensemble d’origines approuvées et que le client reconstruit les en-têtes depuis le contexte d’identifiant initial au lieu de copier un ancien lot d’en-têtes.

Le code de statut compte. Une 303 peut changer une requête en GET dans le comportement habituel des navigateurs, tandis que 307 et 308 conservent la méthode et le corps. Un client qui réessaie et les traite tous de la même façon peut répéter une écriture avec identifiant vers un autre point de terminaison. C’est un problème d’intégrité de l’action, même si aucun secret ne traverse une origine.

Supprimez les en-têtes Authorization et Cookie à chaque redirection inter-origine. Dans de nombreux clients, c’est déjà le comportement par défaut, mais une valeur par défaut n’est pas un résultat de test. Vérifiez la version et la configuration utilisées. Les redirections de même origine nécessitent aussi une liste de chemins autorisés lorsqu’un identifiant donne davantage d’autorité que le point de terminaison initial ne devrait en recevoir.

L’authentification du proxy demande le même traitement. Proxy-Authorization appartient au chemin de proxy sélectionné, pas au service d’origine. Ne le placez jamais dans une carte générique d’en-têtes par défaut. Si deux actions atteignent la même API via des proxys authentifiés différents, séparez leurs entrées de pool et rendez la sélection du proxy visible dans l’enregistrement de l’action.

Un test d’isolation échoué a une forme reconnaissable

La plupart des équipes testent qu’alpha peut appeler l’API et que beta peut appeler l’API. Cela prouve que les identifiants fonctionnent. Cela ne prouve pas que le même client de longue durée peut passer d’alpha à beta sans emporter d’état.

Créez un petit serveur de test avec deux comptes et faites-lui signaler ce qu’il a reçu. Il doit renvoyer le compte déduit de Authorization, faire écho à l’en-tête Cookie entrant, définir un cookie propre au compte et exposer un point de terminaison de redirection. Le service doit rejeter une requête lorsque l’étiquette de compte du cookie diffère de celle du compte bearer. Le rejet rend l’erreur visible au lieu de laisser une couche d’affinité la dissimuler.

Exécutez cette séquence avec la configuration client exacte utilisée en production :

  1. Envoyez une requête alpha via un pool neuf. Confirmez que la réponse définit route=alpha.
  2. Envoyez une requête beta via la recherche de pool utilisée en production. Vérifiez que le serveur voit l’autorisation beta et un en-tête Cookie vide, sauf si beta possède un stockage distinct ne contenant que des cookies beta.
  3. Lancez simultanément des requêtes alpha et beta sur HTTP/2. Faites attendre chaque point de terminaison avant de répondre afin que leurs flux se chevauchent, puis vérifiez séparément les identités et les cookies.
  4. Renvoyez un défi 401 à chaque compte et vérifiez que la nouvelle tentative utilise son enregistrement d’identifiant initial.
  5. Renvoyez une redirection vers une autre origine et vérifiez que la requête de suivi ne porte ni en-tête d’autorisation ni en-tête de cookie.

Les vérifications doivent enregistrer davantage que les codes de statut. Capturez l’identifiant d’enregistrement sélectionné, l’identifiant du pool, l’origine, la version du protocole, la cible de redirection, la présence d’un cookie et le compte de réponse. Ne consignez pas l’identifiant lui-même. Lorsque le test échoue, ces champs permettent de savoir si la fuite vient de la recherche du pool, des en-têtes ambiants, d’un stockage de cookies ou du middleware de nouvelle tentative.

Testez aussi le renouvellement des identifiants. Lancez une requête qui attend côté serveur, remplacez l’enregistrement d’identifiant, puis envoyez une nouvelle requête. La nouvelle action doit utiliser une nouvelle identité de pool. Décidez et documentez si l’action en attente s’achève avec son autorité initiale ou est annulée. Les deux choix peuvent se défendre ; changer silencieusement l’autorité en cours de route ne l’est pas.

Exécutez le test avec toute configuration de proxy prise en charge. L’état du proxy et celui de l’origine vivent souvent dans des couches de bibliothèque différentes, ce qui fait d’un test unitaire rassurant un véritable test d’intégration utile.

Les actions des agents nécessitent une surface d’autorité plus réduite

Voyez quel agent agit
L’autorisation par session affiche le nouveau processus d’agent et son autorité de signature de code avant son premier appel.

Un agent autonome de programmation doit demander une action comme « appeler cette API approuvée avec l’enregistrement d’identifiant billing-read ». Il ne doit pas recevoir un jeton et construire un client large, de longue durée, capable de conserver l’état de session après le changement de tâche. L’exécuteur d’actions peut lier la destination approuvée, la méthode, l’enregistrement d’identifiant et la frontière client avant même d’envoyer le moindre octet.

Sallyport applique cette séparation en conservant les identifiants API et SSH dans son coffre chiffré et en exécutant lui-même l’action : l’agent reçoit ainsi le résultat, pas l’identifiant. Ce confinement n’est utile que si le chemin HTTP traite aussi chaque enregistrement d’identifiant comme son propre contexte d’autorité.

L’approbation ne remplace pas l’isolation du client. Un opérateur peut approuver une action beta légitime, tandis qu’un stockage de cookies partagé négligemment transforme cette action en requête mêlant alpha et beta. L’enregistrement d’approbation documente alors une action différente de celle que l’opérateur voulait autoriser. Placez la frontière sous l’écran d’approbation, là où les en-têtes, les nouvelles tentatives et la sélection du transport ont réellement lieu.

Pour une passerelle d’actions, consignez l’identifiant d’enregistrement et l’identité du pool dans l’événement d’audit, jamais le secret. Si un appelant signale une anomalie de compte, vous devez établir si l’exécuteur a sélectionné le bon identifiant et n’a attaché que l’état appartenant à cet enregistrement. Une piste d’activité inviolable aide à examiner cette question ; elle ne rend pas sûre une conception client ambiguë.

La première modification concrète est simple : trouvez chaque client HTTP partagé et listez ce qu’il mémorise au-delà d’une connexion ouverte. Si la réponse inclut des cookies, des réponses à des défis, des redirections, des certificats clients ou une identité de proxy, attribuez un propriétaire explicite à chaque élément avant que le prochain renouvellement de jeton ou la prochaine exécution simultanée d’agents ne transforme le bogue en problème coûteux.

FAQ

HTTP peut-il conserver un en-tête Authorization sur une connexion réutilisée ?

Une connexion TCP ou TLS ne mémorise généralement pas un en-tête HTTP Authorization une fois la requête terminée. La fuite survient lorsque l’enveloppe cliente, le stockage de cookies, le gestionnaire de redirection, la configuration du proxy ou un identifiant lié à la connexion réutilise un état appartenant à l’appelant précédent.

Le partage de connexion HTTP/2 est-il sûr pour différents comptes ?

HTTP/2 multiplexe plusieurs flux de requêtes sur une même connexion. Une connexion constitue donc une mauvaise frontière de propriété quand les identifiants diffèrent. Gardez les identifiants attachés à chaque requête et ne partagez une connexion qu’après avoir vérifié que le client ne transporte aucun état de compte entre les flux.

Des identifiants API différents doivent-ils partager un stockage de cookies ?

Non. Un stockage de cookies indexe les cookies par hôte, domaine, chemin et attributs associés, pas par le jeton bearer qui les a obtenus. Un stockage partagé peut envoyer le cookie de session d’un compte avec l’en-tête d’autorisation d’un autre compte.

Deux certificats clients peuvent-ils partager un pool de connexions HTTP ?

N’utilisez pas le même pool pour des requêtes qui authentifient la connexion TLS avec des certificats clients différents. Le certificat est choisi lors de la négociation, avant même qu’une requête HTTP individuelle existe.

Les défis 401 créent-ils des risques de fuite d’identifiants ?

Une réponse 401 peut pousser un client à choisir des identifiants depuis un cache, un gestionnaire d’invite ou le contexte d’une requête précédente. Testez explicitement les requêtes soumises à un défi et vérifiez que la nouvelle tentative ne choisit que les identifiants associés à l’action en cours.

Comment nommer les pools quand les jetons sont renouvelés ?

Un pool de connexions doit utiliser un identifiant stable d’enregistrement d’identifiant, pas le texte du jeton ni seulement l’hôte cible. Lorsque vous remplacez cet enregistrement, retirez son ancien pool pour que les nouveaux travaux ne puissent pas hériter de son contexte de transport.

Dois-je désactiver le pool de connexions pour empêcher le mélange d’identifiants ?

Non. Désactiver la réutilisation peut atténuer le symptôme, mais laisse les stockages de cookies partagés, le code de redirection et les caches d’identifiants sans examen tout en ajoutant de la latence et de la pression sur les connexions. Définissez une frontière volontaire et prouvez-la par des tests.

L’authentification du proxy a-t-elle besoin de sa propre frontière de pool ?

L’authentification du proxy est distincte de celle de l’origine, mais elle peut tout de même être mise en cache ou négociée sur une connexion de proxy partagée. Intégrez l’identité du proxy à l’identité du pool et testez-la indépendamment de l’identifiant de l’origine.

Que doit vérifier un test d’intégration pour l’isolation des identifiants ?

Vérifiez l’identité renvoyée par le service, regardez si un en-tête Cookie est arrivé et enregistrez l’identifiant d’enregistrement choisi par le client. Exécutez le même test en HTTP/1.1 et HTTP/2 si le client de production peut utiliser les deux.

Comment une passerelle d’agents IA réduit-elle l’exposition des identifiants HTTP ?

Une passerelle doit recevoir l’action demandée et la référence de l’identifiant, injecter elle-même le secret, puis renvoyer le résultat sans exposer le secret à l’agent. La passerelle doit aussi appliquer de bonnes frontières client en dessous : le confinement du secret ne corrige pas un stockage de cookies partagé mal conçu.

Sallyport

Sallyport exécute les appels d'API et les commandes SSH à la place de votre agent IA. Les clés restent dans un coffre-fort local sur votre Mac ; vous approuvez chaque exécution et chaque action est consignée dans un journal scellé.

© 2026 Sallyport · Open source sous Apache-2.0 · Oleg Sotnikov