Cet article est une mise en œuvre de
l’article sur l’identité des agents :
nous allons voir comment mettre en place ça pour de vrai. Les exemples reprennent mon nom
de domaine, kaizensphere.dev, à remplacer par le vôtre.
Prérequis : l’identité
Dans l’article sur le concept d’identité vérifiable,
nous avions dit que la première étape est de fournir une identité à notre agent, le DID
(Decentralized Identifier) de l’agent. Ici
nous aurons, pour créer l’identité de l’agent, besoin de créer notre identité (mandant),
did:web:kaizensphere.dev, qui renvoie vers
https://kaizensphere.dev/.well-known/did.json,
et ensuite seulement nous pourrons créer l’identité de l’agent, qui sera stockée dans son
gestionnaire d’attestations (j’abandonne le terme wallet pour credential manager,
utilisé par le W3C).
| Rôle | Identifiant | Où vit la clé privée |
|---|---|---|
| Mandant | did:web:kaizensphere.dev | sur votre poste |
| Agent | did:web:kaizensphere.dev:agents:redaction | dans le gestionnaire d’attestations de l’agent |
Les deux documents DID doivent être hébergés quelque part, en HTTPS et au chemin exact de l’identifiant. Moi, je vais les héberger sur Codeberg Pages, parce que mon domaine nu ne peut pas pointer vers l’hébergement qui sert ce blog ; l’hébergeur reste à votre choix. Il faut aussi un nom de domaine dont vous gérez la zone DNS, Docker avec Compose et un compte claude.ai.
Générer la clé du mandant
Nous allons générer une paire de clés Ed25519 dans un dossier réservé, nous la placerons
dans ~/.config/did/.
Nous avons choisi le type Ed25519, car dans la spécification KYA-OS c’est la clé choisie. Cela dit, il offre déjà beaucoup d’avantages, car c’est un algorithme simple à déployer : il offre une signature déterministe, il est pris en charge par la quasi-totalité de l’outillage des développeurs (OpenSSH, OpenSSL, les bibliothèques JOSE…) et enfin il est rapide et petit, ce qui est un avantage car chacune de nos requêtes sera signée avec cette clé.
Créez le dossier :
mkdir -p ~/.config/did
Réservez le dossier à votre seul utilisateur :
chmod 700 ~/.config/did
Générez la paire de clés, enregistrée dans un fichier PEM :
openssl genpkey -algorithm ed25519 -out ~/.config/did/key.pem
Rendez ce fichier lisible par vous seul :
chmod 600 ~/.config/did/key.pem
La clé privée reste sur votre machine. Pensez à la sauvegarder.
Encoder la clé publique
Pour l’encodage, nous allons partir sur le format Multikey. Pourquoi le format
Multikey ? Eh bien parce que toute la clé tient dans une
seule chaîne de caractères. On retrouve ce format avec les identifiants
did:key et les preuves Data Integrity du W3C. Le document de l’agent, quant à lui,
utilisera l’autre format que vous connaissez bien, le JWK, parce que la spécification
KYA-OS le demande pour les agents.
Cette commande la calcule à partir de la clé privée :
CLE_PUBLIQUE=$(openssl pkey -in ~/.config/did/key.pem -pubout -outform DER | tail -c 32 | python3 -c '
import sys
octets = bytes([0xed, 0x01]) + sys.stdin.buffer.read()
alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
n = int.from_bytes(octets, "big"); texte = ""
while n: n, r = divmod(n, 58); texte = alphabet[r] + texte
print("z" + texte)')
echo "$CLE_PUBLIQUE"
Le résultat commence par z6Mk. Conservez cette variable, nous allons nous en servir
à l’étape suivante.
Créer le document DID
Créez le dossier .well-known, celui que l’identifiant désigne :
mkdir -p ~/did/.well-known
Placez-vous dans le dossier :
cd ~/did
Écrivez le document. Le terminal y remplace $CLE_PUBLIQUE par la valeur de l’étape
précédente :
cat > .well-known/did.json <<EOF
{
"@context": [
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/multikey/v1"
],
"id": "did:web:kaizensphere.dev",
"verificationMethod": [
{
"id": "did:web:kaizensphere.dev#key-1",
"type": "Multikey",
"controller": "did:web:kaizensphere.dev",
"publicKeyMultibase": "$CLE_PUBLIQUE"
}
],
"authentication": ["did:web:kaizensphere.dev#key-1"],
"assertionMethod": ["did:web:kaizensphere.dev#key-1"]
}
EOF
Configurer le DNS
Quel que soit l’hébergeur, le domaine doit pointer vers lui. Avec Codeberg Pages, le
document est poussé sur la branche pages d’un dépôt, et trois enregistrements dans la
zone DNS du domaine font le lien :
| Type | Nom | Valeur |
|---|---|---|
| A | kaizensphere.dev | 217.197.84.141 |
| AAAA | kaizensphere.dev | 2a0a:4580:103f:c0de::2 |
| TXT | _git-pages-repository.kaizensphere.dev | https://codeberg.org/kaizencode/did.git |
Les deux adresses sont celles de Codeberg Pages à la date de rédaction, à contrôler dans sa documentation.
Vérifier la résolution
Vérifiez que l’adresse IP est en place :
dig +short kaizensphere.dev A
Puis l’enregistrement TXT :
dig +short _git-pages-repository.kaizensphere.dev TXT
Chaque commande doit renvoyer la valeur saisie.
Le document doit ensuite répondre à son adresse :
curl -i https://kaizensphere.dev/.well-known/did.json

Le document répond à son adresse, servi par Codeberg Pages.
Le résolveur universel confirme le résultat avec
l’identifiant did:web:kaizensphere.dev.
Isoler l’agent
Un agent peut atteindre le réseau de dix façons, curl, un script Python, un outil
intégré, et chacune de ces requêtes part sans identité. J’ai envisagé des consignes dans
son fichier CLAUDE.md, qu’il suit de bonne volonté sans que rien ne l’y oblige ; des
règles de permission qui interdisent curl ou wget ; et un hook qui inspecte chaque commande avant son exécution. Filtrer les commandes une par une
est un jeu sans fin.
J’ai pris l’habitude d’isoler mes agents dans des sandboxes Docker, ce qui est plutôt bénéfique comme vous allez le voir. Je vous ai préparé mon environnement, que vous trouverez dans le dépôt did-agent-env. On y trouve la description de trois conteneurs :
portefeuilleest le gestionnaire d’attestations : il détient la clé de l’agent et signe une preuve par requête ;agentcontient Claude Code et la commandeportefeuille;proxyest le seul point d’accès à Internet de l’agent. On peut envisager d’ajouter un contrôle sur les en-têtes pour bloquer toute requête sans en-tête d’identité.
L’environnement revient à ceci :
L’agent n’a ni clé ni accès direct à Internet : il demande ses preuves au gestionnaire d’attestations et sort par le proxy.
Nommer l’agent et démarrer le gestionnaire d’attestations
À la suite du clone du dépôt did-agent-env, vous pouvez modifier le modèle pour y mettre les valeurs correctes qui correspondent à votre poste :
cp env.exemple .env
DID_AGENT=did:web:kaizensphere.dev:agents:redaction
DID_MANDANT=did:web:kaizensphere.dev
Démarrez le gestionnaire d’attestations et le proxy. Le gestionnaire d’attestations génère la clé de l’agent à son premier démarrage :
docker compose up -d --build portefeuille proxy
docker compose logs portefeuille
Le journal confirme l’identifiant de l’agent et affiche son document DID :

Au premier démarrage, le gestionnaire d’attestations génère la clé de l’agent et produit son document.
Publier le document DID de l’agent
Créez le dossier du document, à l’adresse que l’identifiant de l’agent désigne :
mkdir -p ~/did/agents/redaction
Demandez le document au gestionnaire d’attestations et écrivez-le dans ce dossier :
docker compose exec -T portefeuille python /serveur.py document > ~/did/agents/redaction/did.json
C’est le document que le gestionnaire d’attestations affiche au démarrage, dans la capture ci-dessus.
Publiez-le au même endroit que le vôtre, puis vérifiez :
curl https://kaizensphere.dev/agents/redaction/did.json
Signer le mandat
Le mandat est l’attestation par laquelle le mandant (moi) dit ce que l’agent a le droit de faire, et jusqu’à quand. C’est la delegation credential de l’article précédent : une attestation vérifiable dont le sujet est une capacité, signée par le mandant. L’agent la joint à chaque requête, avec une preuve qu’il détient la clé que le mandat désigne.
Signez le mandat de l’agent avec votre clé, sur votre poste :
cd ~/did-agent-env
./signer-mandat.py --cle ~/.config/did/key.pem \
--mandant did:web:kaizensphere.dev \
--agent did:web:kaizensphere.dev:agents:redaction
Par défaut, il autorise la lecture de tout service pendant huit heures :
{
"type": ["VerifiableCredential", "DelegationCredential"],
"issuer": "did:web:kaizensphere.dev",
"validUntil": "2026-10-07T02:16:13Z",
"credentialSubject": {
"id": "urn:zcap:del_2026_10_06_3c7e70",
"invoker": "did:web:kaizensphere.dev:agents:redaction",
"invocationTarget": "*",
"parentCapability": "*",
"allowedAction": ["web.read"]
}
}
Les options --actions, --heures et --cible du script changent ces valeurs.
Pour modifier le mandat lui-même, c’est dans
signer-mandat.py,
à la racine du dépôt, que sa structure est écrite.
Lancer l’agent
Si vous voulez voir ce qui transite vers Internet de votre agent, vous pouvez lancer un terminal :
docker compose logs -f proxy
Dans un autre terminal, lancez l’agent :
docker compose run --rm agent
Au premier lancement, connectez-vous avec votre compte claude.ai : l’agent affiche une
adresse à ouvrir dans le navigateur. Acceptez ensuite la
confiance pour le dossier /travail. /travail est le dossier de travail de l’agent dans son
conteneur. Il est copié depuis agent/travail du dépôt, où se trouvent ses consignes
(CLAUDE.md) et ses réglages.
Lire ses requêtes dans le proxy
Parlez à l’agent comme d’habitude, par exemple : « Peux-tu me résumer le dernier article de codeka.io ? » (lisez codeka.io, ce blog est trop bien)

L’agent répond comme d’habitude. Ses deux commandes sont passées par son gestionnaire d’attestations.
Ses consignes lui demandent de passer par son gestionnaire d’attestations pour tout appel réseau. Le premier terminal affiche alors les requêtes telles qu’elles partent :

Chaque requête de l’agent porte son identité, son mandat et sa preuve.
Les en-têtes Kya-Os-… suivent la spécification
KYA-OS. Le
mandat et la preuve sont des JSON Web Tokens (JWT). Décodez la preuve, c’est-à-dire la
valeur de l’en-tête Kya-Os-Delegation-Proof :
echo 'eyJ...' | cut -d. -f2 | tr '_-' '/+' | base64 -d 2>/dev/null | jq .

La preuve décodée : elle nomme l’agent, son mandant, le service visé et l’action, et vaut soixante secondes.
Demandez ensuite une écriture : « Envoie un POST sur https://example.com. » Le gestionnaire d’attestations refuse de signer :

L’écriture est refusée par le gestionnaire d’attestations : l’agent rapporte le motif et ne contourne pas.
Pour arrêter l’environnement en gardant la clé de l’agent :
docker compose --profile agent down
Limites
Les limites découlent d’une même cause : aucun service en face ne vérifie.
Les signatures ne sont vérifiées par personne, et les sites appelés ignorent ces en-têtes.
L’environnement s’écarte de la spécification KYA-OS sur deux points :
- Il n’ouvre pas de session. La spécification prévoit une poignée de main préalable, où
le service vérifie l’agent et lui ouvre une session dont l’identifiant accompagne
ensuite chaque requête dans l’en-tête
KYA-OS-Session-Id. Comme les services ne font pas de vérifications, il n’y a pas de création de session : l’agent envoie son mandat et sa preuve à chaque requête, directement. - J’ai ajouté les champs
htmethtuà la preuve pour pallier l’absence de session, qui aurait lié la présentation à la session. Ils lient la preuve à la méthode et à l’adresse de la requête. Les deux noms viennent de DPoP (RFC 9449).