# Acte — persistance des messages de librodrome.org (volume nommé sur /app/data) Classe : déploie (un volume créé et semé sur sonic, puis le push de `main`, qui met en ligne sans étape de test) Projet : librodrome Date : 2026-10-26 **État : PRÉPARÉ** (26/10/2026 vers 18h, par la session de diagnostic ; ni figé ni armé) ## Constat (diagnostic du 26/10/2026, en lecture seule) | Fait | Preuve (26/10/2026, sauf date indiquée) | |---|---| | La prod tourne sur sonic | `getent ahostsv4 librodrome.org` → 161.97.174.60 (sonic, `harness/conventions/infra.md`) | | Un seul conteneur : `lpk-librodrome-main-app-1`, image `lpk-librodrome-main-app` (`sha256:8fe50a5b7c65…`), créé le 2026-09-29T06:59:03Z | `docker ps -a`, `docker inspect` | | Répertoire de travail `/app` ; le serveur lit et écrit `process.cwd()/data`, donc `/app/data` | `docker inspect` (`WorkingDir=/app`) ; `server/utils/content.ts`, `server/plugins/seed-messages.ts` | | Montages : deux binds seulement, `…/LPK/librodrome/data → /src/data` et `…/LPK/librodrome/docker/public → /src/public`, aucun volume nommé | `docker inspect` (`.Mounts`, `.HostConfig.Binds`) | | Les sources de ces binds sont des dossiers vides de root sous `/woodpecker/src/git.open.us.org/LPK/librodrome/`, créés par le démon le 2026-08-12 à 00h58m36Z, la seconde où s'est terminé le pipeline #55. L'ancien chemin `EHV/librodrome/` est vide lui aussi (créé le 23/03 à 03h10m44Z, fin du #36 à 03h10m45Z). | `ls -la --time-style=full-iso`, `find` sur sonic ; API CI. Mécanisme : la CI lance compose depuis son espace de travail, et un chemin relatif y désigne un chemin de l'hôte qui n'existe pas (déjà noté en 8a38c86, 28/02). | | `/app/data/messages.yml` vit dans la couche du conteneur, et non dans un montage : 1099 octets, daté du 2026-09-29T06:59:05Z, sha256 `c8afc7de…a6b8`, identique à la graine `site/messages.yml`, avec 1 message (celui d'Yvv) | `docker exec … sha256sum`, `stat`, `grep -c` ; `docker diff` → `A /app/data/messages.yml` | | Graine rejouée au premier démarrage du conteneur actuel | `docker logs` : `2026-09-29T06:59:05Z [seed-messages] data/messages.yml initialisé depuis site/messages.yml`, une seule fois. Les redémarrages des 12/10 et 18/10 (×4) ne l'ont pas rejouée : un redémarrage garde la couche, une recréation (un déploiement) la détruit. | | Aucune édition /admin dans le conteneur actuel | `docker diff` : rien sous `/app/site` ni `/app/content` | | `ADMIN_GIT_SYNC=false`, pas de `/app/.git`, et aucun commit `[admin]` dans l'historique | `docker inspect` (variable non secrète) ; `ls -d /app/.git` → absent ; `git log --all --grep='^\[admin\]'` → 0 | | Les secrets `NUXT_ADMIN_PASSWORD` et `NUXT_ADMIN_SECRET` sont définis ; les variables Umami sont vides | `docker inspect`, longueur seulement (aucune valeur relevée) | | 42 déploiements réussis sur `main` du 25/02 au 29/09 (#4 à #57) ; les derniers : #46 le 24/04, #50 le 24/07 (manuel, 10 s : recréation non vérifiée), #55 le 12/08, #56 et #57 le 29/09 | `https://ci.open.us.org/api/repos/1/pipelines` (lecture anonyme, Woodpecker 3.19.0) | **Diagnostic** : la persistance de la prod est cassée, et elle l'a toujours été. Avant le 19/03 (`b9e6b4a`), les messages s'écrivaient dans `site/messages.yml`, dans le conteneur ; depuis, dans `/app/data`, dans le conteneur aussi. Chaque déploiement a donc remis les messages à zéro, puis à la graine à partir du 28/03 (`f633940`). Les éditions /admin de `site/` et `content/` ont subi le même sort, et ne sont jamais revenues dans git. **Pertes** : depuis le 29/09, rien n'a été perdu (aucun message reçu, aucune édition). Avant cette date, ce qui a été perdu n'est pas vérifiable : les conteneurs détruits emportent leur couche, et les journaux n'en gardent pas trace (le journal de l'application commence le 29/09, celui de fabio le 07/10). ## Intention Monter les messages des visiteurs sur un volume nommé `librodrome-data` en `/app/data`, pour qu'ils survivent aux déploiements. Il faut le faire maintenant : au prochain push, tout message reçu d'ici là serait perdu. Le code est prêt sur la branche locale `fix/persistance-messages` (a6ba0a6), et il a été répété en local. ## Avant — prémisses vivantes Sur le poste, dans `/home/yvv/Documents/PROD/DEV/librodrome` (attendu en commentaire) : ```bash git status --short # P1 : vide git fetch -q origin && git rev-parse origin/main # P2 : 90f77da49b1c77faf9807ba48740b0abd22aff46 # sinon ARRÊT : quelqu'un a poussé (pull --rebase, rebaser la branche, refaire le dossier) git rev-parse fix/persistance-messages # P3 : a6ba0a6662d54e6ed42ef26ce9e1411e873a2484 git merge-base --is-ancestor origin/main a6ba0a6662d54e6ed42ef26ce9e1411e873a2484; echo $? # P4 : 0 git diff --name-only origin/main a6ba0a6662d54e6ed42ef26ce9e1411e873a2484 \ | grep -E '(^|/)data/|\.env|\.db$|\.enc$|\.key$|\.pem$|id_rsa|id_ed25519|IDENTIFIANTS'; echo $? # P5 : 1 (rien) curl -4 -s 'https://ci.open.us.org/api/repos/1/pipelines?perPage=3' \ | python3 -I -c 'import sys,json; [print(p["number"], p["status"]) for p in json.load(sys.stdin)]' # P6 : « 57 success » en tête, rien en running ni pending curl -4 -s -o /dev/null -w '%{http_code}\n' https://librodrome.org/api/health # P7 : 200 (server/api/health.ts ne fait que répondre) curl -4 -s https://librodrome.org/api/messages \ | python3 -I -c 'import sys,json; print(len(json.load(sys.stdin)))' # P8 : 1 au 26/10 (lecture seule : server/api/messages/index.get.ts) ``` Pour P8, un autre nombre veut dire que des messages ont été publiés depuis : le noter, il doit se retrouver après. Sur sonic, les prémisses sont vérifiées par le script de l'étape 1, qui s'arrête en les nommant : conteneur en marche, `WorkingDir=/app`, `/app/data` non monté, volume `librodrome-data` absent, dossier de sauvegarde absent, `docker diff` vide sous `/app/site` et `/app/content`. Une édition /admin trouvée arrête l'acte : elle doit d'abord passer dans git, sinon ce déploiement l'effacerait. Contenu sortant (`git log origin/main..a6ba0a6 --stat`, relevé le 26/10) : ``` a6ba0a6 2026-10-26 docker : messages persistés dans un volume nommé monté sur /app/data docker/docker-compose.dev.yml | 3 ++- docker/docker-compose.yml | 16 ++++++++++++++-- 23b1767 2026-10-26 claude.md : fiche et boucle de vérification fusionnées CLAUDE.md | 76 +++… HANDOFF.md | 11 +++… 852a488 2026-09-29 handoff : bloc REPRISE (bascule quota 2026-09-29) HANDOFF.md | 39 +++… 1c7cc45 2026-09-29 actes : og + favicon — exécuté et vérifié en production actes/2026-09-29-og-logo-mise-en-ligne.md | 8 +++++--- ``` Le diff du compose retire deux lignes, les montages morts `../data:/src/data` et `./public:/src/public`, et en ajoute une, `data:/app/data`, plus le bloc `volumes:` (`librodrome-data`, `external: true`). Ce dossier n'est pas dans le push : il est sur `main` en local, et partira avec le déploiement suivant. ## Commandes exactes Depuis le poste, dans `/home/yvv/Documents/PROD/DEV/librodrome`. Les scripts sont dans `actes/annexes/`, et chacun se retire de `/tmp` à la fin. ```bash # Étape 1 — sauvegarde, puis volume créé et semé (sonic ; le conteneur en service n'est pas touché) scp -q -o BatchMode=yes actes/annexes/2026-10-26-persistance-messages-avant.sh yvv@161.97.174.60:/tmp/lb-avant.sh \ && scp -q -o BatchMode=yes actes/annexes/2026-10-26-persistance-messages-veille.sh yvv@161.97.174.60:/tmp/lb-veille.sh \ && scp -q -o BatchMode=yes actes/annexes/2026-10-26-persistance-messages-apres.sh yvv@161.97.174.60:/tmp/lb-apres.sh \ && ssh -o BatchMode=yes yvv@161.97.174.60 'bash /tmp/lb-avant.sh' /tmp/lb-veille.log 2>&1 &'