Annexes locales¶
Objectif: faire tourner les briques locales utilisées par le service Todo: MongoDB, lecture de la base, secrets locaux et OpenTelemetry.
Pourquoi MongoDB ?¶
Le repository memory suffit pour les tests et pour un seul processus Python. MongoDB sert de
stockage partagé quand l'API FastAPI, le serveur MCP et LangGraph tournent dans des processus
séparés.
Lancer MongoDB avec Docker¶
Commande minimale:
| Bash | |
|---|---|
Vérifier que le container tourne:
Pour repartir d'une base vide:
La suppression du volume efface uniquement les données MongoDB de ce tutoriel.
Configurer le repository MongoDB¶
Installer l'extra:
| Bash | |
|---|---|
Ajouter l'adapter:
| Bash | |
|---|---|
Choisir mongodb, puis répondre:
| Text Only | |
|---|---|
Modifier config/adapters/adapters.yaml:
Modifier config/adapters/outbound/mongodb.yaml:
| YAML | |
|---|---|
Créer config/secrets.yaml:
Créer secrets.yaml à la racine du projet. Ce fichier reste local:
| YAML | |
|---|---|
Adapter MongoDB Todo¶
Créer les packages:
| Bash | |
|---|---|
Créer src/todo_list_service/adapters/outbound/mongodb/repositories/todo_repository.py:
Créer src/todo_list_service/adapters/outbound/mongodb/repository.py:
| Python | |
|---|---|
Modifier src/todo_list_service/infrastructure/containers/todo_container.py:
Le container choisit MongoDBTodoRepository quand repository: mongodb est actif. Sinon, il utilise
le repository standard Arclith, ce qui permet aux tests de rester en memory.
Pyproject complet¶
Les commandes uv add des étapes API, MCP, agent et MongoDB donnent ce pyproject.toml:
uv.lock est généré par uv sync; il n'est pas recopié à la main dans le tutoriel.
Relancer API, MCP et LangGraph¶
Lancer chaque canal dans son terminal.
API:
| Bash | |
|---|---|
MCP:
| Bash | |
|---|---|
LangGraph:
| Bash | |
|---|---|
À partir de là, une todo créée par Swagger, par LM Studio via MCP ou par LangGraph doit être visible par les autres canaux.
Valider LangGraph Hors Ligne¶
Studio est utile pour apprendre le graphe, mais l'UI hébergée n'est pas nécessaire. Hors ligne, tester l'Agent Server local par API:
| Bash | |
|---|---|
Pour prouver la persistance de conversation, créer un thread:
Les données métier doivent venir de MongoDB via TodoRepositoryPort, pas de la mémoire interne du
processus LangGraph.
Visualiser MongoDB avec Compass¶
Connexion:
| Text Only | |
|---|---|
À vérifier:
- base:
todo-list-service; - collection:
todo; - documents: un document par todo, avec
_idégal à l'UUID public de l'entité.
Requêter MongoDB en CLI¶
Installer mongosh si nécessaire, puis:
| Bash | |
|---|---|
Dans le shell:
| JavaScript | |
|---|---|
Ajouter Vault localement¶
Cette section remplace le fichier local secrets.yaml par un Vault de développement. Le mode dev
conserve tout en mémoire, démarre déjà initialisé et non scellé, et utilise un token racine : il est
adapté uniquement à ce POC local, jamais à la production.
Démarrer Vault en mode dev¶
Choisir un token jetable dans le terminal courant. La valeur ci-dessous n'est pas un credential réel et ne doit pas être réutilisée ailleurs :
| Bash | |
|---|---|
Attendre au maximum 30 secondes que le serveur soit disponible :
| Bash | |
|---|---|
Le binding 127.0.0.1 évite d'exposer le port dev sur le réseau local. Le token est transmis au
container uniquement pour ce lancement jetable ; ne pas le mettre dans Git, dans une image ou dans
un environnement partagé.
Initialiser KV v2 et les données du POC¶
Télécharger le script de seed vérifié, le placer dans le projet Todo sous
scripts/seed-vault.sh, puis l'exécuter :
| Bash | |
|---|---|
ARCLITH_REF peut désigner le tag ou le SHA correspondant à la version de cette documentation afin
de conserver un POC reproductible. Sa valeur par défaut, main, convient pour suivre la version de
développement courante.
Le script active le mount kv en KV v2 s'il n'existe pas. S'il existe déjà dans une autre version,
le script échoue explicitement au lieu de poursuivre avec des routes incompatibles. Il écrit ensuite
deux entrées de démonstration :
| Chemin | Consommateur | Forme attendue |
|---|---|---|
kv/apps/todo-list/mongodb |
VaultSecretAdapter |
champ unique value |
kv/rekipe/tenants/client-a |
VaultTenantResolver |
champs uri et db_name |
Il peut être rejoué : les mêmes valeurs sont réécrites dans une nouvelle version KV. Pour un POC strictement hors ligne, copier le contenu affiché ci-dessous au lieu de le télécharger :
Résoudre le secret applicatif¶
Installer l'extra Vault, puis remplacer le resolver YAML de la section MongoDB par la capability CLI
secrets/vault :
| Bash | |
|---|---|
Le fichier versionné config/secrets.yaml ne contient que le resolver et le mapping :
| YAML | |
|---|---|
Prouver que le service Arclith charge le secret sans afficher sa valeur :
| Bash | |
|---|---|
Lancer ensuite le service avec la même configuration ; son bootstrap effectue la même résolution :
| Bash | |
|---|---|
Dans un autre terminal, curl -fsS "http://127.0.0.1:8120/v1/todos/?page=1&per_page=20"
doit répondre sans erreur de secret. MongoDB doit toujours être démarré comme indiqué au début de
cette page.
VaultSecretAdapter intervient une fois au chargement de la configuration. Le chemin mappé doit
exposer un champ value; il convient aux secrets partagés par l'instance du service.
Résoudre des coordonnées par tenant¶
Cette sous-partie requiert elle aussi l'extra arclith[vault]. L'installer si la sous-partie
précédente n'a pas été suivie, puis configurer séparément tenant/vault avec le catalogue CLI
courant :
| Bash | |
|---|---|
Dans une API multitenant complète, le pipeline d'authentification extrait tenant_id d'un JWT signé
et appelle le resolver. Pour vérifier ici le contrat Vault sans ajouter de logique métier ni simuler
un JWT, appeler directement le port avec l'identifiant de démonstration :
Le résultat attendu est Tenant résolu : todo_client_a. Contrairement au resolver de secrets
applicatifs, VaultTenantResolver lit tous les champs du chemin tenant, les place dans une tranche
AdapterTenantCoords et peut les mettre en cache. L'activation réelle du repository multitenant
requiert aussi multitenant=true et le pipeline JWT décrit dans la capability
tenant.
Réinitialiser et diagnostiquer¶
| Bash | |
|---|---|
Erreurs fréquentes :
permission denied: vérifier queVAULT_TOKENcorrespond au lancement dev courant ;no handler for route: relancerscripts/seed-vault.shpour activer le mount KV v2 ;- secret applicatif non résolu : vérifier le champ
value, le mapping et l'extraarclith[vault]; - tenant introuvable : vérifier le préfixe
rekipe/tenantset l'identifiantclient-a; - container redémarré : le stockage dev est en mémoire, il faut donc rejouer le seed.
Références : serveur Vault en mode dev et configuration d'un mount KV v2.
Ajouter Keycloak localement¶
Ce POC ajoute une identité locale au service Todo sans dépendre du fournisseur de production. Le
realm fourni crée un client public Swagger avec PKCE S256, un rôle licence, deux utilisateurs de
test et un claim tenant_id. Tout est jetable : arrêter le container supprime le realm, les comptes
et les clés de signature.
Télécharger le realm de développement¶
Depuis la racine du projet Todo, télécharger la même révision que celle de la documentation. Un tag
ou un SHA peut remplacer main pour figer le POC :
Le fichier arclith-local-realm.json contient exclusivement
des données de démonstration :
| Élément | Valeur locale | But |
|---|---|---|
| realm | arclith-local |
issuer isolé de la production |
| client public | todo-swagger |
Authorization Code + PKCE S256 |
| audience | todo-api |
validation du token par JWTDecoder |
| rôle realm | rekipe:licensed |
validation par RoleLicenseValidator |
alice |
mot de passe arclith-dev-only |
rôle licence et tenant_id=client-a |
bob |
mot de passe arclith-dev-only |
utilisateur valide sans licence |
Ces mots de passe sont publics, limités au container local et ne doivent être réutilisés nulle part.
Démarrer Keycloak avec l'import¶
La commande lie Keycloak à l'interface loopback et monte le realm en lecture seule dans le répertoire d'import officiel :
Le compte admin sert uniquement à inspecter ce serveur jetable. Attendre que le document de
découverte du realm soit disponible :
| Bash | |
|---|---|
L'endpoint JWKS annoncé par ce document, et utilisé par Arclith, doit être :
| Text Only | |
|---|---|
Configurer l'authentification et la licence¶
Installer l'extra d'authentification, puis utiliser les capabilities CLI courantes :
Le CLI génère les deux fichiers inbound attendus :
| YAML | |
|---|---|
Dans src/todo_list_service/adapters/inbound/fastapi/register.py, protéger toutes les routes du
router Todo avec la dépendance construite par Arclith :
| Python | |
|---|---|
Cette dépendance vérifie la signature RS256 via le JWKS local, l'audience todo-api, l'expiration et
le rôle rekipe:licensed. Elle produit aussi le schéma OAuth2 utilisé par Swagger UI.
Relier le claim au POC Vault¶
Si la section Vault précédente a été suivie, son entrée kv/rekipe/tenants/client-a correspond déjà
au claim signé de alice. Configurer le resolver avec le même nom de claim :
| Bash | |
|---|---|
Le smoke ci-dessous vérifie que le token porte bien tenant_id=client-a. Pour un repository
multitenant: true, remplacer la dépendance auth seule par le pipeline complet
make_inject_tenant_uri décrit dans la documentation multitenant :
le resolver utilisera alors ce claim signé pour lire l'entrée Vault, sans accepter de tenant libre
depuis un header ou une URL.
Tester Swagger et la route protégée¶
Démarrer l'API, puis ouvrir impérativement l'URL déclarée dans le client :
| Text Only | |
|---|---|
Cliquer sur Authorize, conserver les scopes openid profile, puis se connecter avec alice et
le mot de passe local. Swagger exécute le flux Authorization Code avec PKCE et injecte le Bearer
token pour appeler GET /v1/todos/. bob peut s'authentifier mais reçoit 403, car son token ne
porte pas le rôle licence.
Pour un smoke CLI déterministe, le realm active aussi le Direct Access Grant sur ce seul client de développement. Ce flux par mot de passe ne remplace pas PKCE et ne doit pas être repris en production :
Le script fourni vérifie sans afficher les tokens le document de découverte, le JWKS, le rejet d'un
flux Authorization Code sans challenge PKCE, l'audience, le rôle et le claim tenant. Avec
TODO_API_URL, il vérifie aussi les statuts de la route réelle :
Le second appel attend 401 sans token, 200 avec alice et 403 avec bob.
Tokens courts, rotation et nettoyage¶
Le realm fixe la durée de vie des access tokens à cinq minutes. Un client interactif renouvelle son token via le flux OIDC ; un traitement asynchrone ne doit jamais persister ni transporter le JWT brut. Keycloak renouvelle ses clés au redémarrage de ce container éphémère : redémarrer aussi l'API du POC pour vider son cache JWKS. En production, conserver les anciennes clés jusqu'à l'expiration des tokens déjà émis et coordonner la rotation avec le TTL du cache JWKS.
| Bash | |
|---|---|
Erreurs fréquentes :
invalid redirect_uri: ouvrir Swagger viahttp://127.0.0.1:8120, pas vialocalhost;Invalid audience: conserveraudience=todo-apiet vérifier le mapper du client importé ;Clé JWKS introuvableaprès relance : redémarrer l'API locale pour vider son cache en mémoire ;403 Licence invalide ou absenteavecalice: vérifierlicense.role=rekipe:licensedet le realm effectivement importé ;- claim tenant absent : vérifier le mapper
tenant-idet l'attribut de l'utilisateur dans le realm.
Références : container Keycloak et import au démarrage, endpoints OIDC Keycloak et authentification Arclith.
Ajouter OpenTelemetry localement¶
LangSmith observe surtout les runs LLM et LangGraph. OpenTelemetry sert à tracer le service comme un microservice classique: requêtes HTTP, spans, latences et erreurs techniques.
Démarrer Jaeger¶
Pour ce POC, l'image Jaeger all-in-one regroupe le Collector OTLP, le stockage temporaire et l'UI. Elle ne crée ni compte distant ni volume Docker : arrêter le container efface les traces du labo.
| Bash | |
|---|---|
Attendre que l'API de requête Jaeger réponde, puis ouvrir son UI :
| Bash | |
|---|---|
| Text Only | |
|---|---|
Les ports utiles ici sont 4318 pour OTLP HTTP et 16686 pour l'UI. Le runtime Todo s'exécute sur
l'hôte et envoie donc ses traces à http://127.0.0.1:4318. Arclith ajoute automatiquement le chemin
OTLP /v1/traces pour le protocole http/protobuf.
Configurer le projet Todo¶
Depuis la racine du projet généré, ajouter l'adapter avec les paramètres du catalogue courant :
| Bash | |
|---|---|
Le profil development active les traces à 100 %. metrics=false évite d'envoyer des métriques à
Jaeger, qui sert ici de backend de traces. Le CLI active opentelemetry dans
config/adapters/adapters.yaml et génère notamment :
| YAML | |
|---|---|
Pour identifier ce lancement dans les ressources OpenTelemetry, exporter la variable avant de démarrer l'API :
| Bash | |
|---|---|
Produire et retrouver une trace¶
Dans un second terminal, appeler une route métier. /health, /ready et /metrics sont exclus de
l'instrumentation par défaut et ne conviennent donc pas à ce smoke :
| Bash | |
|---|---|
Le runtime exporte par lot. Après quelques secondes, vérifier sans dépendre de l'UI que Jaeger a indexé le service et au moins une trace :
| Bash | |
|---|---|
La première commande doit contenir todo-list-service; la seconde doit afficher un entier supérieur
ou égal à 1. Dans l'UI Jaeger, sélectionner ce service, cliquer sur Find Traces, puis ouvrir la
trace dont l'opération est GET /v1/todos/.
Arrêter, relancer ou réinitialiser¶
| Bash | |
|---|---|
Erreurs fréquentes :
port is already allocated: arrêter le processus ou le container qui utilise déjà16686,4317ou4318, puis relancer Jaeger ;connection refusedsur4318: vérifierdocker ps --filter name=arclith-jaegeretdocker logs arclith-jaeger;- service absent dans Jaeger : appeler une route métier non exclue, attendre le prochain export par
lot et vérifier
signals.traces.enabled, le sampling et l'endpoint ; - export
404sur/v1/metrics: conservermetrics=falsepour ce POC Jaeger, ou utiliser un OpenTelemetry Collector configuré avec un backend de métriques ; - projet lui-même dans Docker :
127.0.0.1désigne alors son propre container ; placer le service et Jaeger sur le même réseau Docker et utiliser le nom du service Jaeger à la place.
Le smoke Docker est volontairement manuel : la construction de la documentation ne suppose pas un
daemon Docker disponible en CI. La commande a été vérifiée avec l'image épinglée, l'export OTLP HTTP,
l'API Jaeger et une trace FastAPI portant le service todo-list-service.
LangSmith ou OpenTelemetry ?¶
Les deux sont complémentaires:
| Besoin | Outil |
|---|---|
| comprendre les messages envoyés au modèle | LangSmith |
| voir pourquoi l'agent repose une question | LangSmith Studio |
| mesurer les appels HTTP et les erreurs techniques | OpenTelemetry |
| corréler plusieurs microservices | OpenTelemetry |
Pour ce tutoriel, LangSmith aide à apprendre l'agent. OpenTelemetry prépare la suite production. Pour travailler sans internet, garder LangSmith désactivé et suivre Validation IA locale et hors ligne.
Tout valider¶
Résultat attendu:
| Text Only | |
|---|---|
Étape précédente: ajouter un agent.