Résidence des données
Où une installation Tale auto-hébergée garde ses données, comment tu fixes les défauts du déploiement au moment du déploiement, et comment une organisation pointe son corpus de connaissances et ses fichiers téléversés vers sa propre infrastructure — à chaud, sans redémarrage.
10 min de lecture
Une installation Tale auto-hébergée tourne sur une infrastructure que tu contrôles déjà : par défaut, ses données vivent donc sur tes hôtes. La résidence des données couvre le cas où un magasin doit vivre à un endroit précis — le texte des documents dans une base que ton équipe exploite, les fichiers téléversés dans ton propre bucket S3, le corpus d’un locataire isolé de tous les autres. Tale y répond à deux niveaux : les défauts du déploiement, que chaque organisation partage et que tu fixes par variables d’environnement au moment du déploiement, et les connexions par organisation, qu’un admin d’org gère à chaud dans Paramètres > Résidence des données.
Cette page couvre ce qui vit où, comment relocaliser les défauts du déploiement, le prérequis qui mord (ParadeDB), et les chemins par organisation pour les connaissances et le stockage d’objets — y compris le déplacement des fichiers existants d’une organisation.
Où vivent les données du déploiement
Trois magasins, chacun avec sa variable d’environnement. Une variable non définie veut dire « utilise le conteneur fourni » : une installation neuve sans surcharge ne change donc pas.
- Base de connaissances — le corpus de connaissances : métadonnées des documents, texte extrait des fragments, embeddings, index BM25, cache sémantique et pages web crawlées. Elle arrive sous la forme du conteneur fourni
knowledge-db(tale_knowledge, avec les schémasprivate_knowledgeetpublic_web) et c’est le magasin dont la plupart des exigences de résidence se soucient, parce qu’il contient le contenu de tes documents.KNOWLEDGE_DATABASE_URLpointe le backend vers un Postgres géré à toi ; la base peut démarrer vide — le backend crée ses schémas au premier accès. - Stockage de fichiers — où vivent les fichiers téléversés (les blobs d’origine). Par défaut, ils sont dans le store d’objets fourni (le service
object-store, sur son propre volume). Ce store se configure autrement que les deux bases : au premier démarrage seulement, le backend écrit le défaut du déploiement à partir des variablesOBJECT_STORE_*dans$TALE_CONFIG_DIR/default/object-storage/connection.json(plusconnection.secrets.jsonpour les clés d’accès, chiffré avec SOPS quand une clé est configurée) ; ensuite il ne lit plus que ce fichier, jamais les variables — et un fichier déjà présent n’est jamais écrasé. Fixe doncOBJECT_STORE_*sur un bucket externe compatible S3 avant le premier démarrage si tu veux commencer là ; pour déplacer le défaut d’un déploiement qui tourne, éditeconnection.jsonetconnection.secrets.jsonà la main, puis déroule les conteneurs backend. Dans les deux cas le basculement est greenfield : les blobs déjà écrits dans le store fourni ne sont pas copiés — copie d’abord le volume dans le bucket hors bande, et lis Backups et restauration, car un défaut repointé sort les blobs des snapshots detale backup. - Base de données applicative — le magasin opérationnel derrière les agents, les runs et le log d’audit (le conteneur fourni
db, la basetale_app).DATABASE_URLla relocalise ; le nom de base vauttale_apppar défaut (surcharge avecAPP_DB_NAME).
Les variables vivent dans le .env du déploiement. DATABASE_URL et KNOWLEDGE_DATABASE_URL sont lues à chaque démarrage des conteneurs backend — change-en une, puis déroule avec tale deploy (blue-green sans interruption) ou docker compose restart backend-api backend-worker ; les variables OBJECT_STORE_* ne comptent qu’au premier démarrage, comme décrit plus haut. Chaque variable, son défaut et sa forme exacte sont dans la référence des variables d’environnement. Rien dans l’app n’écrit ces valeurs : les versions précédentes avaient, dans Paramètres > Résidence des données, une section de magasins au niveau du déploiement qui enregistrait un bloc dataStores dans deployment.yml — mais aucun chemin de démarrage ne le lisait. Cette section a disparu ; un bloc dataStores resté dans un deployment.yml existant est ignoré et retiré au prochain enregistrement du fichier.
Note : la base de connaissances et la base applicative sont deux instances Postgres distinctes — déplacer l’une ne touche pas l’autre. Relocaliser la base de connaissances déplace le texte extrait et les embeddings ; les fichiers téléversés d’origine ne bougent que si tu relocalises aussi le stockage de fichiers.
Le prérequis ParadeDB
La base de connaissances utilise deux extensions Postgres : vector (pgvector) pour les embeddings et pg_search (ParadeDB) pour la recherche hybride plein texte/BM25. Un Postgres de connaissances externe — le défaut du déploiement ou celui d’une organisation — doit tourner sous ParadeDB (qui embarque les deux) pour une recherche de pleine qualité. Si tu le pointes vers un Postgres ordinaire qui n’a que pgvector, l’indexation et la recherche vectorielle fonctionnent toujours, mais la recherche hybride se dégrade en vectoriel seul : la branche BM25 est sautée en silence. Le bouton Tester la connexion par organisation signale la disponibilité de pgvector et de pg_search, tu le vois donc avant de t’engager ; pour le défaut du déploiement, vérifie les extensions sur la base cible avant de changer KNOWLEDGE_DATABASE_URL.
Bases de connaissances par organisation
Le défaut du déploiement est partagé par chaque organisation. Une organisation seule peut au contraire pointer son propre corpus de connaissances vers un Postgres que tu provisionnes pour elle, pendant que toutes les autres orgs gardent le knowledge-db fourni. Réserve cela aux cas où le contenu documentaire et web-crawlé d’un locataire doit résider sur une infrastructure isolée du reste — une exigence de résidence plus stricte que ce que le défaut du déploiement satisfait.
L’intégralité du corpus de connaissances de l’org se déplace — les deux schémas : private_knowledge (métadonnées des documents, texte des fragments, embeddings et cache sémantique) et public_web (les pages de sites web du crawler, leur texte de fragments et les embeddings). Rien dans la base de connaissances d’une organisation n’est partagé avec une autre organisation.
La connexion vit dans le répertoire de configuration propre à l’organisation :
$TALE_CONFIG_DIR/<orgSlug>/knowledge/connection.json— hôte, port, base, utilisateur et sslmode.$TALE_CONFIG_DIR/<orgSlug>/knowledge/connection.secrets.json— le mot de passe, chiffré avec SOPS dès qu’une clé age SOPS est configurée (voir Secrets avec SOPS).$TALE_CONFIG_DIR/<orgSlug>/knowledge/embedding.json— le modèle d’embedding de l’organisation : fournisseur, identifiants stockés optionnels, tag du modèle, largeur des vecteurs et URL de base optionnelle compatible OpenAI.
Le même prérequis ParadeDB s’applique. L’org valide sa base candidate avec un test de connexion à l’échelle de l’organisation qui signale la disponibilité de pgvector et pg_search avant de basculer ; une cible avec seulement pgvector réduit la recherche de cette org au vectoriel seul. La base peut démarrer vide — Tale crée les schémas private_knowledge et public_web au premier accès, tu n’appliques donc jamais les migrations de base à la main.
Ce chemin retombe sans risque. Une organisation sans connection.json garde le knowledge-db par défaut du déploiement exactement comme avant, la fonctionnalité ne change donc rien pour les orgs qui n’y adhèrent pas. Deux organisations qui pointent vers la même base partagent un seul pool de connexions et un changement par org ne demande aucun redémarrage de conteneur : la prochaine requête de cette org est routée vers sa propre base.
Paramètres > Résidence des données est exactement cette surface par organisation : un propriétaire ou un admin de l’organisation y lit et y écrit ces fichiers, avec le même test de connexion avant de basculer. Les fichiers JSON sur le disque restent la source de vérité — un opérateur qui préfère les éditer à la main n’a besoin d’aucune étape UI.
Le modèle d’embedding de l’organisation
La recherche de connaissances demande un réglage de plus par organisation avant de pouvoir tourner : le modèle d’embedding — quel fournisseur et quel modèle transforment documents et requêtes en vecteurs, et à quelle largeur exacte. Sans lui, l’indexation et la recherche refusent avec une erreur actionnable plutôt que de deviner un modèle. Règle-le dans la section Modèle d’embedding de Paramètres > Résidence des données (ou écris embedding.json à la main) : choisis un fournisseur pour lequel des identifiants sont stockés, nomme le tag du modèle comme le fournisseur l’écrit, et déclare la largeur que produit le modèle — elle n’est jamais déduite du nom du modèle, parce qu’une mauvaise supposition écrit des vecteurs que la recherche ne peut silencieusement plus exploiter.
La largeur est fixée par base de données à l’écriture du premier vecteur. Sur le knowledge-db partagé du déploiement, toutes les organisations doivent donc s’accorder sur une largeur ; une organisation qui veut un autre modèle d’embedding à une autre largeur est exactement le cas de la base de connaissances dédiée ci-dessus.
Stockage d’objets par organisation
Le même schéma par organisation couvre les fichiers téléversés. Une organisation seule peut pointer ses propres blobs de fichiers — documents du Knowledge Hub, pièces jointes de chat, audio et médias générés — vers un bucket compatible S3 que tu provisionnes pour elle (AWS S3, MinIO, Cloudflare R2, …), pendant que toutes les autres orgs gardent le défaut du déploiement. Le bucket est dédié à cette organisation ; rien de ce qu’il contient n’est partagé avec une autre.
La connexion vit à côté de celle des connaissances, dans le répertoire de configuration de l’organisation :
$TALE_CONFIG_DIR/<orgSlug>/object-storage/connection.json— région, endpoint optionnel (pour MinIO/R2), indicateur path-style, bucket et un préfixe de clé optionnel.$TALE_CONFIG_DIR/<orgSlug>/object-storage/connection.secrets.json— la paire de clés d’accès, chiffrée avec SOPS dès qu’une clé age SOPS est configurée (voir Secrets avec SOPS).
Ce chemin n’est pas réservé aux installations neuves : dès que la configuration existe, les nouveaux téléversements vont dans le bucket de l’org, tandis que les fichiers stockés avant restent lisibles là où ils sont — les références mixtes sont prises en charge, tu peux donc basculer à tout moment. Les fichiers stockés plus tôt restent dans le store d’objets du déploiement jusqu’à ce que tu les relocalises avec le backfill de blobs ci-dessous. Si tu supprimes la configuration, les nouveaux téléversements retournent au défaut du déploiement ; les fichiers déjà écrits dans le bucket y restent, mais Tale ne peut plus les lire tant que la connexion n’est pas rétablie. Aucun redémarrage n’est nécessaire, dans un sens comme dans l’autre.
Les admins d’org gèrent aussi cette connexion dans Paramètres > Résidence des données ; son test de connexion effectue un aller-retour réel écriture-lecture-suppression contre le bucket avant que tu t’engages. Comme pour la connexion des connaissances, les fichiers JSON restent la source de vérité.
Autorise l’origine de l’app dans la politique CORS du bucket. Les téléversements et les téléchargements passent directement du navigateur au bucket via des URL présignées : le bucket doit donc accepter les requêtes cross-origin depuis l’URL de ton déploiement — autorise cette origine avec les méthodes
GET,PUTetHEADet tous les en-têtes de requête (Cloudflare R2 : Settings > CORS Policy du bucket ; AWS S3 et MinIO : la configuration CORS du bucket). Le test de connexion dans l’app s’exécute côté serveur, pas dans le navigateur — une politique CORS manquante ne se montre donc que plus tard, sous la forme d’un téléversement échoué.
Déplacer les fichiers pré-existants dans le bucket
Connecter le bucket ne réachemine que les nouveaux téléversements ; les blobs écrits avant la connexion restent dans le store d’objets par défaut du déploiement et continuent de fonctionner via les références mixtes ci-dessus. Pour amener aussi cet historique sur ta propre infrastructure — tout l’intérêt de la résidence des données — lance le backfill de blobs : il déplace chaque blob pré-existant dans le bucket de l’org — la copie arrive avec son content type d’origine, est vérifiée contre la taille de la source, et c’est seulement ensuite que la copie source est supprimée. Rien n’est réécrit : un blob garde sa clé pendant le déplacement, et les lectures le trouvent dans le store qui le détient.
Un admin d’org le lance depuis l’UI : une fois la connexion au bucket enregistrée, la section Stockage d’objets de Paramètres > Résidence des données affiche Déplacer les fichiers existants — confirme, et le déplacement tourne en arrière-plan pendant que les téléversements continuent ; une ligne de statut dans la même section rapporte la progression et l’issue du dernier lancement.
Le backfill est idempotent et limité à l’org : il ne déplace que les blobs de cette organisation, saute tout ce qui est déjà dans le bucket, et laisse chaque source en place tant que sa copie n’est pas vérifiée — un nouveau lancement après une interruption reprend donc sans risque et achève tout déplacement coupé entre la copie vérifiée et la suppression de la source. Il parcourt chaque table qui détient des références de blobs : les documents et leur historique, les fichiers téléversés, l’audio de synthèse vocale et les transcriptions de liens vidéo. Il exige que la connexion au bucket soit déjà configurée, et refuse de tourner quand le bucket de l’org est le store du déploiement lui-même — il n’y aurait rien à déplacer, et achever un déplacement supprimerait la seule copie. Ce n’est délibérément pas une migration de framework versionnée — il tourne à la demande, par organisation, quand tu choisis de relocaliser l’historique d’un locataire, pas à une frontière de version.
Les défauts du déploiement et leurs variables sont listés dans la référence des variables d’environnement ; les sidecars de secrets par organisation suivent Secrets avec SOPS.