Aller au contenu

Données et sauvegarde

Kauze n'utilise pas de base de données. Chaque entité est un fichier JSON dans ~/.cause/, chargé au démarrage du backend et réécrit à chaque modification. Le dossier est monté dans chaque conteneur sous /root/.cause.

Arborescence

Données métier, à sauvegarder

Chemin Contenu Édition manuelle
users/ Un fichier par compte, mot de passe haché. Déconseillée.
patients/ Un fichier par patient. Déconseillée.
consultations/ Un fichier par consultation : pistes, segments, rapports, verrou. Déconseillée. Un fichier illisible est ignoré au démarrage et nommé dans les logs.
act-types/ Référentiel des actes et sous-types. Peuplé au premier démarrage, puis le dossier fait foi. Possible, relu au redémarrage. Préférez l'onglet Actes.
prompts/ Surcharges des prompts par acte, dossier plat. Possible, lu à chaque génération. Préférez l'onglet Templates et prompts.
templates/ Surcharges des templates de rapport vide, préfixés report-. Idem.
pdf-templates/ Modèles d'export PDF : un XHTML et son fichier .compose.json de paramètres. Un HTML déposé sans fichier de paramètres est utilisable mais pas rééditable dans le compositeur.
asr-harvest.json Corrections en cours de comptage pour l'apprentissage. Non.
asr-variants-learned.json Variantes de transcription apprises, injectées au glossaire. Risquée.
dictionary-versions/ Copies horodatées du glossaire à chaque apprentissage. Non, ce sont des archives.
licence.lic Licence signée de cette machine. Lisible, jamais modifiable : toute retouche invalide sa signature. Voir La licence machine. Non. Une nouvelle licence s'obtient auprès de l'éditeur.
licence-grace.txt Compte des heures de la fenêtre de grâce qui suit un refus de licence, scellé. Voir La licence machine. Jamais. Toute retouche, et l'effacement sur un serveur en service, provoquent un arrêt immédiat.
docker-compose.yml, .env Sur un serveur livré : le compose du client et les versions épinglées, posés par le script d'installation depuis le disque de livraison. OLLAMA_MODEL, CAUSE_VERSION et CAUSE_MOTEURS se modifient, le reste non.
.env.backend.overrides Vos réglages de l'application, une ligne par réglage. Voir Votre fichier de réglages. Oui, c'est le seul fichier de configuration qui vous appartienne. Aucune installation ne l'écrase.
letsencrypt/desec.ini Jeton DNS pour le certificat. C'est vous qui le créez.
certs/ Certificat, clé privée et bloc nginx HTTPS. Réécrit par le script.

Caches, retéléchargeables

Chemin Contenu
ollama/models/ Poids du modèle de langue, plusieurs Go.
whisper/ Modèle de transcription.
qwen-asr/ Second modèle de transcription, 4,4 Go sous hub/, posés par l'installation depuis le disque de livraison même tant que le profil qwen dort. Le sous-dossier triton/ est un cache compilé pour la carte de cette machine.
spring-ai-cache/, djl-cache/ Modèle d'embedding du backend.
vllm-cache/ Uniquement si le profil vLLM est activé.

Conservé mais hors sauvegarde

Chemin Contenu
audio/ Les segments audio de toutes les consultations. Conservés sans limite de temps : seuls les fichiers qu'aucun segment ne réclame sont purgés, après 168 heures. Ce dossier grossit donc sans borne et n'entre pas dans la sauvegarde par défaut, car il pèse des dizaines de Go pour une donnée déjà transcrite. Mesuré le 11/10/2026 : 431 Ko par portion de 30 s, soit environ 50 Mo par heure de dictée. Voir Exploitation.
corpus-entrainement/ Le couple audio et transcription figé à la validation d'un rapport, rangé par mois, consultation, piste, puis instant de validation, avec un manifeste par unité. Cette dernière clé rend chaque unité autonome : un médecin qui recommence sa dictée puis revalide produit une seconde unité, et aucun audio ne reste sans description. Destiné à l'entraînement et au fine-tuning. Les audios y sont des liens durs vers audio/ : un seul contenu sur le disque sous deux noms, donc le corpus ne coûte rien tant que l'original existe, et en devient propriétaire quand le balayage retire l'autre nom. Conséquence à connaître : du sous-estime sa taille réelle. Part par l'envoi mensuel vers S3, jamais par la sauvegarde quotidienne.

Transitoires

Chemin Contenu
logs/ Un fichier par jour et par service. Jamais purgés automatiquement.
qrcodes/ QR codes du hotspot et page d'impression. Supprimer un PNG force sa régénération.
sessions/ Créé par le script, inutilisé.
vector-store.json N'existe que si l'ancien enrichissement lexical est activé.
remontee/ File d'attente de la remontée des évaluations et son registre envoyes.json. Son contenu est déjà dé-identifié : voir ci-dessous.

La remontée des évaluations

Active par défaut. Elle envoie une fois par mois, vers le stockage compatible S3 de l'éditeur, un seau commun où votre installation a son propre préfixe, ce que la machine produit d'évaluations et qui ne part dans aucun dépôt : relevés de baseline (baseline-diagnostic/), mesures de charge (mesure-charge/), corpus dé-identifié et ses audios (corpus-entrainement/). Sans elle, une campagne menée chez un client reste sur son disque et se perd. Les variables sont dans Configuration.

Sans ses clés, rien ne part. La file se remplit quand même, un seul avertissement est inscrit au journal du backend, et c'est ce qui rappelle de les poser ; le script d'installation vous les demande, puis redit à la fin de son passage ce qui manque encore. Sur une machine où le corpus d'entraînement existe, la construction du 1er du mois inscrit donc ce corpus en file : ses audios y entrent par lien dur, ce qui ne coûte aucun octet, et le seul coût réel est la copie des JSON et des textes dé-identifiés.

Deux passages, à deux rythmes. Le 1er du mois, la file est construite : chaque fichier nouveau des dossiers sources est dé-identifié puis inscrit dans remontee/, le fichier d'origine n'étant jamais touché. Toutes les cinq minutes, la file est reprise jusqu'au succès, parce qu'un lien d'établissement à 1 ou 2 Mb/s coupe au milieu d'un envoi. Un envoi est inscrit sur le disque avant la première tentative : un redémarrage ou une coupure ne perd rien, et une clé déjà posée ne repart pas le mois suivant.

La dé-identification a lieu à la construction, pas à l'envoi. C'est ce qui garantit qu'un envoi avorté ne laisse jamais de donnée nominative dormir dans la file. Ce qui est retiré de chaque JSON et de chaque texte qui part :

  • les champs dont le nom annonce une identité : nom, prénom, nom complet, identifiant de connexion, date de naissance, téléphone, courriel, adresse, numéro de sécurité sociale, et les champs qui désignent une personne comme validatedBy, createdBy ou patient ;
  • les identifiants techniques, remplacés par une empreinte stable : deux fichiers restent reliables entre eux, sans porter l'identifiant d'origine, y compris dans les clés d'objet ;
  • dans tout texte restant : courriel, téléphone, numéro de sécurité sociale, date au format jour/mois/année, et les noms que l'installation connaît, ceux de ses patients et de ses comptes, quelles que soient la casse et les accents.

Ce qui n'est pas retiré, et qu'il faut savoir : l'audio part tel quel, c'est sa raison d'être, donc une voix et les noms qu'elle prononce partent avec lui. Et un nom de personne inconnue de l'installation, cité dans une dictée, n'est pas reconnu. Un fichier que la remontée ne sait pas relire ne part pas : un JSON illisible, un binaire d'un type qu'elle n'inspecte pas, un fichier plus gros que la limite sont écartés et nommés dans le journal.

Sauvegarder

Un outil est fourni : scripts/sauvegarde/sauvegarde.py, qui n'a besoin que de Python 3.

python3 scripts/sauvegarde/sauvegarde.py sauvegarder --destination /mnt/sauvegardes

Il écrit cause-<horodatage>.tgz et sa somme de contrôle .sha256 à côté, puis relit l'archive qu'il vient de produire : chaque JSON doit s'y analyser et porter un enregistrement. Une sauvegarde qu'on n'a pas relue n'est pas une sauvegarde.

Un fichier déjà perdu n'arrête pas la sauvegarde. Il est écarté, nommé dans le compte rendu, et listé dans un PROBLEMES.txt joint à l'archive pour que le défaut voyage avec elle. Deux cas comptent comme perdus : un JSON illisible, et un JSON réduit à null, qui est du JSON parfaitement valide mais que le backend ignore au démarrage. Seule une source sans aucune donnée métier sauvegardable est un refus franc.

Les codes de sortie servent à une tâche planifiée : 0 tout est propre, 2 archive produite mais avec des réserves, à regarder, 1 rien n'a été produit.

Il affiche ce qu'il a archivé, et surtout ce qu'il a laissé :

/mnt/sauvegardes/cause-20261010-141559.tgz  (246,6 Ko)
  21 consultations, 22 patients, 8 users, 6 act-types, 1 pdf-templates
  somme de contrôle : cause-20261010-141559.tgz.sha256
  non sauvegardé, inconnu de l'outil : consultations.bak-20260514-155515
  ÉCARTÉS, donnée déjà perdue (1), détaillés dans PROBLEMES.txt au sein de l'archive :
    users/00000000-0000-0000-0000-000000000000.json : JSON valide mais aucun enregistrement

Ce qui entre : les dossiers métier du tableau ci-dessus, plus licence.lic, licence-grace.txt, asr-harvest.json et asr-variants-learned.json. Ce qui n'entre pas : les caches, les transitoires, ce qui part par la remontée mensuelle, les artefacts de mesure locaux du banc d'essai (bench-output/, bench-reports/), l'audio (des dizaines de Go pour une donnée déjà transcrite ; --avec-audio le force), et aucune clé privée : ni certs/, ni ssl/, ni letsencrypt/, ni aucun fichier .pem ou .key. Une archive de sauvegarde voyage et se duplique ; une clé privée, non. Ces éléments se régénèrent avec le script de certificat.

licence-grace.txt entre dans l'archive, de sorte qu'un serveur reconstruit sur une machine neuve arrive avec son compteur et obtienne ses soixante-douze heures le temps que l'éditeur émette la licence de cette machine ; sans lui, une installation restaurée refuserait le métier dès le premier démarrage. Vous n'avez rien à en faire : il se réécrit seul à chaque démarrage où la licence est acceptée.

Le compose du client et son .env n'entrent pas non plus : ils viennent du disque de livraison et de l'éditeur, donc une réinstallation les repose à l'identique.

Votre fichier de réglages .env.backend.overrides reste dehors pour une autre raison : il peut porter des clés si vous en écrivez, et un secret n'a rien à faire dans une archive qui voyage. Il tient en quelques lignes : recopiez-les dans le cahier d'exploitation du service. Les deux clés du stockage, elles, vivent dans le fichier généré : sur une machine reconstruite, relancer le script les redemande.

L'outil n'archive que ce qu'il connaît, et nomme le reste dans la ligne « non sauvegardé ». Relisez cette ligne : si un dossier que vous avez créé vous-même y figure, c'est à vous de le sauvegarder à part.

Le backend peut tourner pendant la sauvegarde. Chaque fichier est écrit par un remplacement atomique, un temporaire voisin puis un déplacement d'un seul geste, donc une copie prise au mauvais moment voit l'ancienne version ou la nouvelle, jamais un fichier tronqué.

Vérifier une sauvegarde

python3 scripts/sauvegarde/sauvegarde.py verifier /mnt/sauvegardes/cause-20261010-141559.tgz

À faire périodiquement sur les archives anciennes : un disque se dégrade en silence. La commande compare la somme de contrôle, décompresse et analyse chaque JSON.

Restaurer

docker compose stop backend
python3 scripts/sauvegarde/sauvegarde.py restaurer /mnt/sauvegardes/cause-20261010-141559.tgz
docker compose start backend

La restauration vérifie la somme de contrôle avant d'écrire quoi que ce soit, et refuse toute archive dont un chemin sortirait de la destination ou contiendrait un lien symbolique. La destination par défaut est votre dossier personnel, donc les fichiers reprennent leur place dans ~/.cause/. Les fichiers existants sont écrasés par ceux de l'archive ; ceux qui n'y figurent pas sont laissés en place.

Répétez cette restauration une fois, vers un dossier d'essai, avant d'en avoir besoin :

mkdir -p /tmp/essai && python3 scripts/sauvegarde/sauvegarde.py restaurer <archive> --destination /tmp/essai

Les fichiers de consultation d'une version antérieure sont migrés à la lecture.