La passerelle HTTP
La même passerelle sert MCP en Streamable HTTP : un service réseau
mutualisé au lieu d’un processus par agent. Chaque initialize ouvre une
session, avec sa propre instance du serveur enveloppé, sa propre chaîne
d’audit et sa propre identité ; le jeton arrive à chaque requête dans
l’en-tête Authorization, là où le SSO d’entreprise le place. Supprimer la
session ferme sa chaîne ; le ledger la scelle ensuite comme n’importe quel
autre WAL, sous <chain-id>-<session>.
Le lancer
Section intitulée « Le lancer »cargo run -p obsign-proxy -- \ --http 127.0.0.1:8080 \ --policy /tmp/demo/policy-bundle.json \ --trusted-keys /tmp/demo/trusted-keys.json \ --identity-bundle /tmp/demo/identity-bundle.json \ --wal /tmp/demo/wal --chain-id demo --env prod \ -- ./target/debug/mock-mcp-serverOuvrir une session et l’exercer :
TOKEN=$(cat /tmp/demo/token.jwt)SID=$(curl -si http://127.0.0.1:8080/mcp \ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' \ | tr -d '\r' | awk 'tolower($1)=="mcp-session-id:" {print $2}')
curl -s http://127.0.0.1:8080/mcp \ -H "Authorization: Bearer $TOKEN" -H "Mcp-Session-Id: $SID" \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"delete_production_db","arguments":{}}}'# → isError: refused by policy, recorded, never reached the server
curl -s -X DELETE http://127.0.0.1:8080/mcp -H "Mcp-Session-Id: $SID"# → the WAL now holds the complete session (chain demo-$SID),# ready for an obsign-ledger pass to sealPourquoi du HTTP nu, et ce que cela exige
Section intitulée « Pourquoi du HTTP nu, et ce que cela exige »La couche HTTP est écrite à la main sur std::net, sans exécutif asynchrone
ni framework web. La liste des dépendances fait partie du produit, et le
sous-ensemble de HTTP/1.1 dont ce transport a besoin est plus petit que
l’arbre de n’importe quel framework. Le HTTP entrant ne touche pas à
l’invariant « aucun appel réseau », qui interdit les dépendances sortantes
(récupération de JWKS, allers-retours vers le ledger) : identité et
politiques arrivent toujours sous forme de fichiers signés.
Le transport est en HTTP nu par le même argument (aucune pile TLS dans l’arbre auditable), et le jeton bearer ne doit donc jamais traverser un réseau en clair : TLS se termine dans un reverse proxy placé devant, et la passerelle n’écoute que là où TLS se termine.
Les configurations nginx et Caddy testées, le contrat du proxy (mise en
tampon SSE, délais d’attente, liste d’Origin autorisées) et les sondes pour
contrôler un déploiement :
TLS devant la passerelle.