NotariumDocumentation
Version de la documentation: latest
FR

Sauvegarde et restauration

Dans Notarium, la sauvegarde est une commande intégrée à l'image, et non une copie de fichiers prise de l'extérieur. notarium backup assemble un ZIP logique pendant que le service continue de répondre : la lecture reste disponible d'un bout à l'autre, et l'écriture n'est retenue que le temps de deux brefs points de contrôle. Il ne vous faut rien de plus que Docker et un conteneur en marche — aucune interruption de service.

Ne copiez jamais un meta.db à chaud

cp /data/meta.db n'est pas une sauvegarde. La base de métadonnées tourne en mode WAL : des lignes déjà validées peuvent encore se trouver dans meta.db-wal, et des fichiers copiés un à un ne forment pas un instantané pris à un même instant. Une instance restaurée depuis une telle copie perd des données en silence.

Effectuer une sauvegarde

docker compose exec -T notarium backup > notarium-20260731.zip

C'est une vraie sauvegarde, pas un test « est-ce que ça marche » : la commande construit l'archive, la passe à son propre vérificateur, et ce n'est qu'ensuite qu'elle en laisse sortir les octets. L'option -T est obligatoire : sans elle, Compose alloue un pseudo-terminal, or un flux binaire n'en ressort pas intact — l'archive serait corrompue. Un simple docker exec, lui, n'alloue aucun pseudo-terminal par défaut : l'option y est inutile. La sortie standard est strictement réservée aux octets du ZIP ; la progression et le récapitulatif final partent sur stderr et ne contaminent jamais l'archive.

Pour une tâche planifiée

La redirection > cache un piège : le fichier portant le nom définitif est créé avant que la commande n'ait fait le moindre travail. Si Docker ou la sauvegarde s'arrête en cours de route, il vous reste un fichier correctement nommé au contenu incomplet. Voici une séquence de publication sûre en cas de panne : écrire dans un fichier temporaire, le forcer sur le disque, puis le publier par un lien physique atomique.

backup="notarium-$(date -u +%Y%m%dT%H%M%SZ).zip"
partial="${backup}.partial.$$"
set -eu
umask 077
committed=0
cleanup() { test "$committed" -eq 1 || rm -f "$partial"; }
trap cleanup EXIT
docker compose exec -T notarium backup > "$partial"
sync -f "$partial"
sync -f "$(dirname "$backup")"
committed=1
ln "$partial" "$backup"
if sync -f "$(dirname "$backup")"; then
  if rm "$partial"; then
    sync -f "$(dirname "$backup")" ||
      echo "backup warning: final is durable; partial cleanup fsync failed" >&2
  else
    echo "backup warning: final is durable; retaining recovery partial $partial" >&2
  fi
else
  echo "backup warning: final is visible; retaining durable recovery partial $partial" >&2
fi
trap - EXIT

set -e empêche la publication de l'archive si Docker ou la sauvegarde a renvoyé une erreur. Le nom temporaire porte le PID : deux tâches lancées en même temps ne se disputeront pas le même fichier. Le fichier temporaire et son répertoire sont forcés sur le disque avant le point de publication, et la publication elle-même est un lien physique atomique qui n'écrase jamais rien : deux tâches visant le même nom cible ne peuvent pas s'écraser l'une l'autre.

Les échecs survenant après le point de publication sont des avertissements, pas de fausses alertes : le fichier définitif est en place et, dans les cas ambigus, le fichier temporaire est conservé comme copie de secours. Même un code de sortie non nul et ambigu de ln le préserve — le lien a pu être créé juste avant l'interruption du processus. umask 077 rend l'archive lisible par son seul propriétaire. Gardez le fichier temporaire et le fichier définitif sur le même système de fichiers.

Pour un conteneur démarré par un simple docker run sous le nom notarium, tout ce qui précède reste valable — une seule ligne change :

docker exec notarium backup > "$partial"
Si le conteneur voit déjà votre répertoire de sauvegardes

L'enveloppe ci-dessus existe parce que le transport par stdout ne sait pas publier un fichier tout seul. Lorsque le répertoire des archives est déjà monté — un partage de sauvegarde, du NFS, un volume temporaire à côté d'un système de fichiers racine en lecture seule —, backup --output /path/archive.zip fait le même travail : il écrit dans un fichier temporaire à côté de la cible, le force sur le disque, le vérifie, puis le publie par un lien physique atomique qui n'écrase jamais rien ; en cas d'échec, il ne laisse rien derrière lui sous le nom cible. Aucune enveloppe shell n'est alors nécessaire, et stdout ne porte qu'un unique récapitulatif JSON.

La sauvegarde exige un conteneur en marche

La commande se lance par docker exec dans le conteneur qui sert déjà les requêtes — et non dans un conteneur séparé : pour prendre un instantané cohérent, il faut se coordonner avec l'application en fonctionnement.

Quand une sauvegarde peut échouer

La sauvegarde est conçue pour que vous ne vous retrouviez jamais en silence avec une archive incohérente : si l'instantané ne peut pas être pris, la commande se termine en erreur et ne publie rien. Deux cas où cela se produit :

  • Un flot ininterrompu de modifications. Les données doivent rester immobiles le temps que l'archive s'assemble ; une écriture qui vient s'y superposer déclenche une nouvelle tentative, et sous un flot sans fin de modifications la commande abandonne en erreur. En pratique, cela se voit sur une instance très sollicitée — relancez simplement plus tard.
  • Un import ou un export long est en cours. La sauvegarde a besoin de deux très courtes pauses dans l'écriture, et une tâche de longue haleine n'y tient pas. Ne planifiez pas une sauvegarde dans la même fenêtre qu'un import massif.

Dans les deux cas, le service n'en souffre pas : la file d'écriture est libérée immédiatement, et la commande d'exploitation ne retient jamais l'application. La lecture ordinaire, elle, reste de toute façon disponible pendant toute la durée d'une sauvegarde.

Ce que contient l'archive

Chemin dans l'archiveDe quoi il s'agit
data/meta.dbComptes, sessions, appartenances, identifiants stables, historique des versions et état des tâches.
data/spaces/La source de vérité Markdown, y compris la mémoire de l'agent et les fichiers marqueurs des projets.
data/jobs/Artefacts terminés et téléversements d'import durables.
manifest.jsonVersion du format, horodatage, ensemble exact des répertoires, plus la taille, la mtime et le SHA-256 de chaque fichier.

Le répertoire dérivé data/engine/ n'y figure pas : les index sont reconstruits à partir des fichiers après une restauration. Parmi les fichiers incomplets, seuls les fichiers internes sont écartés — les fichiers temporaires de l'écriture atomique des notes, les imports téléversés à moitié et les fragments d'artefacts d'export. Les fichiers utilisateur ordinaires dont le nom se termine par .part restent dans l'archive : ils sont légitimes.

L'archive est une donnée sensible

Le ZIP contient les comptes et l'état des sessions issus de la base de métadonnées. Traitez-le comme un secret : le umask 077 de l'extrait ci-dessus rend toute nouvelle archive lisible par son seul propriétaire.

Vérification

La vérification ne change rien, et sa place est dans chaque tâche de sauvegarde :

docker compose exec -T notarium backup verify < notarium-20260722.zip

# pour un simple docker run :
docker exec -i notarium backup verify < notarium-20260722.zip

En cas de succès, la commande imprime un unique récapitulatif JSON et sort avec le code zéro. Elle rejette les chemins dangereux ou en double, les fichiers absents du manifeste, un ensemble de répertoires qui ne correspond pas exactement, les écarts de taille et d'empreinte, les métadonnées temporelles invalides, les dépassements de limites et un contrôle d'intégrité SQLite en échec. Le répertoire de données en service, lui, n'est ni lu ni modifié au passage.

Les sommes de contrôle ne sont pas une signature

Les empreintes attrapent une corruption accidentelle, mais elles ne protègent pas d'une falsification : quiconque peut remplacer à la fois le contenu et le manifeste passera la vérification. Considérez le stockage des sauvegardes comme un état de confiance à accès restreint, ou ajoutez une signature ou un chiffrement dans la couche qui transporte l'archive.

Restauration

La restauration est une opération hors ligne de reprise après sinistre. Elle n'accepte qu'une racine des données vierge et vide ; jamais elle ne fusionne avec une instance existante ni ne l'écrase.

Préparez un volume neuf et faites-y pointer le service avant de démarrer la restauration :

set -eu
docker compose stop notarium
# mettez l'ancien volume de côté ; montez un /data vide dans compose
docker compose run --rm --no-deps -T notarium restore \
  < notarium-20260722.zip
docker compose up -d --force-recreate --no-deps notarium

Ensuite, le conteneur doit être recréé, et pas seulement démarré : docker compose start relancerait l'ancien conteneur avec son ancienne configuration de montage. Conservez l'ancien volume tant que vous n'avez pas contrôlé l'instance restaurée.

La restauration vérifie l'archive entière avant d'installer quoi que ce soit. Si le processus est interrompu en pleine installation, il laisse derrière lui un marqueur explicite : considérez cette cible comme à usage unique et restaurez dans une nouvelle cible vide, plutôt que de forcer le passage ou de fusionner.

Ce qu'il faut contrôler après une restauration :

  1. Vous connecter avec un compte issu de la sauvegarde.
  2. Ouvrir quelques espaces et vérifier que les adresses et les identifiants sont intacts.
  3. Ouvrir une note que vous aviez modifiée et regarder son historique.
  4. Contrôler les tâches d'import et d'export dont les téléversements ou les artefacts comptent pour vous.
Compatibilité avec le schéma de la base de métadonnées

La base de métadonnées que vous restaurez doit porter un registre des migrations que le build cible accepte. Une base non vide sans registre refuse de continuer — la restauration ne devine pas sa version et n'inscrit pas d'elle-même l'entrée du registre. Voir Base de données.

Limites

La commande intégrée ne prend en charge que la disposition canonique : une racine des données unique et une base de métadonnées dans un fichier SQLite. Si META_DB_URL pointe vers Postgres, ou si les notes vivent hors de DATA_DIR, la commande refuse de continuer au lieu de vous remettre une archive partielle — servez-vous alors des outils propres à votre base de données, complétés par des instantanés des répertoires montés.

Les fichiers intermédiaires d'une sauvegarde et d'une vérification vivent par défaut dans /tmp. Une sauvegarde en flux se vérifie elle-même avant de publier : elle peut donc avoir temporairement besoin de la place de l'archive plus deux étapes décompressées ; une vérification isolée demande l'archive plus une. La restauration met le flux entrant en tampon dans le répertoire temporaire, mais décompresse l'archive directement dans la nouvelle racine des données. Si le système de fichiers racine du conteneur est monté en lecture seule, ou si votre jeu de données est volumineux, faites pointer NOTARIUM_BACKUP_TMPDIR vers un répertoire monté accessible en écriture.

L'entrée, compressée comme décompressée, est plafonnée à 64 Gio et un million d'entrées ; les noms, les structures internes du ZIP et manifest.json disposent d'une limite mémoire distincte de 32 Mio. Les grandes installations de confiance peuvent relever NOTARIUM_BACKUP_MAX_BYTES, NOTARIUM_BACKUP_MAX_ENTRIES et NOTARIUM_BACKUP_MAX_METADATA_BYTES — voir Variables d'environnement.

Ensuite

  • CLI de l'image — le contrat complet des commandes, des flux et des codes de sortie.
  • Base de données — ce que contient précisément la base de métadonnées et pourquoi on ne peut jamais la laisser hors d'une sauvegarde.
  • Production — reverse proxy, invariant de l'instance unique, exploitation au quotidien.