Runbook — Base de données
Migrations, sauvegardes et restauration de la base Neon d’Orbit.
Ce runbook est pour toi, le fondateur : tout se fait depuis les tableaux de bord (Neon, GitHub,
Vercel, éventuellement Cloudflare). La version agent, avec le détail technique, est la skill
.claude/skills/database/SKILL.md ; l'agent la suit avant
toute modification du schéma.
Rappel des règles (AGENTS.md) : la base d'Orbit est sa propre base Neon ; chaque changement de
schéma est une migration committée ; jamais de db push ; jamais d'action sur la base d'un autre produit.
1. La phase : packages/db/PHASE
Un fichier d'un seul mot dit à l'agent à quel point il doit être prudent.
| Valeur | Quand | Ce que ça change |
|---|---|---|
pre-users | personne d'autre que toi n'a de compte | l'agent peut fusionner les migrations ou proposer une réinitialisation (toujours avec ton GO explicite) |
production | le premier vrai utilisateur s'est inscrit | aucune donnée ne doit jamais être perdue : migrations en plusieurs étapes, sauvegarde avant chaque migration, ton GO à chaque fois |
Passer en production (le jour où tu ouvres les inscriptions, au plus tard à la première
inscription réelle) :
- GitHub → dépôt
orbit.easyconnector.app→packages/db/PHASE→ icône crayon. - Remplace
pre-usersparproduction(un seul mot, rien d'autre). - Commit changes (via une PR si
mainest protégée). - Active les sauvegardes externes (section 4) si ce n'est pas déjà fait, et règle Neon (section 3).
On ne revient jamais de production à pre-users, sauf si tu as volontairement supprimé toutes les
données des utilisateurs et que tu l'écris noir sur blanc à l'agent.
2. Appliquer une migration en production
L'agent prépare la migration dans une PR et termine sa description par un bloc « Base de données — action du propriétaire ». Après le merge :
- Sauvegarde — Neon → projet Orbit → branche de production → Backup & restore →
Create snapshot, nom
avant-<nom de la migration>. (Sur le plan Free, un seul snapshot manuel est possible : supprime l'ancien d'abord, ou crée plutôt une brancheavant-<nom>depuis Branches → Create branch.) - Migration — GitHub → Actions → Deploy production database → Run workflow → branche
main→ Run workflow. - Contrôle — le run doit être vert, y compris l'étape « Verify schema drift ».
- Si le run est rouge : ne relance pas. Envoie le lien du run à l'agent. Il te dira quoi faire (souvent une petite correction SQL à coller dans l'éditeur SQL de Neon, qu'il aura écrite et relue).
En phase production, l'agent découpe tout changement risqué (renommer, supprimer, changer un type)
en plusieurs PR : d'abord on ajoute (ce qui ne casse rien), puis le code utilise la nouveauté, et
seulement à la fin on retire l'ancien. Pourquoi : Vercel met le code en ligne dès le merge, alors que
la migration ne part que quand tu lances le workflow ; et un Instant Rollback Vercel remet l'ancien code
en ligne sans toucher à la base. La base doit donc toujours convenir à l'ancien et au nouveau code.
Ne merge jamais une PR qui dit « nécessite la migration X » tant que X n'est pas passée.
Tester une migration risquée sur une copie (optionnel, conseillé en production)
- Neon → Branches → Create branch depuis la production, nom
test-<migration>, avec une date d'expiration. - Donne à l'agent… rien : il n'a pas accès à Neon. Il te fournit le SQL de la migration ; colle-le dans l'éditeur SQL de la branche de test (vérifie le nom de la branche en haut de l'éditeur), puis les requêtes de vérification qu'il t'aura données.
- Supprime la branche de test.
3. Sauvegardes dans Neon (à régler une fois)
- Historique (restauration à la seconde près) : Neon → projet → Settings → fenêtre d'historique (« history window ») au maximum du plan : 6 h sur Free (fixe), jusqu'à 7 jours sur Launch, jusqu'à 30 jours sur Scale.
- Snapshots automatiques (plans payants) : Backup & restore → Edit schedule → Daily, conservation 35 jours.
- Snapshot manuel avant chaque migration (section 2).
4. Sauvegarde externe (hors Neon)
But : si le compte Neon disparaît, Orbit garde une copie chiffrée ailleurs. Le workflow
Back up production database (.github/workflows/db-backup.yml) fait chaque nuit une copie de la
base, vérifie qu'elle se restaure dans une base vide, la chiffre, puis la range comme artefact
GitHub (et, si tu le configures, dans un bucket Cloudflare R2). Il est éteint tant que tu ne l'actives pas.
Mise en route (une fois, ~20 minutes)
- Clé de chiffrement (le seul moment en terminal) : installe
age(Mac :brew install age; Windows :winget install FiloSottile.age), puisage-keygen -o orbit-backup-key.txt.- La ligne
AGE-SECRET-KEY-…est la clé privée : mets-la dans ton gestionnaire de mots de passe et garde une copie hors ligne, puis supprime le fichier. Sans elle, aucune sauvegarde n'est lisible. - La ligne
# public key: age1…est la clé publique (pas secrète).
- La ligne
- GitHub → Settings → Environments → New environment →
db-backup→ Deployment branches :mainuniquement. Dans cet environnement :- Secret
DB_BACKUP_DATABASE_URL: dans Neon → Connect → désactive Connection pooling → copie la chaîne (l'hôte ne doit pas contenir-pooler). Idéalement celle d'un rôle en lecture seule (l'agent te donne le SQL, voir la skillreferences/backup-restore.md). - Variable
DB_BACKUP_AGE_RECIPIENT: la clé publiqueage1….
- Secret
- GitHub → Actions → Back up production database → Run workflow. Le run doit être vert ; le
résumé affiche « Restore test: OK », le nombre de lignes par table et l'empreinte SHA-256 ; l'artefact
orbit-db-…apparaît en bas de la page. - Pour l'activer chaque nuit : GitHub → Settings → Secrets and variables → Actions → onglet
Variables → New repository variable
DB_BACKUP_ENABLED=true. - (Conseillé en phase
production) Deuxième copie chez un autre fournisseur : Cloudflare → R2 → crée un bucket privéorbit-db-backups→ règle de cycle de vie « supprimer après 90 jours » → crée un jeton API R2 limité à ce bucket, droits Object Read & Write. Ajoute dans l'environnementdb-backuples secretsDB_BACKUP_S3_ENDPOINT(https://<id du compte>.r2.cloudflarestorage.com),DB_BACKUP_S3_BUCKET,DB_BACKUP_S3_ACCESS_KEY_ID,DB_BACKUP_S3_SECRET_ACCESS_KEY(les quatre, ou aucun).
Facultatif : DB_BACKUP_ARTIFACT_RETENTION_DAYS (variable, 30 par défaut) pour la durée de
conservation des artefacts GitHub.
Tous ces noms appartiennent à Orbit. Ne copie jamais la valeur d'un autre produit.
Pourquoi ces choix
- Artefact GitHub par défaut : aucun nouveau compte, bouton de téléchargement sur la page du run.
- Cloudflare R2 en plus : un fournisseur différent de Neon, Vercel et GitHub, sans frais de sortie.
- Pas Vercel Blob : c'est la même plateforme que l'application ; un incident ou une fuite de jeton toucherait la production et ses sauvegardes en même temps.
- Chiffré avec une clé publique : GitHub peut chiffrer, jamais déchiffrer. Seule ta clé privée ouvre une sauvegarde.
5. Restaurer
Choisis l'outil selon l'incident, et préviens toujours l'agent avant de cliquer (il prépare les vérifications) :
| Situation | Quoi faire |
|---|---|
| Une migration ou un bug a abîmé des données, il y a moins que la fenêtre d'historique | Neon → Backup & restore → Restore from history : d'abord sur une nouvelle branche pour regarder, puis soit l'agent te donne le SQL pour recopier seulement les lignes perdues, soit (si tout est à reprendre) restauration de la branche de production |
| Revenir à l'état d'avant une opération prévue | Neon → Backup & restore → le snapshot avant-… → Restore → Multi-step (pour vérifier) ou One-step |
| Plus ancien que l'historique, moins de 35 jours | snapshot automatique, même chemin |
| Le projet Neon est perdu | sauvegarde externe : nouveau projet Neon, restauration par l'agent avec toi (tu tapes la commande de déchiffrement, tu ne colles jamais la clé privée dans une conversation), puis mise à jour de DATABASE_URL et DATABASE_URL_UNPOOLED dans Vercel et dans l'environnement GitHub production |
Une restauration Neon de la branche de production efface tout ce qui a été écrit après l'instant
choisi, sur toutes les bases de la branche. Neon garde l'état d'avant dans une branche
<nom>_old_<horodatage> : on peut annuler en restaurant depuis celle-ci.
6. Exercice de restauration (tous les trois mois, phase production)
- Télécharge l'artefact du dernier run vert de Back up production database et vérifie l'empreinte SHA-256 affichée dans le résumé.
- Neon → crée une branche
exercice-<date>; dans son éditeur SQL :DROP SCHEMA public CASCADE; CREATE SCHEMA public;(vérifie trois fois le nom de la branche). - Avec l'agent : déchiffrement avec ta clé privée,
pg_restoredans cette branche. - Compare quelques comptages avec le résumé du run. Note la date et le résultat.
- Supprime la branche
exercice-<date>.
7. Réinitialiser la base (phase pre-users uniquement)
Tant qu'Orbit n'a aucun utilisateur, l'agent peut te proposer de fusionner les migrations et de repartir
d'une base vide. Il ne le fait jamais sans ton GO écrit. La marche à suivre est
Runbook reinitialiser-la-base. En phase production, c'est interdit.
8. Ce que l'agent ne fera jamais
- toucher la base de production autrement que par le workflow Deploy production database ;
- utiliser
prisma db push, ou réécrire une migration déjà appliquée en production ; - donner un GO à ta place, ou considérer un merge comme un GO pour une réinitialisation ou une restauration ;
- copier un secret d'un autre produit.