Le blog

Infrastructure

Zéro rétention de prompts : notre test, sa contre-épreuve, ses limites

« Nous ne conservons pas vos données » : facile à écrire, difficile à montrer. Sur notre LLM hébergé à Paris, un marqueur unique traverse la chaîne, puis on le cherche dans les journaux et fichiers. Une contre-épreuve provoque une fuite pour vérifier que le test la voit.

Portrait de Raphaël Prasquier

Raphaël Prasquier

Infrastructure & sécurité applicative

Publié
Lecture
12 min
Un tamis vide au-dessus d’une coupelle lavande ; une seule bille indigo est passée au travers et repose dans la coupelle.
Illustration conçue avec l’IA

Le jour où on était contents de voir un test échouer

Sorties réelles, extraits

Le même test, avec et sans fuite

preuve-zdr.shexécution normale
== 4. Recherche du marqueur sur les deux machines
  absent ia-poc-veilleur : journald, tous services (190 entrées écrites pendant le test)
  absent ia-poc-gpu : journald, tous services (2585 entrées écrites pendant le test)
  absent ia-poc-gpu : /run (513 fichier(s) modifié(s) pendant le test, tous lus)
  absent ia-poc-gpu : /data (0 fichier(s) modifié(s) pendant le test, tous lus)
  …

RÉSULTAT : CONFORME — marqueur ZDR-20261109-0c608fa886ee2d8c introuvable…
preuve-zdr.sh --contre-epreuvejournaux vLLM activés
== 4. Recherche du marqueur sur les deux machines
  absent ia-poc-veilleur : journald, tous services (127 entrées écrites pendant le test)
  TROUVÉ ia-poc-gpu : journald, 4 ligne(s) :
           … [request_logger.py:119] Generated response chatcmpl-ace6…: output: 'ZDR-20261107-0764b9712833da79', finish_reason: stop
  TROUVÉ ia-poc-gpu : / : /var/log/journal/…/system.journal /var/log/syslog
  …

RÉSULTAT : NON CONFORME — 2 point(s) en échec (voir ci-dessus).
Extraits historiques : exécution normale du 9 novembre et contre-épreuve du 7 novembre 2026. Lignes, chemins et identifiants abrégés par « … ». Les verdicts suivent les contrôles alors en vigueur ; l’envoi des statistiques de vLLM n’était pas encore bloquant.

Un samedi de novembre, notre script de vérification a retrouvé un prompt écrit en clair dans le journal d’un serveur. Nous l’avions provoqué exprès : c’était la contre-épreuve. Deux minutes et demie plus tard, configuration rétablie, le même script, avec un nouveau marqueur, ne trouvait plus rien.

Un test qui ne trouve rien ne vaut que s’il sait trouver quelque chose. Ces deux résultats, ensemble, apportent une vérification technique concrète, en complément des engagements contractuels. Cet article décrit comment nous testons la promesse « zéro rétention » (Zero Data Retention, ZDR) de notre LLM hébergé à Paris : ce que nous visons, ce que le test vérifie, ce que nous avons découvert en chemin, et ce qu’il ne peut pas établir.

Définir avant de promettre : zéro contenu, pas zéro métadonnée

Une promesse floue ne se vérifie pas. La nôtre a été écrite avant la première ligne de code :

Aucun prompt ni aucune réponse n’est écrit sur disque ni journalisé, à aucun maillon de la chaîne serveur : moteur d’inférence, passerelle, proxy, système. Seules des métadonnées sont conservées.

« Seules des métadonnées » n’est pas une échappatoire : sans elles, ni facturation ni sécurité. Mais elles doivent être listées, avec leur règle de conservation.

DonnéeOùConservation documentée
Une ligne par requête : date, clé utilisée, chemin, statut, tokens, duréejournal système du veilleur14 jours au plus, sans copie syslog
Allumages et extinctions du GPUjournal système du veilleuridem
Démarrage et débit du moteur, ligne d’accès sans contenujournal système du serveur GPU, en mémoire seulementjusqu’à l’extinction du serveur, 14 jours au plus
Connexions d’administration (SSH)journaux système des deux machinesidem dans le journal système ; registres de connexion (wtmp, btmp, lastlog) hors de cette règle
Métriques des machines (CPU, réseau, disque)Scaleway Cockpit31 jours[1]
Actions sur les machines (allumer, éteindre, modifier)Audit Trail[2] et facturation Scalewayselon la politique de Scaleway

Deux précisions. Le journal identifie une clé, pas nécessairement la personne derrière la requête. Et une métadonnée qui désigne un utilisateur reste une donnée personnelle : le journal système ne la garde donc que 14 jours au plus, sur chaque machine, et le test contrôle cette durée.

La carte des fuites

Carte des fuites

Dix endroits où un prompt peut finir écrit, et ce qu’on y a fait

Entrée publique
  • 01Proxy ou CDN qui déchiffreDNS seul : le chiffrement s’arrête sur une machine que nous administrons
    ✓ contrôlé
  • 02Journal d’accès du reverse proxyCaddy sans directive log
    ✓ contrôlé
Veilleur
  • 03PasserelleUne ligne de métadonnées, jamais le corps
    ✓ contrôlé
  • 05Outils tiersExclus de l’architecture ; le test cherche les agents connus
    ✓ contrôlé
  • 06Supervision de l’hébergeurMétriques machine seulement
    ✓ contrôlé
Serveur GPU et systèmes
  • 04Journaux du moteurDésactivés, options forcées en dernier
    ✓ contrôlé
  • 07SwapAucun, sur les deux machines
    ✓ contrôlé
  • 08Core dumpsapport masqué, valeur du noyau contrôlée
    ✓ contrôlé
  • 09Snapshots et imagesAucun dans notre projet en fr-par-2, selon les API interrogées
    ✓ contrôlé
Hors de notre chaîne
  • 10Votre posteL’agent garde l’historique en local
    ! chez vous

! hors périmètre du test mémoire vive et cache du GPU · accès technique de l’hébergeur · réseau privé en clair entre les deux machines · contenus effacés ou transformés

« Contrôlé » : vérifié par le script à chaque exécution, sur l’infrastructure réelle, dans le périmètre décrit par l’article. Un contrôle ponctuel, pas une garantie définitive.

Un prompt traverse plus de maillons qu’on ne le croit : le poste, la connexion chiffrée jusqu’au veilleur, le veilleur lui-même, le réseau privé, le serveur GPU. Voici dix endroits où il peut finir écrit, et ce que nous y avons fait.

  1. Le proxy ou CDN devant le service. Un proxy qui termine le TLS voit tout en clair. Notre domaine est déclaré chez Cloudflare en « DNS seul », sans proxy[3] : le chiffrement s’arrête sur une machine que nous administrons.
  2. Le journal d’accès du reverse proxy. Caddy n’a aucune directive log : pas de journal d’accès[4].
  3. La passerelle. Le veilleur n’écrit qu’une ligne de métadonnées par requête, jamais un corps. Même le journal par défaut de son serveur HTTP, qui noterait chemins et adresses, est neutralisé.
  4. Le moteur d’inférence. vLLM sait journaliser requêtes et réponses[5]. Nous forçons --disable-log-requests --disable-log-outputs, ajoutés en dernier à la ligne de commande : quand une option apparaît deux fois, la dernière l’emporte[6], et un réglage glissé plus haut ne peut pas réactiver les journaux.
  5. Les outils tiers. Suivi d’erreurs (type Sentry[7]), traces (OpenTelemetry[8]), collecte de journaux : chacun peut capturer un corps de requête « pour aider au débogage ». Nous les avons exclus de cette architecture, et le test cherche les agents connus.
  6. L’observabilité de l’hébergeur. Aucun journal applicatif n’est envoyé à Cockpit, la supervision de Scaleway[1] ; seules ses métriques automatiques existent.
  7. Le swap. De la mémoire écrite sur disque, donc potentiellement des prompts. Aucune des deux machines n’en a.
  8. Les core dumps. Quand un processus plante, Linux peut écrire sa mémoire sur disque[9], prompts en cours compris. C’est là que nous avons eu la surprise du projet.
  9. Les sauvegardes et instantanés. Un snapshot fige tout ce que contient un disque. Les API de Scaleway n’en listent aucun dans notre projet, en fr-par-2.
  10. Votre poste. L’agent de code garde l’historique des conversations en local. C’est hors de notre chaîne, mais pas hors de votre périmètre.

La surprise : apport

Pour interdire les core dumps, la méthode classique tient en une ligne de sysctl : kernel.core_pattern=|/bin/false. Nous l’avions écrite. Elle ne tenait pas.

Sur notre image Ubuntu, le service apport, qui collecte les rapports de plantage, reprenait la main sur kernel.core_pattern au démarrage[10][11]. Or, quand ce réglage confie les dumps à un programme (le | en tête), la limite ulimit -c 0 du processus ne suffit plus à empêcher le noyau de transmettre le dump[9] : c’est le collecteur qui applique ensuite ses propres règles[10]. Nous avons masqué et arrêté apport, puis réappliqué notre configuration sysctl, sur les deux machines.

Et le test contrôle désormais la valeur effective du noyau, pas celle du fichier de configuration. C’est la leçon la plus utile du projet : vérifier la configuration écrite ne suffit pas, il faut vérifier l’état réel.

Le test du marqueur

Le principe : faire traverser toute la chaîne à une chaîne de caractères unique, puis la chercher là où elle aurait pu être écrite.

  1. Un marqueur unique, du type ZDR-20261109-0c608fa886ee2d8c : la date et huit octets aléatoires, pour rendre une collision accidentelle extrêmement improbable.
  2. De vraies requêtes, par l’API publique : une requête classique, une requête en flux (SSE), et une requête avec une clé invalide dont le corps contient aussi le marqueur. On demande au modèle de recopier le marqueur, et on vérifie qu’il l’a fait : la chaîne est dans le prompt et dans la réponse.
  3. Une recherche sur les deux machines. Le journal système écrit depuis le début du test, tous services confondus, interrogé sous forme exportée. Puis les fichiers ordinaires modifiés depuis le début du test, sur les systèmes de fichiers ext4, xfs, btrfs, vfat et tmpfs, y compris /run, /dev/shm et le disque qui porte les poids du modèle. Les noms de fichiers aussi, et le swap s’il en existait.
  4. Résultat attendu : introuvable.

Deux détails, parce qu’un test de fuite peut lui-même fuir ou se tromper :

  • Le marqueur ne figure pas dans la commande SSH : il arrive par l’entrée standard de la session. Les outils de recherche le lisent ensuite par un tube, jamais en argument : il n’apparaît dans la ligne de commande d’aucun processus des serveurs.
  • On laisse aux journaux le temps d’arriver : quelques secondes d’attente, puis journalctl --sync, avant de chercher. Un test trop pressé ne trouve rien, et conclut à tort que tout va bien.

Le 9 novembre, la recherche a porté sur 2 585 entrées de journal écrites pendant le test sur le serveur GPU et 190 sur le veilleur, et sur 513 fichiers modifiés dans /run côté GPU. Marqueur introuvable.

Un point a été corrigé depuis : « tous lus » est ce qu’affiche le script, mais une erreur de lecture pendant la recherche ne le faisait pas échouer. Pour qu’un « conforme » vaille quelque chose, toute erreur d’inventaire, de lecture, de recherche ou de réponse d’API rend désormais le test non concluant ; seul un fichier effacé entre l’inventaire et la lecture est compté à part.

La contre-épreuve : un test qui ne peut pas échouer ne prouve rien

Un marqueur introuvable peut vouloir dire deux choses : rien n’a fuité, ou le test cherche mal. Pour trancher, on active volontairement la journalisation des requêtes de vLLM le temps du test, et on le rejoue. Le contrôle de configuration doit échouer, bien sûr ; surtout, la recherche doit retrouver le marqueur.

Elle l’a retrouvé : quatre lignes dans le journal du serveur GPU, et deux fichiers sur disque (le journal système et syslog). On rétablit la configuration et on rejoue avec un nouveau marqueur : introuvable dans le périmètre examiné. Ce rejeu ne démontre pas l’effacement des traces de la contre-épreuve : elles restent dans les journaux du serveur GPU jusqu’à leur purge. La surcharge de configuration vit dans /run, qu’un redémarrage efface, et le script prévoit de la retirer en sortant, même en cas d’erreur ; une interruption brutale demanderait une intervention.

Notre relecteur a alors soulevé le vrai problème : la contre-épreuve est elle-même une fuite. Pendant sa fenêtre de 5 à 10 minutes, le moteur journalise tout ce qu’il reçoit, y compris les requêtes des autres utilisateurs, et ces journaux restent, en mémoire, jusqu’à l’extinction du serveur GPU. Le script refuse donc de démarrer si une autre clé que la sienne a appelé le service pendant le délai d’inactivité, et signale après coup toute requête étrangère arrivée pendant la fenêtre.

Ce garde-fou a ses limites, et nous les écrivons : il observe les requêtes terminées et il ne bloque pas les requêtes concurrentes. Il utilisait aussi la clé d’un associé : le test a désormais sa propre clé, distincte de celles des utilisateurs. Depuis que nos associés ont leur clé, nous n’avons pas rejoué la contre-épreuve.

Vérifier aussi ce qu’on a exclu

Le marqueur couvre ce qui est écrit sur nos machines. Le même script vérifie des indices cohérents avec notre architecture :

  • le nom de domaine résout (en IPv4) vers l’adresse du veilleur, pas vers un CDN ;
  • le certificat servi au public a la même empreinte que celui stocké sur le veilleur ;
  • l’API de Scaleway compte zéro snapshot et zéro image de disque dans la zone utilisée ;
  • Cockpit n’a ni source de données ni jeton d’envoi créés par nous ;
  • aucun agent connu d’erreurs, de traces ou de journaux ne tourne (Sentry, OpenTelemetry, Promtail, Alloy…) ;
  • pas de swap, core dumps désactivés (valeur effective), Caddy sans journal d’accès, vLLM avec ses deux options en dernier.

Ces contrôles ne valent pas une attestation exhaustive d’absence d’intermédiaire, de collecte ou de sauvegarde. Ils ont pourtant déjà servi. La vérification du 9 novembre a signalé que vLLM envoyait par défaut des statistiques d’usage anonymes à ses développeurs : type de GPU, modèle, réglages, jamais de contenu[12]. Elles sont désormais coupées et contrôlées : le test en fait un échec bloquant, et la vérification rejouée le même jour a constaté la coupure.

Ce que le test ne peut pas établir

Une preuve honnête dit où elle s’arrête.

  • Ce qui n’est pas lisible tel quel. La recherche porte sur la chaîne exacte. Elle ne reconstitue pas un contenu effacé, écrasé, compressé dans une archive, encodé ou fragmenté, ni l’espace libre des disques.
  • Tous les chemins d’erreur. Trois scénarios sont testés, pas toutes les exceptions, déconnexions ou écritures différées possibles.
  • La mémoire vive. Pendant le calcul, prompt et réponse sont en clair dans la mémoire des deux machines, et vLLM garde un cache des échanges récents sur le GPU, jusqu’à éviction ou extinction[13][14].
  • L’hébergeur. Scaleway peut techniquement accéder à la mémoire de ses machines, et le trafic entre le veilleur et le GPU circule en clair sur son réseau privé.
  • Demain. Un administrateur peut réactiver les journaux : la contre-épreuve le démontre. La garantie repose aussi sur un engagement, et sur des vérifications répétées.
  • Les tiers sans contenu, comme Cloudflare pour le DNS ou Let’s Encrypt, qui publie le nom de domaine dans les registres de certificats[15]. Et le poste client.

Pour vérifier par vous-même, il faut des accès d’administration. Un client peut assister à l’exécution ; une vérification indépendante suppose aussi l’accès au script, à la révision testée et aux sorties expurgées.

Quatre garanties qui se complètent

Quatre garanties

Elles répondent à des questions différentes ; aucune ne remplace les autres

1ConfigurationQu’est-ce qui est écrit, envoyé, conservé ?Se vérifie par un test rejouable, à un instant donné.● testé dans notre prototype
2Engagement contractuelQui s’engage, sur quoi, avec quels recours ?Accord de sous-traitance (RGPD, art. 28) dès que des données personnelles sont traitées pour un client.hors prototype
3Audit et qualificationUn tiers indépendant l’a-t-il vérifié ?Audit, ISO 27001, SecNumCloud : à vérifier sur le périmètre exact du service retenu.hors prototype
4Protection matérielleL’opérateur peut-il lire la mémoire ?Mémoire chiffrée et attestation, sur tout le chemin. N’empêche pas une application autorisée de journaliser.hors prototype
Notre test couvre la première garantie. Les trois autres dépendent du contrat, de l’hébergeur et du matériel retenus.

Notre test porte sur la configuration. Il ne remplace pas les trois autres garanties, qui répondent à d’autres questions :

  1. La configuration : ce que décrit cet article, vérifiable par un test rejouable.
  2. L’engagement contractuel : lorsqu’un prestataire traite des données personnelles pour un client, l’accord prévu par l’article 28 du RGPD s’applique[16]. Ce n’est pas une option.
  3. L’audit et la qualification : audit indépendant, ISO 27001, ou qualification SecNumCloud de l’ANSSI. Cette dernière se vérifie sur le périmètre exact du service retenu[17], et ne démontre pas à elle seule l’absence de journalisation applicative.
  4. La protection matérielle : l’informatique confidentielle peut réduire l’accès de l’opérateur aux données en cours de traitement[18], avec une architecture compatible et une attestation vérifiée, sur tout le chemin (terminaison TLS, mémoire, transferts, GPU). Elle n’empêche pas une application autorisée de journaliser.

État avant publication

Ce que la relecture nous a fait ouvrir, et qui conditionne la valeur du prochain « conforme » :

  • La contre-épreuve : avant d’activer les journaux, fermer l’admission des requêtes ordinaires et attendre la fin de celles déjà admises ; n’autoriser qu’une identité de test dédiée ; maintenir la fermeture jusqu’au rétablissement vérifié du moteur. Puis rejouer un cycle complet (normal, contre-épreuve, rétabli) sur une révision identifiée, sorties expurgées à l’appui.

La phrase qu’on s’autorise à écrire

Notre objectif est de traiter prompts et réponses sans les écrire sur disque ni dans les journaux de nos serveurs. Lors du test du 9 novembre 2026, aucun des marqueurs envoyés par l’API n’a été retrouvé dans les journaux et fichiers examinés sur nos deux machines Scaleway, à Paris. Ce résultat vaut pour les scénarios et le périmètre décrits ici ; il ne constitue pas une preuve générale d’absence de conservation.

Si un fournisseur vous promet davantage, demandez-lui comment il le prouve.

Sources

  1. Scaleway — Cockpit - Concepts

    scaleway.com · non daté · consulté le · Documentation vivante, revue le 20 janvier 2026 : données d’observabilité collectées nativement par les produits Scaleway intégrés à Cockpit ; métriques Scaleway gardées 31 jours par défaut.

  2. Scaleway — Audit Trail - Concepts

    scaleway.com · non daté · consulté le · Documentation vivante, revue le 20 octobre 2026 : registre des actions menées sur les ressources Scaleway. Les Instances figurent parmi les produits intégrés (page « Resource integration with Audit Trail », revue le 4 novembre 2026).

  3. Cloudflare — Proxy status

    developers.cloudflare.com · non daté · consulté le · Documentation vivante, mise à jour le 21 avril 2026 : un enregistrement « DNS only » renvoie l’adresse réelle du serveur et ne fait pas passer le trafic HTTP et HTTPS par le réseau de Cloudflare.

  4. Caddy — log (Caddyfile directive)

    caddyserver.com · non daté · consulté le · Documentation vivante : la directive « log » active et configure les journaux d’accès.

  5. vLLM — vllm serve

    docs.vllm.ai · non daté · consulté le · Documentation de la version stable : options --enable-log-requests / --disable-log-requests et --enable-log-outputs / --disable-log-outputs.

  6. Python Software Foundation — argparse — Parser for command-line options, arguments and subcommands

    docs.python.org · non daté · consulté le · Section « dest » : quand plusieurs options partagent la même destination, la dernière donnée sur la ligne de commande l’emporte. vLLM lit sa ligne de commande avec argparse, et chaque paire --enable-… / --disable-… y partage une destination (code source de vLLM v0.33.0, vllm/utils/argparse_utils.py).

  7. Sentry, SDK Python — Options

    docs.sentry.io · non daté · consulté le · Option max_request_body_size : les intégrations capturent les corps de requête HTTP, jusqu’à 10 000 octets avec la valeur par défaut « medium ».

  8. OpenTelemetry — Semantic conventions for generative client AI spans

    github.com · non daté · consulté le · Statut « Development » : instructions, messages des utilisateurs et sorties du modèle ne sont pas capturés par défaut, mais peuvent l’être sur option.

  9. Linux man-pages (man7.org) — core(5) — Linux manual page

    man7.org · publié le · consulté le · man-pages 6.19. Un core dump contient une image de la mémoire du processus ; la limite RLIMIT_CORE ne s’applique pas aux dumps transmis à un programme.

  10. Ubuntu, documentation des contributeurs — Apport

    ubuntu.com · non daté · consulté le · Apport reçoit les core dumps par /proc/sys/kernel/core_pattern et capture le plantage même avec ulimit -c 0.

  11. Launchpad, bogue Ubuntu n° 1080978 — apport forcefully overrides sysctl kernel.core_pattern from values set in /etc/sysctl.*

    bugs.launchpad.net · publié le · consulté le · Rapport de bogue public, au statut « Confirmed », encore signalé en 2018 et 2019 : au démarrage, apport réécrit kernel.core_pattern par-dessus les réglages sysctl.

  12. vLLM — Usage Stats Collection

    docs.vllm.ai · non daté · consulté le · Collecte anonyme active par défaut (matériel, architecture du modèle, réglages), désactivable par VLLM_NO_USAGE_STATS ou DO_NOT_TRACK. Liste des champs vérifiée dans le code de vLLM v0.33.0 (vllm/usage/usage_lib.py) : aucun contenu de requête.

  13. vLLM, documentation de conception — Automatic Prefix Caching

    docs.vllm.ai · non daté · consulté le · Les blocs du cache KV d’une requête terminée restent en cache jusqu’à leur éviction (politique LRU).

  14. vLLM — Optimization and Tuning

    docs.vllm.ai · non daté · consulté le · vLLM réserve son cache KV dans la mémoire du GPU (« vLLM pre-allocates GPU cache »).

  15. Let’s Encrypt — Certificate Transparency (CT) Logs

    letsencrypt.org · non daté · consulté le · Page mise à jour le 4 septembre 2026 : Let’s Encrypt soumet tous les certificats qu’il émet aux journaux Certificate Transparency.

  16. CNIL, texte du règlement (UE) 2016/679 (RGPD) — CHAPITRE IV - Responsable du traitement et sous-traitant

    cnil.fr · non daté · consulté le · Article 28, paragraphe 3 : le traitement par un sous-traitant est régi par un contrat ou un autre acte juridique.

  17. ANSSI — FAQ avant de se lancer dans la qualification SecNumCloud

    cyber.gouv.fr · non daté · consulté le · La qualification porte sur des offres de service (IaaS, PaaS, CaaS, SaaS), recensées dans la liste des produits et services qualifiés, pour trois ans.

  18. Confidential Computing Consortium — Confidential Computing Consortium

    confidentialcomputing.io · non daté · consulté le · Page d’accueil : l’informatique confidentielle protège les données pendant leur traitement ; l’attestation rend cette protection vérifiable.

Le scan gratuit

Et chez vous,
qu’est-ce qui est exposé ?

Nous examinons ce que vos équipes et prestataires livrent avec l’IA : code, infrastructure et accès aux données. Un humain relit chaque constat.

Demander le scan gratuit

Scan gratuit. Des failles détectées ? Le rapport détaillé est payant.