# CHANGELOG — Set-OPS ## 2026-09-30 (48) — Chezlepro rasée et reconstruite par les runners : l'état est revenu, à l'identique **La preuve visée** : que (47) remette réellement l'état, sur une vraie reconstruction. **Déroulé** : `make sauvegarder-maintenant` (8 instantanés à 03:06) ; empreintes témoins relevées ; depuis le runner du site `raser` (13/13), `flotte-creer`, `inseminer` ; armement depuis le poste (voûte, clé de voûte, identité SSH du runner reprise de la voûte) ; sur le runner de Chezlepro : `_amorcer-socle`, `deployer-tout`, `valider` — **0 échec**. **Chaque jeu a choisi l'instantané de 03:06**, le dernier avant la naissance des machines (03:12), et l'a remis : | Témoin | Avant | Après | |---|---|---| | racine de l'AC | `F0:32:89:A0:BB:98:79:AC` | identique — la flotte s'est enrôlée auprès d'elle | | `sysadmin` | empreinte `4406f2fa…`, changé le 2026-09-16, pas de changement forcé | identique | | annuaire | 12 entrées | 12 | | Nextcloud | 217 fichiers, `instanceid` `ocytcfy7ss5a` | identique | | clé DKIM | `760208b7…` | identique — l'enregistrement publié reste valable | | Keycloak | 100 tables, 2 utilisateurs | identique | | Nextcloud (base) | 139 tables | 139 | | boîtes | `root` | `root` (+ `testmail`, écrit par `valider`) | **Un défaut trouvé, dans le code d'hier soir** : la mesure « l'annuaire est-il vierge ? » filtrait par `grep -v` — sur un annuaire VRAIMENT vierge, plus aucune ligne, `grep` sort en 1, `pipefail` fait échouer la tâche. Technolibre, déjà peuplée, ne pouvait pas l'exercer. Comptage par `awk`, puis reprise à `deployer-tout` : l'AC et les bases, déjà remises, ont été reconnues « actées » et n'ont pas été rejouées. **Après** : les 8 dépôts passent la garde ; contrôles de dépôt et de restauration rapportés à Icinga ; recette — fédération LDAP authentifiée, passerelle vers Keycloak, vigie, Nextcloud, Dovecot ; `essai.chezlepro.ca` sur `.61` → 200, page absente → la 404 du PSPBT. Pare-feu Proxmox réactivé VM par VM : 13/13, flux identiques avant et après, Icinga à 0 critique. **Ce que ça prouve, et ce que ça ne prouve pas.** Une reconstruction complète d'un écosystème, conduite par les runners, **rend l'état qu'on lui a confié** — la limite qu'énonçait honnêtement (46). Le SITE, lui, n'a toujours jamais été reconstruit ; et chaque reconstruction a encore trouvé un défaut (ici un seul, dans le code neuf). ## 2026-09-30 (47) — La reconstruction remet l'état : chaque rôle propriétaire restaure le sien **Le défaut** (46) : une reconstruction repartait d'un état neuf. Les instantanés se restauraient pour PROUVER qu'ils s'ouvrent, jamais pour être remis en service. **Le principe retenu** : pas d'étape à part dans la chaîne — chaque rôle qui POSSÈDE un état le remet **au moment où il le créerait neuf**, et l'ordre vient des couches. La chaîne des runners (`_amorcer-socle` → `deployer-tout`) et `make reconstruire` n'ont pas changé. | Rôle | Point de remise | |---|---| | `serveur_step_ca` | avant `step ca init` ; refus si la voûte n'ouvre plus les clés restaurées | | `serveur_postgresql` | bases juste créées, chacune rejouée en UNE transaction | | `serveur_openldap` | fin de rôle (le LDIF exige `ppolicy`), puis comptes de service réalignés sur la voûte | | `serveur_nextcloud` | fin de rôle : base + fichiers + config ensemble, `occ upgrade` | | `serveur_rspamd` | avant la génération DKIM — sinon chaque reconstruction changeait la clé publiée | | `serveur_dovecot`, `serveur_forgejo`, `serveur_web_*` | racine encore vide | **Nextcloud se remet en fin de rôle, pas avant l'installation** : le code de `user_oidc` et `richdocuments` n'est pas sauvegardé ; restaurée avant, la base les dirait installés et `occ app:install` refuserait. `serveur_postgresql` exclut donc sa base (et celle d'Icinga, dont l'historique est lié à un environnement non sauvegardé). **Quelle incarnation** : le dernier instantané pris AVANT la naissance de la machine — la date de sa clé d'hôte SSH (`machine-id`, lui, vient du gabarit : tous les nœuds portent le 2026-09-01). Un état en place n'est jamais écrasé d'office ; ce qui est remplacé est mis de côté (`/var/backups/setops-avant-restauration/`) ; chaque jeu laisse un marqueur. **La sauvegarde refuse de déposer tant qu'un état d'avant attend** (code 3). Sans cette garde, `restic forget --keep-daily 7` aurait gardé l'instantané de l'état NEUF et chassé celui d'avant dès qu'ils tombaient le même jour — ce matin, ils étaient à cheval sur minuit par hasard. **Nouveaux gestes** : `make sauvegarder-maintenant` (juste avant `raser`, inscrit au runbook `flotte-refaire`), `make restauration-etat`, `make restauration-renoncer`. Outil de nœud utilisable sans Ansible : `setops-restaurer` (avec des répétitions qui ne touchent à rien : `annuaire --essai`, `base --vers`, `fichiers --vers`). **Garde** : `scripts/tests/test_restauration.py` — chaque détenteur d'état du catalogue doit avoir son point de remise (une liste qui suit une autre prend du retard), et l'outil, rendu depuis son gabarit avec des doublures de `restic`/`psql`, doit choisir l'incarnation d'avant, refuser d'écraser, bloquer puis libérer la sauvegarde, et couper la section d'une base avant le `DROP DATABASE postgres;`. Le test a trouvé un défaut avant tout déploiement : une apostrophe dans `${c:-…}`, que bash lit comme un guillemet ouvrant. **Déployé sur Technolibre** (runner, `deployer-tout`) : 13 hôtes, 0 échec. Chaque jeu vivant reconnu `en_place` (rien écrasé) ; les deux serveurs web, vides, ont pris le chemin `a_restaurer` depuis leur instantané d'avant. `make sauvegarder-maintenant` : les 8 dépôts passent la garde. Répétitions sur les vraies données, sans rien toucher : annuaire à blanc (12 entrées rejouables depuis `71fb5481`), courriel remis dans un répertoire jetable. **La répétition a trouvé un défaut** : `psql` tourne en `postgres`, qui ne lit pas le répertoire jetable de root (0700) — en reconstruction, la base serait restée vide et le déploiement aurait échoué. La section passe désormais par l'entrée standard ; le test l'exige. **Pas encore éprouvé par une reconstruction réelle** : l'épreuve est la prochaine. ## 2026-09-30 (46) — Technolibre : l'état d'avant la reconstruction remis en place, sans reconstruire **Le constat** : après (45), le mot de passe de `sysadmin` était revenu à celui de l'amorçage. La reconstruction n'avait rien restauré — **aucune étape de Set-OPS ne réinjecte les instantanés**. « Restauration vérifiée » (`make valider`) veut dire : restaurée dans un répertoire temporaire et lue, pas remise en service. Chezlepro est dans le même cas (racine d'AC et annuaire nés le 2026-09-13). **Perdu, mesuré contre l'instantané de 23:50** (`restic-tech`) : `sysadmin` (changé le 2026-09-16), racine et intermédiaire de l'AC (nouvelle empreinte), bases Keycloak et Nextcloud, fichiers Nextcloud (197 → 108), une boîte aux lettres. **Remis en place, sur les machines existantes** (l'état neuf mis de côté avant chaque geste, dans `/root/avant-restauration-2026-09-30` de chaque nœud) : - **annuaire** (`idm-01`) : `slapadd -u` à blanc, puis base remplacée ; empreinte du mot de passe = celle d'avant, sans changement forcé. `amorcage_acces` ne touche qu'un compte absent : il ne le réécrasera pas ; - **Keycloak** (`data-sql-01`) : section `keycloak` du `pg_dumpall` rejouée dans une base vide, Keycloak arrêté ; sonde `identite` verte (fédération LDAP authentifiée) ; - **Nextcloud** (`collab-01` + `data-sql-01`) : base, `data/` et `config/` ensemble (les secrets d'instance voyagent avec les données), cache Redis purgé, `occ upgrade` (richdocuments) ; - **courriel** (`infra-mail-01`) : `/var/vmail`. **Le piège du runbook s'est présenté** : la section `nextcloud` se terminait par `DROP DATABASE postgres;`. La découpe coupe désormais aussi sur `DROP DATABASE`, et la garde « hors-périmètre » l'avait vu. **Non remis** : l'AC (aucune confiance extérieure ne s'y rattache — le poste ne fait confiance à aucune des deux racines ; la remettre imposerait de réenrôler les 13 machines) ; `icingadb` (historique de supervision) ; `forgejo` (base orpheline, plus de forge au plan). **Contrôle** : `make valider` depuis le runner de Technolibre, 0 échec ; sondes identité, passerelle, vigie, collaboration, boîtes, tableaux vertes. **Reste à coder** : l'étape de réinjection dans la reconstruction elle-même, dans l'ordre AC → confiance de la flotte → annuaire → bases → fichiers et courriel. Chezlepro n'est pas reconstruite tant qu'elle n'existe pas. ## 2026-09-30 (45) — Technolibre reconstruit depuis le runner du site, puis par son propre runner **La preuve visée** : la limite 2 du jalon de reconstruction autonome — un locataire refait PAR LES RUNNERS, pas depuis le poste. Le runner du site rase, recrée les VM et amorce le runner du locataire (sans aucun de ses secrets) ; le poste l'arme (sa voûte et sa clé, le seul geste humain) ; le runner du locataire déploie et valide sa flotte. **Déroulé** : sauvegarde fraîche des 8 machines qui portent un état (vérifiée) ; `raser` (13 VM, toutes `123…`) et `flotte-creer` depuis le runner du site ; `inseminer` (socle + moteur) ; armement depuis le poste ; sur le runner de Technolibre : amorçage du socle (AC, DNS), `deployer-tout`, `make valider` — **0 échec, valider vert, restaurations vérifiées**. Puis le pare-feu Proxmox VM par VM (procédure éprouvée : 13/13, flux identiques avant et après, Icinga à 0 critique), et la recette. **Quatre défauts trouvés — c'est le rôle de l'épreuve — tous corrigés dans le code :** 1. **L'identité SSH du runner changeait à chaque reconstruction** : il fabriquait sa paire à sa naissance, la flotte naissait avec la clé DÉCLARÉE — refus sur les 13 machines. Chez Chezlepro, la clé déclarée était déjà celle d'un runner disparu. Désormais la clé privée vit dans la voûte du locataire, l'armement la remet, et refuse si sa clé publique n'est pas déclarée au plan (garde éprouvée en défaut). Pour cette passe, les 12 VM encore vierges ont été recréées avec la bonne clé. 2. **`make flux` sur le runner d'un locataire amputait les pare-feux** : sans `underlay.yml` ni plan du site, les paires qui en dépendent ne se résolvaient à rien — ni transfert de zone depuis le DNS public du site, ni collecte de la fabric, ni insémination. Le générateur s'abstient désormais (les règles committées font foi). Vu sur le runner avant tout dommage durable ; les fichiers committés ont été remis pendant le déploiement. 3. **Keycloak : un client absent faisait échouer les mappers de groupes et de rôles** — sous `set -euo pipefail`, `grep` sans correspondance sortait avant la branche « client absent ». Jamais vu tant qu'un client `forgejo` subsistait d'une époque où le locataire avait une forge. 4. **(conception, non corrigé)** Un Icinga neuf marque aussitôt `sauvegarde` et `restauration` critiques (« jamais rapporté ») ; la restauration n'étant vérifiée que le dimanche, elle resterait rouge jusque-là après chaque reconstruction. Ici : sauvegarde, vérification du dépôt et contrôle de restauration déclenchés à la main sur la flotte reconstruite. **Et une garde qui a fait son travail** : le runner a refusé de déployer un génome en retard d'un commit sur la forge du site — publié pendant qu'il travaillait. **Recette** : Proxmox 0 écart ; frontière mesurée conforme ; depuis l'Internet sur `.60` : 404 du frontal, SMTP (bannière de son `edge-mta-01`), 465/587/993 en TLS 1.3 ; zone `technolibre.ca` republiée et reprise par le DNS public du site (nouveau numéro de série). ## 2026-09-29 (44) — La page du PSPBT devient la page 404 du frontal de Chezlepro Précision de l'exploitant sur (43) : c'est le **frontal** qui doit servir cette page, comme **page 404 par défaut** — pas le dorsal comme page d'un site. - **Le rôle `serveur_web_frontal`** accepte une page 404 du locataire (`serveur_web_frontal_page_404`, un chemin dans le dépôt du locataire) : le serveur par défaut la sert, statut 404, à tout nom qu'il ne publie pas ; sans page déclarée, il ferme toujours sans réponse (444). La page est `internal` (pas de lecture directe de `/404.html` : 404 aussi), avec sa propre CSP (styles et scripts en ligne, rien d'extérieur). Le nom inconnu n'obtient toujours aucun service. - **Chezlepro**, puis **Technolibre** à la demande de l'exploitant : `contenus/frontal-404.html` (le fichier `site web.html` de l'exploitant), déclaré dans leurs variables du frontal. Technolibre éprouvé de même sur `69.70.26.60` (racine, chemin quelconque, nom inconnu → 404). - **`essai.chezlepro.ca` revient à sa page de test**, CSP par défaut (la CSP propre au site, posée en (43), est retirée). Éprouvé depuis l'Internet : `http://69.70.26.61/`, un chemin quelconque, `/404.html` et un nom inconnu → 404 avec la page du PSPBT ; `essai.chezlepro.ca` → 200, la page de test. ## 2026-09-29 (43) — `essai.chezlepro.ca` sert la page du « Parti de la Sainte-Paix et du Bon Temps » À la demande de l'exploitant : le fichier `site web.html` (le seul du dossier `parti politique du bonheur national`) devient `public/index.html` du dépôt `essais/essai-web` sur la forge du site (mise à jour par l'API, depuis le runner du site), puis le dorsal de Chezlepro le tire. **Une CSP propre à ce site.** La page porte ses styles et ses scripts dans le HTML (`