
Un proxy transparent de masquage des PII pour le trafic API LLM. Se place entre une application et un fournisseur LLM (actuellement Anthropic), pseudonymisant les données sensibles en sortie et les restaurant en entrée. Construit avec FastAPI + httpx.
Un proxy transparent de réduction des données personnelles pour le trafic API des LLM. Se place entre votre application et le fournisseur de LLM, pseudonymisant les données sensibles à l'aller et les restaurant au retour.
Votre LLM ne voit jamais de vrais noms, e-mails, IP ou domaines — il travaille exclusivement avec des pseudonymes structurés comme [email protected]. Votre application récupère les valeurs originales, de manière transparente.
Lors de l'utilisation de LLM pour des opérations de sécurité, de réponse à incident ou toute tâche impliquant des données clients réelles, vous risquez d'envoyer des données personnelles à des API tierces. Ce proxy résout ce problème en :
# 1. Créez votre configuration
cp config.json.example config.json
# Modifiez config.json avec vos domaines internes, entités connues, etc.
# 2. Lancez avec Docker
docker build -t llm-token-proxy .
docker run -p 8090:8080 -v ./config.json:/app/config.json llm-token-proxy
# 3. Pointez votre application vers le proxy
export ANTHROPIC_BASE_URL=http://localhost:8090/session/my-session/
Voilà. Vos appels API Anthropic passent désormais par le proxy avec les données personnelles réductées.

Flux typique : Application → Token Proxy (réduction des données personnelles) → API LLM (pseudonymes uniquement) → Token Proxy (restauration des originaux) → Application
admin de [email protected])Les pseudonymes sont déterministes au sein d'une session — une même valeur réelle correspond toujours au même pseudonyme.
Lorsqu'un LLM analyse des journaux de sécurité, le fournisseur d'hébergement et la géolocalisation d'une adresse IP comptent — une connexion depuis une IP Hetzner en Allemagne raconte une histoire différente d'une connexion depuis un FAI résidentiel aux États-Unis. Un simple remplacement par des IP de plage de documentation (ex. 198.51.100.x) détruit ce contexte.
Avec la base de données optionnelle MaxMind GeoLite2-ASN, le proxy remplace les IP réelles par une IP différente du même ASN et sous-réseau. Le LLM voit une IP d'apparence réelle qui résout le même fournisseur d'hébergement et une géographie approximative — mais ce n'est pas l'adresse réelle.
10.99.99.x (aucun contexte ASN à préserver)198.51.100.x (plage de documentation)L'IP donneuse est choisie de manière déterministe via HMAC avec un sel par session, de sorte qu'une même IP réelle corresponde toujours au même donneur au sein d'une session, mais différentes sessions produisent des mappages différents.
Le proxy est livré avec un config.json vide — aucune liste de mots intégrée ni hypothèse spécifique au domaine. Le config.json.example inclus est paramétré pour les opérations de sécurité avec Microsoft Sentinel et Entra ID (plus de 8 000 noms de tables/colonnes KQL, termes d'autorisation Graph API, domaines de référence sécurité). Si cela correspond à votre cas d'usage, copiez ce dont vous avez besoin. Si vous utilisez le proxy pour un domaine différent (santé, juridique, finance, etc.), partez de la configuration vide et construisez vos propres listes.
config.json{
"internal_domains": ["yourcompany.com"],
"partner_domains": ["partnercorp.com"],
"internal_ip_ranges": ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"],
"known_persons": ["John Smith"],
"known_orgs": ["YourCompany"],
"known_hostnames": ["DC01", "FS01"],
"ner_enabled": true,
"ner_skiplist": [],
"redaction_enabled": true
}
_internal_)spacy + en_core_web_sm)false, le proxy devient un simple passagefalse, les domaines sont transmis sans modification (les e-mails, IP, noms sont toujours réduits). Utile lorsque les noms de domaine portent un contexte important pour le LLM (par exemple, distinguer outlook.com de protonmail.com) et ne sont pas considérés comme sensibles.Gérez les listes blanches et activez/désactivez la réduction sans redémarrer :
# Voir toutes les listes blanches
curl http://localhost:8090/token-proxy/config/whitelist
# Ajouter des termes à la liste de saut NER (réduit les faux positifs)
curl -X POST http://localhost:8090/token-proxy/config/whitelist \
-H "Content-Type: application/json" \
-d '{"category": "ner_skiplist", "values": ["EvoSTS", "Hetzner"]}'
# Ajouter des domaines à la liste blanche (ne jamais pseudonymiser ceux-ci)
curl -X POST http://localhost:8090/token-proxy/config/whitelist \
-H "Content-Type: application/json" \
-d '{"category": "domain_allowlist", "values": ["github.com"]}'
# Désactiver la réduction (mode passage)
curl -X POST http://localhost:8090/token-proxy/config/status \
-H "Content-Type: application/json" \
-d '{"redaction_enabled": false}'
Catégories de liste blanche : ner_skiplist, domain_allowlist, known_persons, known_orgs, known_hostnames
Inspectez ce que fait le proxy en temps réel :
# Lister les sessions actives
curl http://localhost:8090/token-proxy/sessions
# Voir les mappages de pseudonymes pour une session
curl http://localhost:8090/token-proxy/sessions/{session_id}/mappings
# Voir le journal d'activité de réduction
curl http://localhost:8090/token-proxy/sessions/{session_id}/log
# Rechercher dans les mappages
curl http://localhost:8090/token-proxy/sessions/{session_id}/search?q=admin
# Voir les payloads capturés (ce que le LLM a réellement vu)
curl http://localhost:8090/token-proxy/sessions/{session_id}/payloads
# Utilisation des tokens pour une session (tokens d'entrée/sortie sur toutes les requêtes)
curl http://localhost:8090/token-proxy/sessions/{session_id}/usage
# Statistiques globales (inclut total_tokens sur toutes les sessions)
curl http://localhost:8090/token-proxy/stats
Le proxy enregistre input_tokens et output_tokens pour chaque requête qu'il transmet — à la fois non-streaming (lus depuis l'objet usage de la réponse) et streaming (analysés depuis les événements SSE message_start et message_delta). Comme le proxy se situe entre votre application et le LLM, vous obtenez un point de contrôle unique pour mesurer la consommation de tous les clients qui le partagent, sans instrumenter chacun d'eux.
curl http://localhost:8090/token-proxy/sessions/my-session/usage
# {
# "session_id": "my-session",
# "request_count": 3,
# "input_tokens": 1240,
# "output_tokens": 587
# }
curl http://localhost:8090/token-proxy/stats | jq .total_tokens
# { "input_tokens": 48213, "output_tokens": 19044 }
L'utilisation par requête est également incluse dans /token-proxy/sessions/{session_id}/log sous usage_counts. Seuls les comptages bruts de tokens sont suivis — la tarification est laissée à l'appelant.
Le proxy prend en charge le streaming SSE (stream: true). Les pseudonymes sont restaurés en temps réel à l'aide d'une approche de buffer de queue qui gère les pseudonymes répartis sur plusieurs morceaux SSE.
Le proxy utilise un modèle d'adaptateur de fournisseur. Actuellement, il prend en charge :
/v1/messages)Voir CONTRIBUTING.md pour savoir comment ajouter le support d'autres fournisseurs (OpenAI, Google Gemini, etc.).
en_core_web_sm) détecte les noms de personnes/organisations en anglais. Les noms dans d'autres langues peuvent être manqués à moins d'être ajoutés à known_persons/known_orgs dans la configuration.admin [at] acme.com, numéros de téléphone, adresses physiques) ne seront pas détectées. Le pipeline de détection est paramétré pour les données IT/sécurité structurées./token-proxy/config/* et /token-proxy/sessions/* n'ont pas d'authentification. Le proxy est conçu pour des réseaux internes/de confiance — n'exposez pas ces points de terminaison à des réseaux non fiables.# Installer les dépendances de développement
pip install -e ".[dev,ner]"
python -m spacy download en_core_web_sm
# Exécuter les tests
pytest
# Linting
ruff check token_proxy/ tests/
Apache 2.0 — voir LICENSE.
| Type d'entité | Exemple interne | Exemple externe |
|---|
[email protected] | [email protected] | |
| Domaine | domain-internal-001.com | domain-external-001.net |
| IP | 10.99.99.1 (RFC1918) | IP donneuse basée sur l'ASN (voir ci-dessous) |
| Personne | person_internal_001 | person_external_001 |
| Organisation | org_internal_001 | org_external_001 |
| Nom d'hôte | host_001 | host_001 |
| Variable | Valeur par défaut | Rôle |
|---|
ANTHROPIC_API_BASE | https://api.anthropic.com | URL de l'API Anthropique en amont |
TOKEN_PROXY_CONFIG_PATH | /app/config.json | Chemin vers le fichier de configuration |
LOG_LEVEL | info | Niveau de journalisation |
GEOIP_ASN_DB_PATH | /app/data/GeoLite2-ASN.mmdb | Base de données MaxMind GeoLite2-ASN (optionnelle) |