Backups RAG

Dépôt central des sauvegardes du parc Ocade RAG

Parc

0 instance

0 à jour0 en retard0 jamais

Disque du serveur

Go

/ Go

Go libres · purge à 75 % · refus à 85 %

Empreinte des backups

0 backup stocké · du disque

Parc (0)

Chargement...

Guide complet du fonctionnement — briques, poll ≠ backup, cycle de vie, sécurité, test d'intégrité, restauration, dépannage

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.

1. Les trois briques et leurs rôles

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.

BriqueRôle
LICENCE RAGServeur de licences : qui a le droit d'utiliser le produit (active / suspended / revoked / expired). Signe ses réponses (clé privée Ed25519).
BACKUPS RAGServeur 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 INTERFACELe 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.

2. Comment les trois briques s'articulent

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.

3. Le modèle « PULL » — l'agent appelle le serveur, jamais l'inverse

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.

4. Poll ≠ Backup (la distinction essentielle)

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ûtquasi nul (petite requête HTTP signée)lourd (dump + compression + upload)
Fréquencetoutes les 10 minutes1 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.

5. Cycle de vie d'un backup

Backup planifié (automatique, 1×/jour) :

  1. À 02:00 UTC, l'échéance du jour est franchie.
  2. Au prochain poll (≤ 10 min après), le serveur répond due = true.
  3. L'agent exécute pg_dump -Fc --schema=public et compresse (gzip).
  4. Il calcule un sha256, puis envoie le dump en streaming à /api/agent/upload.
  5. Le serveur vérifie le sha256 et le quota, écrit le fichier, crée un backup_runs au statut ok.
  6. L'instance passe à jour.

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.

6. Rétention « grand-père / père / fils » (GFS)

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).

7. Sécurité

  • Authentification des agents : chaque agent s'identifie avec sa 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).
  • Réponses signées Ed25519 : l'agent rejette toute réponse non ou mal signée (fail-safe) → un faux serveur ne peut ni déclencher de dumps, ni (en phase 2) de restaurations.
  • Téléchargement réservé à l'admin : seuls les boutons du dashboard (session admin) téléchargent un dump. Le volume n'est pas exposé sur le web.
  • Garde anti-écrasement : un upload ne remplace jamais un backup existant (chaque run a un id unique).

8. Saturation du disque

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 » :

  • info (60 %) : simple alerte d'occupation.
  • soft (75 %) : purge GFS agressive (rétention temporairement divisée par deux) pour récupérer de l'espace.
  • hard (85 %) : les uploads sont refusés (507) et les backups reportés par l'agent — sans échec définitif — pour protéger Supabase et le serveur de licences.

Parade pérenne : un disque dédié pour les backups (isolation physique) + une rétention plus courte.

9. Limite de la phase 1 — ce qui n'est PAS sauvegardé

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 ».

10. Les statuts du parc

BadgeSignification
à jourun backup réussi récent existe
en retardpas de backup réussi depuis l'échéance (le mécanisme marche, il faut juste un backup)
échecle dernier essai a échoué (voir le message d'alerte)
jamaisaucun 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
Piloteinstance 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.

11. Notifications e-mail

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.

12. Restauration manuelle (phase 1)

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.

13. Dépannage — pièges rencontrés au déploiement

SymptômeCauseFix
Login « Mot de passe incorrect » alors que la valeur semble bonneVariable Coolify tapée mais pas « Update » → le conteneur garde l'ancienne valeurCliquer « Update » sur la variable PUIS Redeploy
Login OK mais renvoyé aussitôt au loginADMIN_SESSION_SECRET absente → pas de cookie de sessionAjouter la variable + Redeploy
Toutes les routes en 500 vide (parc vide, réglages plantent)SUPABASE_URL ou SUPABASE_SERVICE_KEY absente/invalideCopier les deux depuis Licence RAG (valeurs exactes, clé service_role complète)
Could not find the table 'public.backup_settings' in the schema cacheSUPABASE_DB_URL (migrations) pointe une base différente de l'API RESTPasser 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 à rootL'entrypoint démarre en root, chown le volume, bascule vers nextjs (su-exec) — corrigé dans l'image
Statut non supporté sur une instanceSUPABASE_DB_URL absente sur l'app RAG Interface du clientPoser 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.

14. Test d'intégrité des backups

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éejamais le serveur des licences (« les backups cèdent, jamais les licences ») :

  • décompresse le .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) ;
  • verdict enregistré, base de test nettoyée. Indicateur restauré le JJ/MM ✓ sous le badge de backup de l'instance.

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é).