Dépôt central des sauvegardes du parc Ocade RAG
Parc
0 instance
Disque du serveur
— Go
/ — Go
—— Go libres · purge à 75 % · refus à 85 %
Empreinte des backups
—
0 backup stocké · — du disque
Chargement...
Ce guide décrit l'ensemble du système : comment les trois applications (Licence, Backups, RAG Interface) se parlent, ce qu'est réellement un backup, et comment dépanner. Il est volontairement détaillé — gardez-le sous la main.
Le système repose sur trois applications qui partagent une même base Supabase (licence-rag.supabase.autocade2.fr). La table licences de cette base est la source de vérité du parc : qui existe, qui est actif.
| Brique | Rôle |
|---|---|
| LICENCE RAG | Serveur de licences : qui a le droit d'utiliser le produit (active / suspended / revoked / expired). Signe ses réponses (clé privée Ed25519). |
| BACKUPS RAG | Serveur de backups (celui-ci). Reçoit les dumps, les stocke sur disque, monitore le parc, gère rétention et alertes. Réutilise la table licences et la même clé de signature que Licence RAG. |
| RAG INTERFACE | Le produit déployé chez chaque client (pilote + 1 instance par client). Contient la base à sauvegarder et l'agent de backup embarqué. |
Pourquoi une base partagée ? Pour ne pas dupliquer l'annuaire du parc : créer une licence dans Licence RAG la fait apparaître automatiquement ici. Zéro synchronisation à maintenir.
INSTANCE CLIENTE (RAG INTERFACE, chez le client)
Base Supabase locale
▲ pg_dump (l'agent dumpe SA base)
┌────┴───────┐
│ Agent │─(1) /api/verify ──────► LICENCE RAG « licence valide ? »
│ embarqué │─(2) /api/agent/poll ──► BACKUPS RAG « backup dû ? ordre ? »
│ │ (réponse SIGNÉE Ed25519)
│ │─(3) /api/agent/upload ► BACKUPS RAG envoie le dump compressé
│ │─(4) /api/agent/report ► BACKUPS RAG signale un échec
└────────────┘
│
BACKUPS RAG (sur le VPS Ocade)
• lit la table licences (authentifie l'agent)
• écrit les métadonnées (backup_runs, policies, orders…)
• range le dump sur /data/backups (volume disque)
• cron interne : alertes + purge rétention GFS
│
Base Supabase PARTAGÉE (licence-rag.supabase.autocade2.fr)
tables : licences (commune) + backup_* (backups)Le lien de confiance : Backups RAG signe ses réponses avec la même clé privée (LICENCE_SIGN_KEY) que Licence RAG, et l'agent les vérifie avec la clé publique correspondante, embarquée dans le code de RAG Interface. D'où l'obligation de poser la même LICENCE_SIGN_KEY sur les deux serveurs.
Le serveur Backups RAG ne peut pas se connecter aux instances : elles sont derrière des pare-feu / NAT, et exposer leur PostgreSQL sur Internet serait une faute de sécurité. Le seul canal fiable est l'agent qui appelle le serveur depuis l'intérieur de l'instance. Conséquence concrète : on ne peut pas « pousser » un backup instantané. Un clic « Backup maintenant » dépose un ordre que l'agent récupère à son prochain contact (≤ 10 min). C'est le compromis assumé du pull.
C'est la confusion la plus fréquente. Le poll et le backup sont deux choses différentes :
| Poll (~10 min) | Backup réel (le pg_dump) | |
|---|---|---|
| Ce que c'est | « as-tu quelque chose pour moi ? » | un vrai dump compressé (ex. 132 Mo) |
| Coût | quasi nul (petite requête HTTP signée) | lourd (dump + compression + upload) |
| Fréquence | toutes les 10 minutes | 1 fois par jour (heure planifiée) ou sur ordre manuel |
Le poll de 10 min est juste la granularité de réactivité : au pire un backup planifié (ou votre clic) part avec 10 min de retard. Ce n'est PAS un backup toutes les 10 minutes. À chaque poll, le serveur recalcule « cette instance a-t-elle un backup réussi depuis sa dernière échéance ? » — le calcul est côté serveur pour qu'aucune dérive d'horloge ne fausse le planning.
Backup planifié (automatique, 1×/jour) :
due = true.pg_dump -Fc --schema=public et compresse (gzip)./api/agent/upload.backup_runs au statut ok.Backup manuel (« Backup maintenant ») : le clic crée un backup_order en attente → transmis à l'agent au prochain poll → dump + upload → l'ordre passe done.
En cas d'échec (ex. SUPABASE_DB_URL absente, disque plein, réseau) : l'agent fait jusqu'à 2 tentatives, puis envoie un /api/agent/report. Une alerte rouge apparaît, le run est marqué failed, le prochain cycle réessaie.
Pour ne pas accumuler des téraoctets, on garde beaucoup de backups récents, de moins en moins en remontant. Par défaut, par instance : 7 quotidiens + 4 hebdomadaires + 6 mensuels → environ 6 mois d'historique. Le cron interne purge automatiquement au-delà de ces quotas. Chaque valeur est réglable par instance (panneau « Config » sur sa ligne).
LICENCE_KEY, vérifiée dans la table licences. Une licence révoquée ou expirée ne peut plus envoyer de backup ; une licence suspendue le peut encore (on continue de protéger ses données).Le volume de backups, Supabase et Licence RAG partagent (par défaut) le même disque du VPS. Trois paliers (réglables dans Réglages, défaut 60 / 75 / 85 %) appliquent la règle « les backups cèdent, jamais les licences » :
Parade pérenne : un disque dédié pour les backups (isolation physique) + une rétention plus courte.
Le backup contient la base PostgreSQL (schéma public : chunks, embeddings, conversations, réglages) mais PAS le bucket Storage « documents » (les fichiers PDF / originaux uploadés). Une restauration redonne un RAG fonctionnel mais sans les fichiers sources. Le bucket Storage est prévu en phase 2.
Niveau de sûreté : la phase 1 protège contre la suppression accidentelle, la corruption logique, une migration ratée, la perte d'une instance cliente. Elle ne protège pas contre la perte du VPS Ocade lui-même → la copie hors-site chiffrée (Scaleway) de la phase 2 atteindra le « 3-2-1 ».
| Badge | Signification |
|---|---|
| à jour | un backup réussi récent existe |
| en retard | pas de backup réussi depuis l'échéance (le mécanisme marche, il faut juste un backup) |
| échec | le dernier essai a échoué (voir le message d'alerte) |
| jamais | aucun backup, agent jamais vu (ex. RAG Interface pas encore redéployé chez ce client) |
| non supporté | l'agent tourne mais SUPABASE_DB_URL est absente → pg_dump impossible |
| Pilote | instance qui reçoit les nouveautés en avant-première (notion héritée du système de licence) |
La colonne Agent affiche le dernier contact (« poll il y a X min »). Au-delà d'1 h sans poll, une alerte « agent muet » se déclenche.
Configurables dans Réglages. Elles réutilisent le serveur SMTP déjà configuré dans RAG Platform (Admin → SMTP) — aucun nouveau secret. Sans SMTP configuré, les e-mails sont silencieusement ignorés (best-effort, jamais de blocage). On choisit l'adresse destinataire, un anti-spam (throttle), et quels événements déclenchent un mail : échec de backup, seuils de disque, backup en retard, instance muette.
Sur la ligne de l'instance → « Historique » → Télécharger le .dump.gz, vérifier l'archive (gunzip -t fichier.dump.gz ne doit rien afficher), puis sur le serveur du client :
gunzip backup.dump.gz docker cp backup.dump <conteneur-supabase-db>:/tmp/ docker exec -it <conteneur-supabase-db> pg_restore \ -U postgres -d postgres --clean --if-exists --schema=public /tmp/backup.dump # puis redémarrer l'app RAG INTERFACE (relance migrations + invalide les caches)
--clean --if-exists écrase le schéma public existant. À ne faire que sur une restauration volontaire (ou une base de test). La restauration orchestrée 1-clic (avec snapshot de sécurité automatique avant) est prévue en phase 2.
| Symptôme | Cause | Fix |
|---|---|---|
| Login « Mot de passe incorrect » alors que la valeur semble bonne | Variable Coolify tapée mais pas « Update » → le conteneur garde l'ancienne valeur | Cliquer « Update » sur la variable PUIS Redeploy |
| Login OK mais renvoyé aussitôt au login | ADMIN_SESSION_SECRET absente → pas de cookie de session | Ajouter la variable + Redeploy |
| Toutes les routes en 500 vide (parc vide, réglages plantent) | SUPABASE_URL ou SUPABASE_SERVICE_KEY absente/invalide | Copier les deux depuis Licence RAG (valeurs exactes, clé service_role complète) |
Could not find the table 'public.backup_settings' in the schema cache | SUPABASE_DB_URL (migrations) pointe une base différente de l'API REST | Passer le SQL des CREATE TABLE backup_* à la main dans Studio (base de l'API REST). À terme : aligner SUPABASE_DB_URL sur SUPABASE_URL. |
EACCES: permission denied, mkdir '/data/backups/…' | Le conteneur tourne en nextjs mais le volume appartient à root | L'entrypoint démarre en root, chown le volume, bascule vers nextjs (su-exec) — corrigé dans l'image |
| Statut non supporté sur une instance | SUPABASE_DB_URL absente sur l'app RAG Interface du client | Poser cette variable (« Available at Runtime ») + redéployer l'instance. Format : postgresql://postgres:PWD@supabase-db-…:5432/postgres |
Variables d'environnement de Backups RAG (récapitulatif)
SUPABASE_URL + SUPABASE_SERVICE_KEY : API REST (lecture/écriture des tables). Copier de Licence RAG.SUPABASE_DB_URL : connexion directe pour les migrations. Copier de Licence RAG.LICENCE_SIGN_KEY : la même que Licence RAG (signe les réponses aux agents).ADMIN_PASSWORD + ADMIN_SESSION_SECRET : accès au dashboard (propres à Backups RAG).BACKUP_STORAGE_DIR=/data/backups : où sont rangés les dumps (= le volume monté).BACKUP_DISK_SOFT_PCT / BACKUP_DISK_HARD_PCT : optionnels (défauts 75 / 85).RESTORE_TEST_DB_URL : base Postgres jetable du restore-test (§14) — optionnel ; sans elle, seule la vérif d'archive tourne.Un backup n'a de valeur que s'il est restaurable. Deux étages, automatiques, le prouvent en continu :
Étage 1 — vérification d'archive (tout le parc)
À chaque cycle, le serveur relit les fichiers au repos et, en une passe, recalcule le sha256 (détecte la corruption disque) et déroule un gunzip (détecte une archive tronquée) — en pur Node, sans rien écrire sur disque. Jamais-vérifiés d'abord, puis re-vérif hebdomadaire (anti bit-rot), bornée à quelques fichiers par passage. Résultat : badge ✓ vérifié / ✗ corrompu à côté de chaque backup dans l'Historique.
Étage 2 — restore-test réel (Postgres jetable dédié)
Périodiquement (rotation, 1 instance par intervalle réglable, défaut 7 j), le serveur restaure pour de vrai le dernier dump d'une instance dans une base Postgres jetable dédiée — jamais le serveur des licences (« les backups cèdent, jamais les licences ») :
.dump.gz → réinitialise le schéma public + garantit le type vector (le dump --schema=public y réfère sans l'embarquer) ;pg_restore du dump, puis requêtes sanity (nombre de tables > 0, total de lignes) ;Sérialisé (un seul à la fois), sauté si le disque est au palier critique. Nécessite la variable RESTORE_TEST_DB_URL pointant une base pgvector/pgvector:pg17 (vrai superuser — pas l'image supabase/postgres autonome, qui casse la DDL). Sans cette variable, seul l'Étage 1 tourne.
Un échec (archive corrompue ou restore-test raté) lève une alerte integrity + e-mail (réglable). Le backup suspect n'est jamais supprimé : on alerte, l'opérateur décide. Réglages : Réglages → Test d'intégrité (activer le restore-test, intervalle, e-mail intégrité).