Commit graph

665 commits

Author SHA1 Message Date
2887b57f0c GUI : les six registres ont un formulaire genere, et la sauvegarde aussi
CHAMPS_ECRITS_A_LA_MAIN est vide. Serveurs et applications, les deux plus
gros, sont passes au generateur — chargement, rendu et sauvegarde.

L EPREUVE QUI COMPTE. Ouvrir chaque vue et enregistrer sans rien toucher
doit renvoyer exactement le plan qu on vient de lire : 14 serveurs, 25
applications, 2 domaines, 4 bases, IDENTIQUE partout. C est ce qui separe
un formulaire genere d un formulaire qui en a l air — un champ visible a
l ecran et perdu en silence a l enregistrement serait le pire des deux
mondes. test_rendu_gui.py le mesure a chaque make prouver.

TROIS DEFAUTS TROUVES EN CHEMIN.

Le formulaire annoncait des defauts INVENTES : 2048 Mo, 2 coeurs, 16G. Il
n existe aucun defaut fixe — deriver_ressources calcule depuis les roles
portes (1024 et 1 pour infra-pki-01, 5632 et 4 pour collab-01). Un repere
faux fait croire qu on connait la valeur. Le schema nomme le champ derive,
et l ecran montre la valeur reelle de cet hote. L option vide d un select
dit desormais ce qu elle produira : « (defaut : asgard) ».

Une SECONDE occurrence du defaut d hier dormait dans sourceDeValeurs :
elle lisait encore data.nomenclature. Elle n avait jamais leve parce que
la vue Serveurs, seule a emprunter cette source, avait un formulaire ecrit
a la main. Elle a leve a la seconde ou le generateur l a prise. Le banc ne
voit que les chemins vivants : verifier_gui.py fait donc aussi une
verification STATIQUE, qui voit ce qui dort.

La validation client s accrochait a data-v, pose a la main sur trois
champs. Le formulaire genere l aurait perdu et la validation serait passee
au vert sur ZERO champ. Le generateur marque chaque controle, et la
sauvegarde refuse si elle n en inspecte aucun.

DEUX CHAMPS GARDENT LEUR EDITEUR, et le schema le dit (x-editeur) : la
matrice des integrations montre les universelles et les exemptions, et
l editeur de liens contraint le role a meta/liens.yml. Le generateur s
efface plutot que de remplacer un editeur qui en sait plus que lui.

LIMITE : je n ai toujours pas ouvert ces pages dans un navigateur.

make prouver : CONFORME, 61 OK, 0 echec, 1 saute.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-08 18:26:33 -04:00
c0ea0c8e71 wiki : publie (formulaires generes, ecritures preservantes)
Some checks are pending
verifier / verifier (push) Waiting to run
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-08 17:56:02 -04:00
935a64a1bf plan : sauvegarder n emportait plus quarante lignes de commentaire
En voulant generer deux formulaires de plus, j ai trouve pire que ce que
je cherchais.

CE QUI ETAIT DEJA LA. Les quatre ecrivains de registre ecrasaient le
fichier au safe_dump. Mesure sur les fichiers reels : domaines.yml 6->3,
applications.yml 27->5, serveurs.yml 18->3. Quarante lignes, detruites
par n importe quel clic sur Sauvegarder dans les vues Serveurs,
Applications ou Domaines. Parmi elles, celle qui explique pourquoi
backup-01 a ete retire, et celle qui dit dans quel ordre les deux roles
du runner s appliquent. C etait l incident du 2026-08-18, jamais corrige
pour les registres du plan. Les quatre passent par _ecrire_registre :
aller-retour a vide identique a l octet, sur les quatre fichiers.

TROIS ECARTS DE SCHEMA, trouves en confrontant le schema aux VALIDATEURS
et non aux seuls plans :
 - edge designe un GROUPE, pas un hote. Le schema disait serveurs : un
   formulaire genere aurait offert une valeur qu aucun hote ne reconnait,
   donc aucun SAN, donc la panne du 2026-08-25 reintroduite ;
 - exposition, entierement valide par le moteur, manquait au schema ;
 - liens etait items: {type: object} — une liste d objets sans forme.
Et mail, offert par la vue Domaines depuis sa creation, decrit ici comme
un booleen, saisi la-bas comme du texte, lu par rien : retire.

P62 garde tout ca. Elle separe l entite du reste mecaniquement : un
validateur lit son entite par des variables LOCALES, les autres registres
par ses PARAMETRES. Controle negatif rejoue.

LES FORMULAIRES. Serveurs de BD et Domaines sont generes, chargement et
sauvegarde compris. Quatre registres sur six. Le generateur a appris la
liste d objets.

LIMITE : restent serveurs et applications, les deux plus gros ; et je n ai
toujours pas ouvert ces pages dans un navigateur.

make prouver : CONFORME, 61 OK, 0 echec, 1 saute (62 preuves).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-08 17:54:49 -04:00
00a8dc5283 wiki : publie, avec la vue Nomenclature
Some checks are pending
verifier / verifier (push) Waiting to run
Temoin de publication mis a jour ; P60 repasse au vert.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-08 17:11:56 -04:00
88033f37b8 GUI : la vue Nomenclature, et deux fautes que mes bancs ne voyaient pas
Some checks are pending
verifier / verifier (push) Waiting to run
LA VUE. La nomenclature etait le seul registre que le GUI ne savait pas
ecrire du tout : ajouter une fonction exigeait d ouvrir le YAML. Elle a
sa vue, et son formulaire est GENERE depuis le schema. Deuxieme registre
sur six. couverture_gui verifier passe : les 28 champs des plans reels
sont editables.

Elle n est pas un registre comme les autres : elle decrit la REGLE dont
VMID, VLAN, adresse et passerelle se derivent. Chaque fonction montre ce
qu elle derive et les VM qui la portent ; l index est montre mais pas
editable, parce qu il est alloue par le site ; valider_nomenclature
refuse de retirer une fonction encore portee, ou de designer une zone
non declaree.

DEUX FAUTES, ET POURQUOI MES BANCS NE LES VOYAIENT PAS.

Le formulaire des bases, livre la veille, etait casse dans un navigateur.
Il lisait data.schema, or il n existe aucun data global : c est une const
locale de charger(). ReferenceError a l ouverture, et zone morte dans
sauvegarderBases. Je l avais eprouve sous node EN LUI PASSANT data : le
banc reproduisait la fonction, pas sa portee. D ou test_rendu_gui.py, qui
charge le JS entier dans un DOM simule et dessine les douze vues, avec son
controle negatif.

Le schema decrivait reservations comme une table de zones ; le fichier
reel est un bloc plat. P61 comparait des NOMS aplatis, donc ne voyait
rien. Elle compare desormais aussi la FORME.

ECRIRE SANS DEPLACER UN COMMENTAIRE. _fusion_chirurgicale remplace le
bloc entier des qu une valeur change : quinze entrees compactes devenaient
42 lignes, et le commentaire du poste d exploitation se retrouvait en tete
du bloc, ou il affirmait que collab etait le poste d exploitation. Un
commentaire deplace n est pas laid, il est faux. _fusion_table edite les
tables ligne a ligne ; le diff fait trois lignes.

Au passage : sort_keys triait le schema, donc l ordre des cases a l ecran
(reserve_max avant reserve_min) ; et _ecrire_index_nomenclature ecrivait
encore par write_text, oubliee au passage des ecritures atomiques.

LIMITE : deux registres sur six sont generes, et je n ai toujours pas
ouvert cette page dans un navigateur.

make prouver : CONFORME, 60 OK, 0 echec, 1 saute.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-08 17:08:45 -04:00
34b7ec3886 schema : le marqueur du champ requis, et l entree de CHANGELOG qui manquait
Some checks are pending
verifier / verifier (push) Waiting to run
Deux dettes des commits 0eaceb1 et 03c628d.

La premiere se voyait a l ecran : le marqueur « requis » se collait au
libelle et se lisait « Base requis », « Secret (Vault) requis ». Il
devient une asterisque discrete portant l infobulle « Champ requis ».

La seconde est une entorse a la regle 5 : les deux commits precedents
ont livre le schema derive et le premier formulaire genere SANS entree
de CHANGELOG. Elle est ecrite ici, avec sa limite dite franchement — un
registre sur six est genere, et le trou de la nomenclature reste ouvert.

make prouver : CONFORME, 60 OK, 0 echec, 1 saute.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-08 16:34:25 -04:00
03c628d5e1 GUI : le formulaire des bases est GENERE depuis le schema
Etape 3, sur un seul registre — `bases_donnees`, le plus simple et le seul ou
observe et editable coincidaient deja. Les cinq autres gardent leurs formulaires
ecrits a la main : on ne bascule pas six vues d un coup.

CE QUI DISPARAIT DU JAVASCRIPT

Huit `<label>` en dur, trois constructions de `<option>`, et la regle qui
choisissait la source du consommateur selon la portee. Cette derniere ne vivait
que dans le JS ; elle est desormais DECLAREE au schema (`x-source-selon`), donc
lisible et gardee.

Le formulaire rend exactement les memes huit champs qu avant — verifie en
EXECUTANT le moteur sous node avec le schema et des donnees reelles, pas
seulement en passant `node --check`.

LA BOUCLE EST FERMEE DES DEUX COTES

Le chemin de SAUVEGARDE enumerait lui aussi les sept champs en dur. Un champ
ajoute au registre serait apparu au formulaire genere et aurait disparu
SILENCIEUSEMENT a l enregistrement — le pire des deux mondes. Il derive
maintenant du schema, valeurs par defaut comprises (`default`).

LA SEPARATION FORME / COHERENCE, MONTREE

  portee=groupe + consommateur APPLICATION   -> REFUSE par valider_bases
  portee=application + consommateur app      -> ACCEPTE
  secret absent                              -> REFUSE

Le schema a rempli la FORME (les defauts `groupe` et `principale` se sont
poses), le validateur a attrape l INCOHERENCE. Aucune de ces trois regles ne
s exprime en JSON Schema, et vouloir l y mettre creerait la seconde source de
verite que ce depot refuse.

CHAMPS_ECRITS_PAR_GUI COMMENCE A DISPARAITRE

Renomme CHAMPS_ECRITS_A_LA_MAIN, et `bases_donnees` en est SORTIE : sa
couverture se derive du schema. P19 lit desormais `champs_ecrits_par_gui()`, qui
reunit les deux. Le jour ou la table sera vide, elle gardera un mecanisme au
lieu d une liste.

UNE GARDE A CORRIGER AU PASSAGE

`declaration_derive()` verifiait que chaque champ declare apparait dans le
SOURCE du GUI. Pour un registre genere il n y apparait plus — c est le but.
Elle aurait crie sur precisement le progres qu elle devait constater. Les
registres generes en sont exemptes : c est P61 qui tient la promesse pour eux.

make prouver : CONFORME, 60 OK, 0 echec, 1 saute.
P07 (node --check), P19 (couverture), P61 (schema) : verts.

Reste : les cinq autres vues, et le trou de la nomenclature — que le passage au
generateur fermera par construction, puisque le schema decrit deja `categorie`
et `service`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-08 15:30:08 -04:00
0eaceb1048 schema du plan : la forme des registres devient derivee, et gardee
Etape 2 du chantier « l UI reflete fidelement la structure ». Le GUI porte
CHAMPS_ECRITS_PAR_GUI, une liste tenue A LA MAIN de ce qu il sait ecrire, que
P19 confronte au reel. C est une copie — gardee, donc honnete, mais une copie :
quelqu un doit penser a l allonger.

`make schema` produit docs/audit/schema-plan.json : six registres, 42 champs,
leurs types, leurs enumerations et ce qui est requis.

CE QUE LE SCHEMA EST, ET CE QU IL N EST PAS

  schema       -> la FORME      -> generera les champs du formulaire
  validateurs  -> la COHERENCE  -> refusent une saisie incoherente

Un JSON Schema ne sait pas dire qu un `consommateur` designe une application
inexistante, ni qu une integration universelle recopiee au plan est un defaut.
Vouloir l y mettre creerait la seconde source de verite que tout ce depot
refuse. Les valider_* restent l autorite.

LES ENUMERATIONS SONT IMPORTEES, JAMAIS RECOPIEES

ETATS_SERVEUR, PORTEES_BD et AUTORITES_DNS viennent des constantes que les
validateurs appliquent. Une enumeration recopiee diverge — c est la lecon des
neuf resolutions d instance que P41 garde depuis.

CE QUE L ETAPE 1 AVAIT TROUVE, ET QUE P61 A CONFIRME

Le recensement montrait `categorie` et `service` presents dans TOUS les plans et
absents de CHAMPS_ECRITS_PAR_GUI, dont la ligne `nomenclature` est vide : le GUI
ne sait pas les editer, l operateur doit ouvrir le YAML. P61 a refuse le premier
schema pour cette raison exacte. Les trois tables imbriquees de la nomenclature
sont donc DECRITES et non resumees en « object ».

31 champs observes dans l instance courante, 42 decrits par le schema. La
difference n est pas du bavardage : observer une instance n est pas un schema.
`noeud`, `stockage` et `coeurs` sont legitimes et simplement inutilises ici — un
schema derive de l observation les INTERDIRAIT.

P61, EPROUVEE DANS LES DEUX SENS

  fichier genere perime                -> REFUSE
  champ du plan absent du schema       -> REFUSE
  champ decrit mais inutilise au plan  -> COMPTE, pas refuse

Le troisieme point est delibere : refuser obligerait a retirer du schema un
champ valide des que plus personne ne s en sert. Meme mesure que les lacunes
nommees de P29.

UN DEFAUT DE MON INSTRUMENT, PAYE EN ROUTE

P61 comparait des noms a plat quand couverture_gui aplatit les tables
imbriquees : elle criait sur un schema correct. L instrument mesurait autre
chose que la cible. On aplatit desormais des deux cotes.

make prouver : CONFORME, 60 OK, 0 echec, 1 saute.

Prochaine etape : generer les formulaires depuis ce schema, et retirer
CHAMPS_ECRITS_PAR_GUI.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-08 15:19:20 -04:00
6077b179af plan : l ecriture des registres devient atomique — tout, ou rien
Some checks are pending
verifier / verifier (push) Waiting to run
`path.open("w")` TRONQUE avant d ecrire : entre les deux, le fichier est vide.
Une exception dans yaml.safe_dump, un disque plein, un Ctrl-C, et
instance/plan/serveurs.yml reste mutile.

L asymetrie fait la gravite : hosts.yml se regenere d un
make instancier-appliquer, le PLAN ne se regenere de rien. C est la source
unique de verite. Git est le filet, mais encore faut-il savoir qu on est tombe.

TREIZE SITES, UNE SEULE FONCTION

Le defaut n etait pas dans le GUI seul : douze sites dans sept fichiers, dont
les miroirs CLI des MEMES registres. Corriger le GUI seul aurait recree la
divergence que P41 garde depuis les neuf resolutions d instance. La fonction
vit donc dans inventory_rules.py, que les sept importaient deja. Une source,
pas douze.

TROIS DETAILS QUI FONT LA DIFFERENCE ENTRE « CA MARCHE » ET « CA TIENT »

  temporaire dans le MEME dossier   os.replace n est atomique qu au sein d un
                                    meme systeme de fichiers ; un /tmp sur une
                                    autre partition casserait la garantie sans
                                    rien dire
  fsync AVANT le rename             sinon le renommage peut atteindre le disque
                                    avant le contenu : au retour d une coupure
                                    brutale, un fichier neuf et VIDE — le defaut
                                    qu on ferme, deplace d un cran
  report des droits                 mkstemp cree en 0600, le plan est en 0664 et
                                    doit rester lisible par le groupe sur les
                                    runners

LE TEST PORTE SON PROPRE CONTROLE NEGATIF

scripts/tests/test_ecriture_atomique.py rejoue D ABORD l ancienne forme et
verifie qu elle DETRUIT. Sans ce controle, « le fichier est intact » ne
prouverait rien — il pourrait l etre parce que rien n a ete ecrit du tout. Une
garantie qu on n a jamais vue echouer n est pas une garantie, c est une
habitude.

Branche sur P02, dont le titre annoncait « inventory_host » alors qu il lance
maintenant trois tests. Corrige au passage.

LA VOUTE DU GUI : VERIFIEE, PAS DE DEFAUT

Le soupcon etait qu executer_flux pose ANSIBLE_VAULT_PASSWORD_FILE (un seul mot
de passe) alors que creer une VM ouvre DEUX voutes depuis « une voute, une cle ».

Eprouve contre deux voutes JETABLES a mots de passe distincts — jamais les
vraies. Les deux variables se CUMULENT : Ansible essaie tous les secrets, et un
PASSWORD_FILE errone n empeche rien. Confirme en sondant l environnement qu une
recette make recoit reellement : le mot de passe saisi ET les cinq cles
calculees par voutes.py.

Ce qui sauve ce chemin n est donc pas le mot de passe saisi, c est
l IDENTITY_LIST que make pose par-dessus. Chacun couvre ce que l autre ne
couvre pas — le PASSWORD_FILE sert le runner qui n a que sa cle, l IDENTITY_LIST
le poste qui les a toutes. Ecrit au-dessus du code, pour que personne ne
« simplifie » en retirant l un des deux.

make prouver : CONFORME, 59 OK, 0 echec, 1 saute.
make instancier : DIFF VIDE, quatre registres relus, droits 664 preserves.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-08 14:57:57 -04:00
6da1032ceb positionnement : le gel tient, c est la carte des seuils qui etait fausse
La question « et si on retirait le gel du perimetre ? » a mis les cinq seuils a
l epreuve. Deux ne tenaient pas — et les garder etait plus dangereux que le gel
lui-meme : une carte des seuils fausse ne fait pas perdre du temps, elle fait
FRANCHIR UN SEUIL QUI NE L EST PAS.

RBAC — couvert, et par un mecanisme plus fort

Trois classes d acteurs aux pouvoirs disjoints existent depuis les runners : le
poste de l exploitant, le runner de SITE (materialiser, n entre jamais chez un
tenant) et les runners de TENANT (configurer). La separation est CRYPTOGRAPHIQUE
— une voute, une cle, 2026-08-28 — pas applicative : c est la presence des
fichiers qui borne le pouvoir, jamais une table de permissions qu une faille de
l application contournerait. Adopter AWX pour ce besoin serait REGRESSER.

Le tableau avait ete ecrit avant que les runners existent.

IPAM — sans objet par construction

Un IPAM sert a ALLOUER. Ici rien ne s alloue : tout derive du seed. Et cinq
preuves tiennent deja ce qu il verifierait — P20 (aucun adressage stocke), P21
(collisions d index), P23 (chevauchement d underlay), P28 (pools), P33 (ports).
L adopter remplacerait une propriete PAR CONSTRUCTION par un controle a
posteriori.

LE SEUIL QUI MANQUAIT : L EMANCIPATION

Le GUI ecoute sur 127.0.0.1 avec un jeton de session — un modele
mono-utilisateur, juste tant que l exploitant est une personne a son poste. La
trajectoire de filiation-emancipation.md mene a plusieurs HUMAINS, aux portees
disjointes, sur des machines qui ne sont pas les notres.

Ce seuil n appelle pas AWX : les runners portent deja la separation des
pouvoirs. Il appelle une decision sur la facon dont le GUI s ouvre a quelqu un d
autre, et elle n est pas prise. Un seuil qu on ne nomme pas est un seuil qu on
franchit sans le voir.

CE QUE LE GEL N INTERDIT PAS

Il porte sur les FONCTIONS de type NetBox/AWX, jamais sur les VUES. Montrer a l
ecran ce que le moteur sait deja — l ecart des dix devis, l etat du diff entre
Sauvegarder et Appliquer, le perimetre sur lequel un check vert a porte, les
temoins du genome et lequel a decroche — ne franchit aucun seuil : rien de tout
cela n existe dans NetBox ou AWX, parce que rien de tout cela n existe hors de
ce modele.

Le gel n est pas leve. D-84 le consigne, AGENTS.md suit.

make prouver : CONFORME, 59 OK, 0 echec, 1 saute.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-08 14:02:08 -04:00
cbdc6c523d wiki-publier : un wiki vide se clone tres bien, et le garde-fou ne le voyait pas
Some checks are pending
verifier / verifier (push) Waiting to run
En rattrapant origin, sa forge s est revelee porter un wiki VIDE — jamais
publie. Avant de viser la forge, la manoeuvre a ete eprouvee contre un depot
bare local, comme la premiere fois.

LE DEFAUT

Le garde-fou ne testait que l ECHEC du clone, alors que son message parle du cas
vide : « le wiki doit exister (creer une 1re page dans Forgejo) ». Or un depot
vide se clone parfaitement. La recette allait donc au bout et creait une branche
`master` — parce que init.defaultBranch vaut master sur ce poste — alors que le
wiki deja en service vit sur `main`.

Le nom de branche etait DEVINE, et il divergeait d une forge a l autre. Un wiki
publie sur la mauvaise branche est un wiki que Forgejo peut ne pas afficher :
la publication reussit, la page reste introuvable, et rien ne l explique.

LE CORRECTIF

Refuser un wiki sans commit, plutot que de choisir a la place de Forgejo. Creer
une premiere page dans son interface initialise le wiki avec LA branche qu il
attend — il n y a plus rien a deviner.

EPROUVE DANS LES DEUX SENS

  depot bare vide      -> REFUSE, avec le geste a faire
  depot bare non vide  -> publie
  forge eregion reelle -> « deja a jour », temoin depose

Le troisieme essai compte autant que le premier : remplacer un defaut par un
blocage aurait ete un autre defaut.

CE QUE CA LAISSE OUVERT

Le wiki d origin reste vide : l initialiser demande une action humaine dans
l interface de la forge, que cette cible refuse desormais de contourner.

Et le temoin ne nomme qu UNE forge. P60 prouve que le depot n a pas bouge depuis
la derniere publication — pas que toutes les forges sont a jour. Tant qu il n y
en a qu une de publiee, la distinction ne coute rien ; a deux, il faudra que le
temoin porte une liste.

make prouver : CONFORME, 59 OK, 0 echec, 1 saute.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-08 12:56:21 -04:00
e2935edd3d wiki : publie, et le temoin le prouve — P60 passe au vert
Some checks are pending
verifier / verifier (push) Waiting to run
La forge servait le wiki du 2026-08-10 : deux unites jamais publiees, vingt et
une differentes. Toute la revision de documentation n existait pas pour qui lit
la forge plutot que le depot.

Publie. Verifie en reclonant : 26 pages sur 26, aucune differente, aucune en
trop, 8 figures. La forge porte 4da68c7, source c9d31e9.

DEUX DEFAUTS DANS MON PROPRE AJOUT, PAYES A L EXECUTION

1. `@#` au milieu d un bloc shell continue. Le prefixe @ appartient a make, pas
   au shell : bash a cherche une commande nommee « @# ». La publication avait
   REUSSI, et le temoin n a pas ete ecrit — erreur 127 apres coup.

2. Plus grave, et invisible au premier essai : le temoin n etait ecrit que dans
   la branche « il y a des changements ». Or « deja a jour » est PRECISEMENT le
   cas ou il doit dire que la forge est au niveau du depot. Sans lui, P60
   restait rouge apres une publication reussie — une preuve qui refuse un etat
   sain, donc une preuve qu on apprend a ignorer.

   C est la seconde execution qui l a montre : elle est passee par cette branche
   justement parce que la premiere avait publie. Le defaut se corrigeait en se
   revelant.

Les deux tiennent dans la meme lecon : une garde qu on n a pas vue dire OUI ne
vaut pas mieux qu une garde qu on n a pas vue dire non.

make prouver : CONFORME, 59 OK, 0 echec, 1 saute.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-08 12:49:40 -04:00
c9d31e9a60 preuves : trois gardes pour ce que ma lecture ne tiendra pas
Une revision de documentation vieillit comme le reste. Ce qui tient, c est ce
qu une machine verifie — et trois lacunes etaient nommees sans etre gardees.

P58  HABILITATIONS. autorisation.md posait la regle (un service nomme un
     GROUPE, jamais une personne, D-66) et meta/acces.yml la portait ; rien ne
     la verifiait. P29 gardait les POSITIONS d authentification, personne ne
     gardait les DROITS.

     Le controle qui porte la preuve est un croisement : une entree
     porte_par: role-realm affirme que l habilitation voyage par un role de
     realm projete depuis un groupe LDAP. P58 le confronte a serveur_keycloak.
     Sans ca, un service annonce une habilitation que rien ne transporte, et l
     ecran reste vide sans que personne sache pourquoi.

     CE QU ELLE N EXIGE PAS, et c est le point le plus important : que les
     groupes nommes existent dans l annuaire. Ce serait contredire le regime du
     paragraphe 2 — le depot AMORCE un acces et se retire, les appartenances
     appartiennent a une personne. dev et personnel n existent dans aucun code,
     et ce n est pas un defaut.

P59  ENUMERATIONS ANNONCEES. Les deux ecarts trouves a la main pendant la
     tournee — cinq portes annoncees devant une table de six, huit lignes
     renvoyees vers une fiche qui en compte dix — etaient d une forme que P57
     ne voit pas.

     Ma premiere version a signale CINQ ecarts, et les cinq etaient du bruit :
     dans « reprise dans les deux devis : », le nombre qualifie autre chose que
     la liste. Cent pour cent de faux positifs — la preuve qui crie sur un cas
     sain et qu on apprend a ignorer. Resserree aux deux formes ou le nombre ne
     peut compter rien d autre. Etroite et vraie plutot que large et devineuse.

P60  WIKI PUBLIE. Le wiki est publie DEPUIS le depot ; rien ne mesurait l ecart,
     et il s est creuse de VINGT-SEPT JOURS en silence. Deux unites jamais
     publiees, vingt et une differentes : pour qui lit la forge plutot que le
     depot, toute la revision n existait pas.

     Le harnais est STATIQUE, zero appel reseau — cloner la forge romprait la
     seule propriete qui fasse qu une preuve vaille hors de ce poste. La mesure
     passe donc par un TEMOIN que make wiki-publier depose. Amorce avec la
     valeur MESUREE : le wiki d eregion porte b6167f2, dont le message dit
     source: ac85278.

     Ce qu elle ne prouve pas : un temoin dit ce qui est PARTI, jamais ce qui
     est ARRIVE.

LES TROIS SONT EPROUVEES DANS LES DEUX SENS

Douze essais negatifs, douze refus : groupe non projete, acces.yml disparu,
personne au lieu d un groupe, mecanisme invente, raison manquante, compte
revenu a cinq, septieme porte ajoutee sans toucher au compte, renvoi croise
fausse, temoin absent, temoin d un autre depot. Une garantie qu on n a jamais vu
dire non n est pas une garantie, c est une habitude.

ETAT : NON CONFORME, 58 OK, 1 echec, 1 saute.

P60 est rouge, et c est le comportement voulu : le registre a le droit de
perdre. Le retard qu elle signale est reel et anterieur a elle. Une commande le
ferme, et elle vient ensuite.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-08 12:47:18 -04:00
36b6926e21 patient 0 : il n existe plus, et le depot le disait encore au present
Ses machines sont detruites. Le depot en parlait comme d un ecosysteme vivant,
et l une de ces phrases datait de la veille : positionnement.md comptait ses
5 VM dans la flotte, chiffre que j avais moi-meme ecrit en revisant.

CE QUI EST CORRIGE

positionnement.md         51 VM sur quatre plans vivants, et non 56 sur cinq
sortir-les-cles-du-poste  le genome est replique DEUX fois, non trois
wiki/Glossaire.md         SQLite : ce qu il PORTAIT
filiation-emancipation    la famille de pairs a perdu un pair
decisions-architecture    D-83 consigne le retrait, avec son cout
carte-set-ops.md          80 decisions en vigueur

LE COUT, PARCE QU IL N ETAIT NOMME NULLE PART

D-82 lui avait laisse deux raisons d etre : la mise en oeuvre de reference du
modele origine, et un TEMOIN de plus du genome. Le retrait solde la premiere et
abaisse la seconde de trois copies vivantes a deux.

Or c est le raisonnement de son propre README qui portait tout : on n echappe
pas a la boucle par la ruse, mais par le NOMBRE. Le nombre a baisse. Et le point
unique de defaillance que patient 0 existait pour eliminer — eregion, hors
flotte, que Set-OPS ne deploie ni ne sauvegarde ni ne prouve — est toujours a la
racine. Il n a jamais ete elimine, seulement promu ; il n y a maintenant plus de
miroir independant pour l absorber.

CE QUI RESTE SUR LE TERRAIN, ET QUI N EST PAS DE LA PROSE

Son plan est encore sur disque en federe: true. La federation lui reserve donc
toujours l index 29, la zone SDN t29, ses VNets, les VLAN 1291-1296 et les
sous-reseaux 10.29.16-21.0/24. Quatre machines du site — backup, cache, dns,
forge — acceptent encore SSH, apt, DNS et HTTPS depuis 10.29.0.0/16 : un
perimetre vide. Le runner du site clone encore ops-patient0.

C est un geste, pas une intention, et il touche le reseau : il n est pas fait
ici. La sequence est listee dans D-83.

make prouver --verifier : CONFORME, 56 OK, 0 echec, 1 saute.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-07 00:13:21 -04:00
5bc3bceac1 documentation : la tournee des 74 documents, parce qu un balayage ne lit pas
Some checks failed
verifier / verifier (push) Has been cancelled
La revision a commence par un balayage par motifs — chemins morts, cibles make
absentes, comptes derives. Il a trouve une trentaine d ecarts et rate presque
tout le reste : un motif ne voit que ce qui s exprime en motif.

make hote-planifier en est l exemple. La cible EXISTE, donc le controle passait
au vert. C est une cible depreciee qui refuse et sort en 2, recommandee par
AGENTS.md, et qui contredit la REGLE D OR du meme fichier trois ecrans plus
haut. Il fallait lire pour la voir.

74 documents lus un par un. 66 corriges, 8 exacts.

CE QUI ETAIT FRANCHEMENT FAUX

AGENTS.md, la source d autorite, annoncait la flotte pas encore executee contre
des VM reelles. Elle a ete rasee et remontee depuis zero trois fois.
ecosysteme-chezlepro.md, le document montre a un client, portait la meme
phrase : il se sous-vendait gravement.

courriel-conception.md s ouvrait sur aucun role n est encore ecrit, au-dessus de
son propre paragraphe 1 qui les nomme. autorisation.md se terminait sur rien n
est construit alors qu il rapporte des mesures datees du role en fonctionnement.
hebergeur-exploitation.md disait rien n est fait d un depot qui existe.
filiation-emancipation.md se contredisait a deux ecrans de distance.

DES MODELES DECRITS D APRES UN MONDE ANTERIEUR

Le resolveur : cinq documents decrivaient un Unbound par VM en opt-in, trois le
donnaient en exemple d integration FACULTATIVE — il est universel depuis le
2026-08-24. L adressage de nomenclature-vm.md : reseau unique, VLAN 11-15, VMID
a cinq chiffres. Le nommage SDN de sdn-evpn.md contre le code : c est le wiki
qui avait raison.

CE QUI CASSE AU PREMIER ESSAI

Le nom du gabarit dore etait faux a quatre endroits, dont la procedure qui le
FABRIQUE et le critere R2 de l epreuve d operateur independant.
preparer-un-site-hebergeur.md avertissait qu une VM faite a la main serait
detruite : raser derive du plan, il ne la detruira jamais — le risque est l
inverse. Un mot de passe d essai en clair dans un depot public.

DEUX PREUVES ETENDUES, ET UNE QUI SE TROMPAIT ELLE-MEME

P57 couvre les groupes : elle a signale aussitot 29 groupes annonces au-dessus d
un tableau qui en cite 40. P29 confronte le tableau de authentification.md aux
declarations reelles : 12 annonces, 21 reels.

Et P57 imposait un chiffre faux — 56 preuves alors que le depot en porte 57, la
conditionnelle vivant hors de tout comptage. Un garde-fou qui fait respecter une
erreur ajoute l assurance a l erreur.

CE QUI RESTE, ET QU AUCUNE PREUVE NE TIENT

Deux comptes trouves a la main. Et une lacune reelle : rien ne garde les
meta/acces.yml — ni qu un service web-sso en porte un, ni que le groupe qu il
nomme existe. P29 tient les positions d authentification, personne ne tient les
habilitations.

make prouver : CONFORME, 56 OK, 0 echec, 1 saute. 0 lien mort.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-06 16:18:23 -04:00
00cee67f02 depot hors site : trois pieges payes en une manoeuvre
Some checks are pending
verifier / verifier (push) Waiting to run
1. ansible -e cle=valeur DECOUPE SUR LES BLANCS — c est ainsi qu on passe
   plusieurs variables. Un chemin avec une espace y perd tout ce qui suit le
   premier blanc, et l erreur parle d un repertoire introuvable au nom tronque.
   Passage en JSON. Le depot avait deja appris ca pour le clonage de VM ; la
   lecon ne s etait pas propagee. Quatrieme fois.

2. /srv/restic est en 0711 — traversable, NON listable, pour qu un locataire
   n apprenne pas qui d autre depose ici. La consequence tombe sur rsync :
   opendir Permission denied, un message qui accuse un droit sans dire lequel.
   --rsync-path=sudo rsync fait lire la SOURCE en root.

3. rsync_opts ne protege pas ses elements : le shell coupait
   --rsync-path=sudo rsync en deux. Guillemets internes.

Resultat mesure : COPIE FIDELE, 233 fichiers, 95,7 Mo, empreintes identiques
une a une — comparees entre la source et la copie, pas supposees.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-09-05 16:33:26 -04:00
9a823a5ea2 depot hors site : sortir du site ce que le site garde pour tout le monde
Some checks are pending
verifier / verifier (push) Waiting to run
Le depot heberge la racine de l AC, la forge du genome, et l etat de CHAQUE
locataire — 109 Mo sur une seule machine, dans un seul batiment. C est le
probleme du filet range dans la flotte qu il protege, un etage plus haut.

On emporte AUSSI les depots des locataires : si le site brule, ils perdent
leurs sauvegardes avec lui, et eux ne peuvent rien y faire. Ils ont depose chez
l hebergeur, c est a l hebergeur de tenir cette promesse.

La copie est OPAQUE — chiffree cote client, illisible par qui la porte. C est
ce qui permet de la deposer chez un pair sans lui demander autre chose que de
la disponibilite.

L empreinte est prise A LA SOURCE avant la copie, puis recalculee sur la copie
et comparee une a une. Sans ca on rentre chez soi avec un repertoire.

Un pipe sans pipefail masque l echec de tout ce qui n est pas le dernier
maillon : ici sed, qui reussit toujours. Une empreinte vide des deux cotes
aurait passe la comparaison.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-09-05 14:20:19 -04:00
dcbb57db69 cles : la cle USB se suffit a elle-meme, et la restauration est prouvee
Some checks are pending
verifier / verifier (push) Waiting to run
Le jour ou l on s en sert, le poste est mort — et cloner le depot demande la cle
SSH qui est dans l archive qu on essaie d ouvrir. Une procedure rangee dans le
depot serait inaccessible exactement quand elle sert.

L export depose donc restaurer_cles.py et un LISEZ-MOI a cote de l archive. La
cle ne demande plus que gpg, python3 et la phrase de passe.

Le script remet chaque fichier a sa place selon son NOM (machine neuve, parfois
autre compte), repose les droits a 0600 — ssh refuse une cle privee lisible par
d autres, et son message ne dit pas qu il s agit d un droit — et refuse d
ecraser une cle presente, en regardant AVANT d ecrire.

Le LISEZ-MOI porte les trois commandes manuelles. Un outil peut avoir un defaut ;
gpg et tar seront la.

EPROUVE : export vers une cle, poste neuf vide, restauration depuis la cle SEULE,
empreintes comparees — identiques 4 sur 4, droits 700/600, et le refus d ecraser
tire. Le filet manuel passe au meme test separement, identiques 4 sur 4.

make cles-restaurer refusera puisque les cles sont en place — et ce refus est la
preuve que l archive s ouvre et que la phrase de passe est la bonne.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-09-05 13:33:31 -04:00
3eff217bd5 cles : un chmod refuse ne doit pas annuler une verification reussie
Some checks are pending
verifier / verifier (push) Waiting to run
Une cle USB est le plus souvent en FAT32 ou exFAT — pas de permissions Unix.
Le chmod de la derniere ligne y echoue, et le script serait mort APRES avoir
ecrit et verifie l archive : une trace d erreur sur un travail termine, au pire
moment pour semer un doute.

On le dit plutot que de le taire : sur un support sans droits, l archive est
lisible par qui branche la cle. C est le chiffrement qui protege, pas le support.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-09-05 11:16:45 -04:00
bebdb84212 cles : sortir du poste ce qui n existe qu au poste
Some checks are pending
verifier / verifier (push) Waiting to run
Le code est replique trois fois (eregion, forge du site, patient 0) et les
voutes chiffrees y sont aussi — le coffre est solide. Les CLES qui l ouvrent
vivaient dans neuf fichiers, 1644 octets, sans copie ailleurs.

Poste seul : les mots de passe restic restent lisibles sur les machines vivantes,
donc recuperable mais douloureux. Poste + une machine : l etat de cette machine
devient illisible. Poste + site : terminal.

make cles-recenser montre ce qui sortirait sans rien ecrire — nom, taille,
empreinte, JAMAIS le contenu. make cles-exporter chiffre en AES256 puis
REDECHIFFRE ce qu il vient d ecrire et compare les empreintes une a une : une
sauvegarde de cles qu on n a pas rouverte n est pas une sauvegarde.

A lancer par l exploitant lui-meme : gpg demande une phrase de passe, elle ne
doit passer ni par un journal ni par le contexte d un assistant.

Trois refus, eprouves en les faisant echouer : destination dans l infrastructure
(un coffre dont la cle est dedans), archive existante (elle est peut-etre la
seule), archive illisible (supprimee). Le premier essai du premier refus etait
faux — le shell developpait HOME avant que je le remplace, l instrument mesurait
ailleurs que la cible.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-09-05 09:42:01 -04:00
42becd0c02 paquets tiers : passer par le cache du controleur, plus par Internet
Some checks are pending
verifier / verifier (push) Waiting to run
Trois depots tiers etaient en HTTPS, et client_artefacts pose
Acquire::https::Proxy DIRECT — sans quoi le cache du site refuse les tunnels et
aucun depot tiers n est joignable. La ligne est juste ; sa consequence ne l avait
pas ete vue : ces trois depots CONTOURNENT le cache, et chaque VM neuve allait
les chercher sur Internet a sa naissance.

step-cli et alloy sont poses par des integrations UNIVERSELLES. Sans lien, une
machine neuve n obtenait ni son client d autorite ni ses metriques — elle n
entrait dans aucun flux chiffre. Dernier obstacle a une reconstruction hors
ligne, et il tenait dans un mot.

make cacher-paquets tire les 21 deb aux versions epinglees, empreinte SHA256
verifiee depuis l index du depot. Le role partage paquets_tiers les depose et les
installe EN UN SEUL appel a apt — il sait resoudre un ensemble de fichiers locaux
qui se dependent, la ou paquet par paquet echouerait sur l ordre (Icinga en
apporte dix-sept). Six roles branches ; chacun retombe sur le depot distant pour
ce que le cache n a PAS fourni, et rien d autre.

Eprouve pour de vrai : packages.smallstep.com renvoye vers 127.0.0.1, step-cli
desinstalle, cache local efface. Le role rejoue installe 0.30.6-1 depuis le cache
et la machine obtient ses certificats. Zero echec.

Deux defauts de mon propre outil, trouves en le construisant : Smallstep sert son
index NON COMPRESSE (404 sur .gz) donc step-cli n etait jamais mis en cache ; et
un depot injoignable faisait continue AVANT d incrementer le total, si bien que
le script rapportait 20 sur 20 alors qu il en manquait un. Une garde qui ne peut
pas echouer ne garde rien — troisieme fois cette semaine. Corrige puis eprouve en
le faisant echouer.

Reste hors ligne : NTP externe, et l expedition des alertes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-09-05 08:22:14 -04:00
872b5e91aa forgejo : laisser le premier demarrage finir avant de l interrompre
Some checks failed
verifier / verifier (push) Has been cancelled
Deuxieme reconstruction de validation de Chezlepro : 14/14 hotes, 0 echec,
make valider 0 echec sur 13. Les corrections de la veille ont toutes tenu —
la garde de clonage a laisse passer trois WARNINGS, client_artefacts a degrade
sur le cache du site puis bascule seul.

Un defaut que la premiere reconstruction n avait pas montre : le role DEMARRE
Forgejo, qui entame son initialisation de premier lancement (schema +
migrations, plusieurs dizaines de secondes), puis flush_handlers le REDEMARRE
parce qu app.ini vient de changer. Redemarre au milieu, il laisse la version
cible inscrite et le schema absent — 5 tables, version=305 — et boucle
indefiniment sur migration[v14a] :  la relation collaboration n existe pas .
Le service reste active, il reessaie, et n ecoute jamais son port.

Invisible aux deploiements suivants : la base est deja migree, le premier
demarrage est instantane, le redemarrage ne tombe au milieu de rien. Le defaut
n existe que sur une base VIERGE, et meme la il depend du timing.

La chaine de sauvegarde est prouvee sur une flotte nee il y a une heure : neuf
detenteurs deposent chez l hebergeur, chacun verifie SON depot avec la cle qu il
est seul a detenir, et le rapporte a sa propre supervision.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-09-02 22:27:38 -04:00
6653f49f2e sauvegarde : la verification suit la cle, pas le depot
Some checks are pending
verifier / verifier (push) Waiting to run
serveur_backup verifiait pour tout le monde — juste tant que le depot vivait
dans l ecosysteme. Depuis qu ils deposent chez leur hebergeur, le site heberge
des octets chiffres COTE CLIENT : il ne peut ni les lire ni dire s ils valent
quelque chose. La verification revient donc au seul qui detient la cle, le noeud.

client_backup verifie SON depot distant — pas le fait d avoir lance sa
sauvegarde. Une unite verte sur un depot vide est ce qui a menti un mois.

serveur_icinga n exige plus un hote serveur_backup et se branche sur deux
modeles : depot local (services sur son hote, nommes sauvegarde: <noeud>) ou
pas de depot (services sur chaque noeud, nommes sauvegarde).

Le 404 qui n etait pas une absence : les noeuds recevaient  No objects found
alors que icinga2 object list montrait le service charge. Le filtre du compte
d API ne portait que la premiere forme de nom — c est la PERMISSION qui
refusait, avec les mots d une absence.

Aussi : ingress 5665 depuis client_backup, le pair ne nommait que
serveur_backup.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-09-02 18:53:44 -04:00
cccb4e5b43 supervision : declarer a qui elle parle
Some checks are pending
verifier / verifier (push) Waiting to run
Icinga livre son exemple avec root@localhost — une adresse que personne ne lit.
Une supervision qui voit tout et n en parle a personne a le meme effet qu aucune
supervision, en plus couteux : elle rassure.

serveur_icinga_destinataire cree un utilisateur dans le groupe auquel les
notifications sont deja assignees (Icinga refuse deux objets de meme nom, on ne
peut pas redefinir l exemple ; on en ajoute un autre, l exemple reste muet).

Vide, aucun destinataire n est pose ET LE DEPLOIEMENT LE DIT — plutot que de
laisser croire qu une alerte partira.

Eprouve : message expedie par le relais du site, status=sent (250 2.0.0 Ok),
livre directement a mx.chezlepro.ca sans intermediaire ni dependance envers un
locataire.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-09-02 17:48:19 -04:00
4e12c3a802 supervision du site, et la panne qui dormait dans un mot
Some checks are pending
verifier / verifier (push) Waiting to run
Le site a son temoin : site-mon-01 (VLAN 36) porte PostgreSQL, Icinga et un
relais. Le depot lui rapporte, et son premier verdict fut un vrai defaut —
site-mon-01 n avait jamais depose son propre etat. Les trois sont au vert.

serveur_backup_verification_locale repasse a true sur le depot : le drapeau ne
dit plus  on renonce  mais  verifie ce que tu peux ouvrir . La liste des noeuds
attendus derive de l inventaire ou tourne le role, donc du site seul.

serveur_postfix gagne un mode relais : un site n heberge aucune boite, il
expedie. Directement par sa frontiere — emprunter le MTA d un locataire ferait
dependre l hebergeur d un ecosysteme qu il peut outvivre.

LA PANNE DE FOND : le modele de routes OPNsense lit enabled ; on lui envoyait
disabled=0, un champ ignore. enabled restait a son defaut, ETEINT. Chaque route
creee par Set-OPS depuis l origine l etait desactivee — invisible, parce qu une
route eteinte EXISTE dans le modele et compte  posee . Le rechargement lance
pour activer la nouvelle patte a fait reprendre au noyau sa table depuis le
modele : 14 routes sur 15 disparues, 11 machines de Chezlepro injoignables le
lendemain de sa reconstruction, et le devis toujours vert.

Trois corrections empilees : la cause (enabled), la cecite (le plan lit la table
du NOYAU et signale absent ou eteint), l inaction (la reconfiguration ne se
declenchait que sur un changement du modele).

Aussi : un role du site peut enfin en appeler un autre a travers la frontiere —
le devis traitait tout pair nomme comme  l exterieur . Et nftables_admin_ssh se
DERIVE de la carte : recopie a la main, il retardait d une zone, et m a
verrouille dehors de la machine que je venais de creer.

Reste ouvert : le destinataire des alertes est encore root@localhost.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-09-02 17:34:01 -04:00
cf9abe74b6 sauvegarde : le site protege enfin son propre etat
Some checks are pending
verifier / verifier (push) Waiting to run
L hebergeur protegeait l etat de tous ses locataires et pas le sien. Deux choses
qu il detient et que personne ne peut regenerer : la racine de son AC, et la
forge du genome. Le reste est reconstructible par le code.

Preuve faite, pas annoncee : sauvegarde reelle puis restic check et restitution.
13 fichiers pour l AC (root_ca_key et intermediate_ca_key compris), 835 pour la
forge.

Le site depose avec SON identite, sur le compte restic que serveur_backup lui
cree, separe des comptes des locataires par les memes permissions. La cible est
derivee du expose de l application qui porte serveur_backup_site : le nom du
service, pas une adresse — client_backup_repo la grave dans le chemin de chaque
instantane.

P36 ne lisait que le plan de l instance montee, donc jamais celui du site — qui
detient pourtant le plus. Elle lit desormais les deux. Verifiee en la faisant
echouer : integration retiree, la preuve tire.

Reste ouvert : personne ne verifie les sauvegardes du site. Le depot tourne avec
verification_locale a false (pose pour les locataires, dont il ne peut pas ouvrir
les depots) et le site n a pas de supervision. La sauvegarde existe et se
restaure ; c est son SILENCE qui n alerte pas encore.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-09-02 11:21:52 -04:00
53c5f41a1a durcissement : le site recoit enfin serveur_durci
Some checks are pending
verifier / verifier (push) Waiting to run
Les six machines du SITE — racine de l AC, forge, cache, resolveur, depot de
sauvegarde, runner — ne recevaient que serveur_debian. Ni auditd, ni fail2ban,
ni apparmor, ni sysctl, ni pare-feu. Le commentaire au-dessus de GROUPE_SOCLE
affirmait pourtant le contraire, ce qui rendait l ecart invisible.

Le pare-feu du site n avait aucune des trois pieces qui le rendent applicable :
- aucune regle generee (resoudre_flux lit un hosts.yml ; le site a un inventaire
  dynamique) -> commande nftables-site, branchee sur make flux
- le chemin du jeu de regles sortait du depot (inventory_dir vaut scripts/ pour
  un inventaire dynamique) -> host_var explicite
- aucun nftables_admin_ssh : l exploitant administre PAR REBOND, la connexion
  arrive avec l adresse de la patte de frontiere DANS LA ZONE VISEE. Le
  generateur refuse desormais de produire des regles sans cet intrant.

Defaut attrape en LISANT le jeu de regles avant de l appliquer : la regle DNS de
site-dns-01 omettait 10.17.0.0/16. _supernets_voisins retire l instance montee —
juste chez un tenant, faux du cote du site, ou ca retire le locataire qu on
pilote. L appliquer aurait prive de DNS les quinze machines reconstruites la
veille.

MaxStartups du depot passe en host_var : dans le meta du role, il ne portait que
dans son play, et le passage de serveur_durci le remettait au defaut sans rien
dire. Un reglage qui depend de l ordre des plays revient en arriere.

Verifie depuis un locataire a travers le nouveau pare-feu : cache, forge, depot
(2 snapshots) et resolveur repondent ; l AC du site refuse, et c est correct.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-09-02 10:31:21 -04:00
0854b2a93c reconstruction : quatre defauts que seule une flotte rasee pouvait montrer
Some checks are pending
verifier / verifier (push) Waiting to run
Chezlepro detruite (15 VM, disques compris) et refaite depuis le gabarit
minimal. 15/15 hotes, 0 echec ; make valider passe, test de restitution
compris. Aucun defaut ne venait de la flotte ni du gabarit.

1. Un avertissement n est pas un echec. Proxmox rend WARNINGS: n pour une
   tache ABOUTIE ; la garde n acceptait que OK et declarait perdues cinq VM
   clonees a 100 pourcent. Le message parlait d etat stopped — celui de la
   TACHE, pas de la VM.

2. client_artefacts se contredisait : son commentaire disait de degrader, son
   code arretait. L autorite monte en premier, donc avant le cache du
   locataire : aucun ordre ne pouvait satisfaire la garde.

3. harden-below-nxdomain etendait le NXDOMAIN signe de la racine pour le TLD
   internal a toute la zone du site, sans jamais interroger l autoritatif.
   Declencheur : toute question sur un nom absent sous internal, y compris la
   zone d un autre locataire. Le cache contenait la bonne reponse ET un
   message negatif ; c est le negatif qui etait servi.
   aggressive-nsec: no avait semble marcher — c est le redemarrage qui vidait
   le cache, pas le reglage.

4. Un locataire doit savoir a qui demander la zone de son hebergeur, sans quoi
   il ne peut plus nommer son depot de sauvegarde. La derivation prenait
   dns_amorcage pour le resolveur du site : faux chez Technolibre, dont
   l amorcage est 9.9.9.9. P03 l a attrape avant tout deploiement.

Au passage : instancier tentait encore le mot de passe unique d avant la
separation des voutes ; comparer echouait en exit 4.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-09-02 09:59:15 -04:00
3ea0c95492 sauvegarde : l etat d un locataire quitte enfin sa propre flotte
Some checks are pending
verifier / verifier (push) Waiting to run
Chezlepro rangeait ses instantanes sur une VM DE SA PROPRE FLOTTE. Raser
l ecosysteme pour le reconstruire, c etait raser le filet avec.

Le site a son depot ; les neuf detenteurs d etat y deposent ; une
restitution est sortie (annuaire LDAP lisible, hors flotte).

Isolation par compte Unix, pas par convention : home 0700, cle exclusive,
racine partagee a root en 0711 (traversable, non listable). Les deux refus
constates. Le site heberge du chiffre : il ne peut ni lire ni ouvrir, d ou
la verification deplacee chez le locataire qui detient la cle.

Quatre defauts reveles par ce deuxieme usage :
- la racine des depots ne peut etre le home de personne (StrictModes rendait
  Permission denied publickey pour un refus de CHEMIN)
- la racine nie le TLD internal, et harden-below-nxdomain etendait ce non a
  toute la zone sans jamais interroger l autoritatif : aucun locataire ne
  pouvait nommer un service du site
- le gabarit transporte des fichiers de durcissement perimes, et les machines
  du site ne recoivent jamais ssh_hardening
- MaxStartups compte les connexions non authentifiees : un depot de site en
  voit la somme de ses locataires

Constat non corrige : les machines du site ne sont pas durcies.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-09-01 22:16:20 -04:00
4abf875e5f gabarit : q35 n est pas un reglage, c est la raison de la procedure manuelle
Some checks are pending
verifier / verifier (push) Waiting to run
CONSTAT DE L EXPLOITANT, PAYE EN ANOMALIES : convertir une machine deja installee
d `i440fx` a `q35` produit une serie de pannes dont chacune ressemble a autre chose
qu a sa cause. Ce n est pas une correction, c est une transplantation.

LE MECANISME, ECRIT POUR QU ON NE LE REDECOUVRE PAS : `i440fx` est un chipset PCI,
`q35` est PCIe. La topologie des bus change, donc les NOMS D INTERFACES
PREDICTIBLES changent avec le chemin PCI (enp0s3 -> enp1s0) et la machine perd le
reseau ; les chemins de disques bougent ; l ordre d enumeration suit.

C EST AUSSI POURQUOI SET-OPS N UTILISE PAS L IMAGE CLOUD OFFICIELLE DE DEBIAN :
`genericcloud` est livree configuree pour `i440fx`. Une machine nait `q35`, ou elle
ne le sera jamais proprement — et c est ce que l installation depuis l ISO garantit.

Ces deux lignes de la procedure n etaient qu une ligne de tableau. Elles portent
maintenant leur pourquoi, et le SITE les declare comme DONNEES (cle `gabarit`),
plus seulement comme prose.

`make gabarit-etat` compare le gabarit reel a ce que le site declare de lui.

ON VERIFIE LA SOURCE, PAS CHAQUE COPIE. Ma premiere version gardait le CLONAGE : la
propriete s herite, donc verifier chaque clone coute a chaque creation sans rien
dire de plus que verifier le gabarit une fois. Retiree.

TROIS FOIS J AI DEVINE LA FORME DE LA REPONSE AU LIEU DE LA REGARDER — regex_search
a groupe qui rend None, proxmox_vm_info sans `config: current` qui ne rend que
l etat. La garde a declare « ? » sur une VM parfaitement conforme : une garde qui
crie toujours est pire qu aucune, on apprend a l ignorer.

A la demande et non dans `make prouver` : ce controle exige le cluster, que le
harnais ne suppose pas joignable. Meme nature que genome-etat et underlay-plan.
Controle negatif verifie : declarer i440fx fait echouer, rc=1.

make verifier : vert. make prouver : CONFORME, 56 OK, 0 echec.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-09-01 14:42:58 -04:00
9aef3614ab gabarit : refabrique, minimal, et puise aux ressources du SITE
Some checks are pending
verifier / verifier (push) Waiting to run
REFABRIQUE (VMID 9006, modeleSetOPS-minimal). Quatre roles au lieu de dix-sept :
qemu_guest_agent, cloud_init, sudo_ansible, ssh_baseline — des conditions
d existence, pas des choix d efficacite. Tout le reste vient du socle, et P56 refuse
qu un role retire ne soit repris par personne.

FABRIQUE CHEZ LE SITE, ET C EST LA DECISION QUI COMPTE. « Les ressources du SITE
font autorite pour tous ses artefacts ; elles servent les tenants jusqu a ce qu ils
s emancipent. »

Il se fabriquait DEHORS : `-i "<ip>,"` ne porte aucun group_vars, donc ni mandataire
ni resolveur. L ancien gabarit allait chercher ses paquets chez Debian et resolvait
chez l ancien LAN — 192.168.10.10, lu sur la VM 99998. Le site avait son cache et
son resolveur, et son propre artefact les ignorait.

Desormais la fabrication DERIVE ses ressources du plan du site (underlay --adresses)
et tourne sur le reseau du genome. PROUVE : dix requetes de 10.0.33.31 servies par
site-cache-01, resolveur pose a 10.0.34.11.

L IDENTITE DU GABARIT VIENT DU SITE, PLUS DU TENANT. `proxmox_clone_vmid_modele`
vivait dans les group_vars de l ecosysteme : deux tenants pouvaient cloner deux
gabarits differents sans que rien ne le dise, et un tenant decidait d un objet dont
descend chaque VM de chaque ecosysteme. Meme mouvement que l INDEX (2026-08-25) : le
site ALLOUE, le tenant RECOIT. Cle `gabarit` du plan du site, lue par
underlay --gabarit.

PIEGE FERME EN CHEMIN : creer-vm passait VMID_MODELE="$SETOPS_VMID_MODELE" a
cloner-vm, or inventory_host.py n emet PAS cette variable. Elle valait donc le VIDE,
et ce vide ECRASAIT la valeur derivee — le gabarit du tenant reprenait la main sans
bruit.

ET UN PIEGE DEJA DOCUMENTE, PAYE UNE TROISIEME FOIS : le chemin du controleur
contient une espace, et `lookup('pipe', ...)` le decoupait. Guillemets.

EPROUVE DE BOUT EN BOUT : VM clonee du gabarit minimal — nom, adresse, machine-id
neuf, cle d hote regeneree, agent invite actif ; puis socle applique dessus,
changed=5, 0 echec, auditd compris. VM d essai retiree.

make verifier : vert. make prouver : CONFORME, 56 OK, 0 echec.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-09-01 13:46:31 -04:00
2f2323bd3e gabarit : minimal — il etait un cache du socle, et il perimait sans le dire
Some checks are pending
verifier / verifier (push) Waiting to run
Il portait DIX-SEPT roles : exactement ceux du socle et du durcissement, que le
deploiement rejoue a l identique. C etait donc un CACHE — et comme tout cache, il
perimait sans le dire.

MESURE : derniere recapture le 2026-08-09, et SIX de ses roles avaient change depuis
— common_packages, cloud_init, ssh_baseline, ssh_hardening, auditd,
nftables_baseline. Rien ne le signalait : le deploiement masquait la derive en
reappliquant tout, donc personne ne pouvait la voir. Aucune preuve du harnais ne
regardait sa fraicheur.

IL NE GARDE QUE CE QUI DOIT EXISTER AVANT QU ANSIBLE PUISSE AGIR :

    qemu_guest_agent  l agent repond AVANT SSH — P52 s en sert
    cloud_init        le seul chemin vers la premiere seconde
    sudo_ansible      la porte par ou tout entre
    ssh_baseline      le serveur SSH

Ce ne sont pas des choix d efficacite, ce sont des conditions d existence.

CE QUE CA COUTE, ET QUI EST COUVERT : une VM neuve n est plus durcie a la naissance.
Elle nait cependant DERRIERE LE PARE-FEU DE L HYPERVISEUR, policy_in=REJECT arme au
clonage — verifie sur obs-01. La fenetre d exposition est fermee par la fabric, pas
par le gabarit. Mon objection initiale tombait devant la mesure.

P56 GARDE LES DEUX MOITIES. Qu il ne REGROSSISSE pas : un role ajoute recree le
cache, donc la peremption invisible. Et que rien de retire ne soit PERDU : un role
absent du gabarit ET du socle disparaitrait de toutes les machines neuves, sans
erreur ni trace, et la panne arriverait des mois plus tard sur une machine qu on
croyait durcie. Verifie : 14 retires, 14 repris, zero orphelin. Deux controles
negatifs.

make verifier : vert. make prouver : CONFORME, 56 OK, 0 echec.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-09-01 10:35:20 -04:00
0b653170fa emancipation : l instrument de la quatrieme ligne — couper, pas sonder
Some checks are pending
verifier / verifier (push) Waiting to run
docs/filiation-emancipation.md decrit quatre temps et n en outillait que trois. Le
quatrieme est celui qu on oublie : « une emancipation non prouvee est une
emancipation non faite ».

SONDER NE PROUVE RIEN. Verifier que le service local repond ne dit pas si l amont
sert encore — le depot le disait deja du cache : « tant qu internet repond, un apt
update qui reussit ne dit pas d ou vient l octet ». L instrument COUPE donc l amont
et refait marcher la chose.

LE MEME ESSAI REND LES DEUX VERDICTS, et c est ce qui le rend honnete :

    coupe, la fonction marche  ->  EMANCIPE, et c est prouve
    coupe, la fonction casse   ->  PAS EMANCIPE, dependance prouvee REELLE

Le second n est pas un echec de l outil, c est son CONTROLE NEGATIF rendu par la
meme commande. Une preuve d emancipation incapable de montrer la dependance qu elle
mesure ne prouverait rien le jour ou elle passerait au vert.

UN TEMOIN PRECEDE LA COUPURE : la fonction marchait-elle seulement avant ? Sans lui,
une panne preexistante se lirait comme une dependance.

LA COUPURE EST GARANTIE REVERSIBLE : une TABLE nftables dediee, jamais une regle
glissee dans une table existante — elle se retire d un geste et ne peut pas laisser
d etat partiel. Le bloc `always` la retire meme si la mesure echoue ou si le play
est interrompu, et une tache verifie ensuite qu elle a bien disparu.

MESURE LE JOUR DE SA NAISSANCE, les deux verdicts sur du vrai materiel :
  obs-01 / resolveur   PAS EMANCIPE — plus aucune resolution des la coupure
  forge-01 / artefacts EMANCIPE — apt installe, cache du site coupe

Ce second verdict a ete DOUTE puis verifie : apt aurait pu reussir en rejouant des
listes fraiches. Refait avec un dossier de listes NEUF, amont coupe : reussit
quand meme. Le cache sert vraiment son contenu.

make verifier : vert. make prouver : CONFORME, 55 OK, 0 echec.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-09-01 08:36:28 -04:00
89ca92b132 D-82 : patient 0 n est le parent de personne — le dilemme est tranche
Some checks are pending
verifier / verifier (push) Waiting to run
Ouvert le 2026-08-28, referme par les faits plus que par le raisonnement. Trois
d entre eux lui ont retire ce role un par un, et il fallait les regarder ensemble :

  D-81 a donne l autorite du genome a la forge du SITE. Son dernier lecteur,
  Chezlepro, a ete corrige le 08-26 ; Technolibre le 08-31. Il ne sert donc le
  genome a personne.

  Le denominateur commun qu il portait vit dans les modeles depuis le 08-24, sous
  le nom `origine`. Ce n est plus lui qu on copie.

  L ANCETRE ETAIT LOCATAIRE DE SON ENFANT : index 29 sur la fabric de
  SITE-Chezlepro, qui descend de lui. Il ne peut pas etre le chemin de reprise de
  son propre hote.

Des trois issues posees, la deuxieme l emporte — non parce qu elle etait la plus
elegante, mais parce que les deux autres avaient cesse d etre disponibles. Et le
depot l avait deja suivie sans le declarer : serveur_forge_site, puis
serveur_cache_site, puis serveur_resolveur_site. Trois services pretes, un patron.

CE QUE CA NE REGLE PAS, ET QUI EST ECRIT. Patient 0 existait pour eliminer un point
unique de defaillance — eregion, hors flotte, que Set-OPS ne deploie ni ne prouve.
Il ne l a pas elimine : IL A ETE PROMU. Le poste y pousse, la forge du site en tire.
La dette a change de proprietaire, pas de nature ; elle appartient au SITE. Un
objectif qu on abandonne sans le dire devient un objectif qu on croit atteint.

Ce qu il garde : sa place de pair dans la famille du genome, et sa forge de travail.
Ce qu il perd : le rang.

make verifier : vert. make prouver : CONFORME, 55 OK, 0 echec.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-08-31 20:57:25 -04:00
35f3881218 recette : un depot occupe n est pas une sauvegarde cassee
Some checks are pending
verifier / verifier (push) Waiting to run
make valider rendait ECHEC sur data-sql-01, et la meme commande rejouee a la main
restaurait 5 fichiers. restic verrouille son depot pendant qu il ecrit : une recette
qui croise la fenetre de sauvegarde lit un echec de RESTAURATION la ou il n y a
qu une attente. Verdict juste sur l instant, faux sur le fond, et DEPENDANT DE
L HEURE a laquelle on la lance.

ON REESSAIE, MAIS ON NE MASQUE PAS. Trois tentatives espacees couvrent un verrou
d ecriture ; au-dela, l echec est REEL et la recette rend la RAISON que restic a
donnee, au lieu d un -1 muet qui obligeait a se connecter pour comprendre.

CONTROLE VERIFIE SUR LA MACHINE : cle de dechiffrement retiree ->
« ECHEC apres 3 tentatives — restic dit : Fatal: Resolving password failed ».
La recette attrape toujours une vraie panne, et elle en donne la cause.

DEUX FAUTES A MOI EN CHEMIN, CORRIGEES.

`[ "$f" -lt 0 ] && printf ...` : la DERNIERE commande d un script decide de son code
de sortie, et un test faux rend 1. La tache echouait donc exactement sur les
machines ou la restauration avait REUSSI — trois hotes verts declares en echec.

Puis la ligne `raison=` n etait emise qu en cas d echec : le gabarit cherchait un
champ absent, rendait None, et explosait sur les DIX machines saines. Elle est
desormais TOUJOURS emise, vide en cas de succes. Un champ toujours present coute un
octet et supprime un cas.

make valider : rc=0. make verifier : vert. make prouver : 55 OK, 0 echec.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-08-31 20:41:39 -04:00
0dd57ba995 artefacts : deux filets — un cache mort ne doit plus rendre un tenant irreparable
CE CACHE EST UN MAILLON DONT DEPEND SA PROPRE RECONSTRUCTION. Toute la flotte y
prend ses paquets. S il pointe vers un amont mort, apt echoue sur les quinze
machines, donc le socle echoue, donc le deploiement n atteint jamais la couche qui
poserait le bon amont. LE CORRECTIF SE RETROUVE DANS LA COUCHE QUE LA PANNE EMPECHE
D ATTEINDRE, et il faut une main pour en sortir.

C est arrive le 2026-08-31 : l amont pointait sur le cache de patient 0, eteint la
veille pour liberer de la RAM. apt rendait « 503 Connection timeout » EN CITANT
L ADRESSE DU CACHE LOCAL, jamais celle de l amont manquant — la panne accusait le
maillon visible.

DEUX FILETS, SYMETRIQUES.

serveur_artefacts sonde l amont AVANT d ecrire, et refuse s il est muet : mieux
vaut un cache qui garde sa configuration precedente qu un cache qu on vient de
rendre inutilisable pour toute la flotte.

client_artefacts sonde la source AVANT de detourner apt vers elle. Ce fichier
ECRASE le plancher d amorcage pose par le socle — le poser sur un cache mort prive
la machine du cache du SITE, qui lui fonctionnait. En refusant, le plancher reste :
la flotte est degradee mais debout, et REPARABLE PAR UN DEPLOIEMENT.

`ignore_errors` ET NON `failed_when: false` — la difference est toute la garde.
failed_when: false REECRIT le verdict : la tache n est plus jamais failed, donc
`is not failed` est toujours vrai, donc l assertion ne peut pas tirer. Ecrite ainsi,
ma premiere version a declare « joignable » un amont mesure MUET la seconde d avant.
Une garde qui ne peut pas echouer ne garde rien.

Deux controles verifies sur la machine : amont eteint -> refuse ; amont reel -> pose.

make verifier : vert. make prouver : CONFORME, 55 OK, 0 echec, 0 saute.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-08-31 18:31:21 -04:00
e2bc235132 audit + timers : deux services en echec sur les quinze machines, muets depuis toujours
`make prouver` disait 15/15 a failed=0. Les machines, elles, portaient chacune trois
unites systemd en echec. Le rapport d Ansible n est pas l etat d une machine.

AUDITD : ONZE REGLES ARMEES, PERSONNE POUR COLLECTER.

Deux fichiers de regles identiques cohabitaient — 99-chezlepro.rules et
99-setops.rules, vestige du renommage du role. augenrules CONCATENE rules.d/ :

    Error sending add rule data request (Rule exists)
    There was an error in line 16 of /etc/audit/audit.rules

audit-rules echoue, et auditd ne demarre pas — c est sa dependance. Resultat :
auditctl -l affiche onze regles, ce qui donne toutes les apparences d un audit qui
fonctionne, et rien ne les enregistre.

Meme mue que ssh_baseline, meme registre : auditd_fichiers_perimes. On n y ajoute
que des noms qu on a REELLEMENT deposes un jour.

Et `failed_when: false` cachait la panne : quinze machines a failed=0 avec auditd
mort sur les quinze. Un service de securite qui ne demarre pas doit se VOIR.

TIMERS APT-DAILY : UN ETAT D ECHEC RESIDUEL.

Masquer le service pendant que son timer tourne lui fait perdre sa cible ; systemd
le note et le GARDE (Unit to trigger vanished). `state: stopped` n efface pas un
etat failed — seul reset-failed le fait.

Ce n est pas cosmetique : une supervision qui compte les unites en echec compte ces
deux-la pour toujours, et la vraie panne s y noiera. Meme defaut que le journal de
la frontiere noye sous 982 000 entrees.

DIAGNOSTIC FAUX, CORRIGE : j avais lu « masked enabled » dans list-unit-files comme
un etat contradictoire, et construit une reparation pour le defaire. Ces colonnes
sont ETAT puis PRESET — masque avec un prereglage constructeur active est normal.
La reparation a ete annulee avant d etre livree.

make verifier : vert. make prouver : CONFORME, 55 OK, 0 echec, 0 saute.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-08-31 16:16:13 -04:00
fde0903ed8 flux + postfix : un port symbolique dans le pare-feu, un rechargement qui n applique rien
Le deploiement depuis ops-01 passe de 3 plays a 21 : douze machines sur quinze
deployees completement. Deux defauts restaient.

UN PORT SYMBOLIQUE ECRIT TEL QUEL.

    /etc/nftables.conf:36: Could not resolve service: Servname not supported
        ip saddr { ... } udp dport derive accept   # serveur_powerdns

`port: derive` dit que le port depend du deploiement. Le devis de la frontiere le
resout depuis le 2026-08-25 ; le generateur nftables ecrivait le mot, et nft
refusait TOUT le fichier. Il resout desormais par le plan et n emet rien quand il
ne peut pas, en le DISANT — un flux tu en silence est une porte qu on croit
ouverte. Verifie que ca ne ferme rien : infra-dns-01 garde son 53 par
serveur_resolveur, et l omission de PowerDNS est juste puisqu il ecoute en loopback
derriere lui.

LA VALIDATION NE POSE PAS LA MEME QUESTION QUE L EMISSION. Ma premiere garde
refusait tout port non numerique et a fait echouer P09, qui valide les roles HORS
instance — la ou derive est legitime. PORTS_SYMBOLIQUES nomme le vocabulaire : le
mot passe a la validation, jamais dans un fichier, et un mot inconnu reste refuse
des deux cotes.

RECHARGER N APPLIQUE PAS UN CHANGEMENT D ECOUTE, deuxieme fois.

    warning: to change inet_protocols, stop and start Postfix
    fatal: :::submission: Address family for hostname not supported

main.cf porte inet_protocols, que Postfix refuse de changer a chaud. Le master garde
all, tente d ouvrir :::submission en IPv6 et meurt — APRES avoir accepte une
configuration valide. postfix check ne dit rien parce que la configuration EST
valide : c est la transition qui ne l est pas. Meme famille que nginx. main.cf
notifie desormais le redemarrage.

Diagnostic corrige en chemin : j ai d abord accuse postfix@-.service, dont
l assertion echouait — c est moi qui l avais declenche par un demarrage manuel,
postfix.service le declare en Conflicts.

make verifier : vert. make prouver : CONFORME, 55 OK, 0 echec, 0 saute.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-08-30 13:00:54 -04:00
a5c9a88f00 resolveur du site : le troisieme service prete pendant la jeunesse d un tenant
Apres les paquets (serveur_cache_site) et le genome (serveur_forge_site), les NOMS.
Meme patron : un installateur, un marqueur.

CE QUE CA CORRIGE, mesure sur infra-pki-01, premiere machine a porter l autorite
de Chezlepro :

    DNS               BLOQUE      un tenant resout chez lui, sa requete ne sort pas
    443 sortant       OK          par adresse
    apt via le cache  OK          le mandataire resout a sa place

apt s en sortait ; TOUT LE RESTE ETAIT AVEUGLE AUX NOMS. Versionner la cle de
signature Smallstep a fait passer une tache et l echec s est deplace d un cran : le
depot lui-meme devait etre joint par son nom.

POURQUOI PAS L UNBOUND DE LA FRONTIERE : il tourne sur le boitier sans etre un
service gere, et le plan du site l avait deja ecrit une fois — un processus n est
pas un service. site-dns-01 est deploye par le moteur et verifie par le harnais.

OU VIT LA DERIVATION : dans site_inventaire.py, qui connait le plan du site ET le
registre des tenants. Le role tourne sur une machine qui n a aucune raison de lire
la carte de l hebergeur — la derivation appartient a qui detient les deux sources.

PIEGE DE FILTRE, ATTRAPE EN LE BRANCHANT : _supernets_voisins() EXCLUT l instance
montee (personne n est son propre voisin). Le site sert TOUS ceux qu il heberge — y
compris celui qu on deploie. Reutilise tel quel, il privait de resolution le seul
tenant en cours de montage. On reutilise donc la DECOUVERTE partagee sans son
filtre : deux recensements divergent, deux filtres non.

Le marqueur verifie deux choses : que le resolveur ECOUTE, et que sa liste
d autorisation ADMET reellement les tenants. Sans la seconde, la panne chez le
locataire ressemblerait a un DNS mort alors que c est une ACL.

make verifier : vert. make prouver : CONFORME, 55 OK, 0 echec, 0 saute.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-08-30 12:37:33 -04:00
fc76a4e252 step-ca : la cle de signature versionnee — plus aucun appel sortant pour l obtenir
Le deploiement de Chezlepro depuis son propre runner a franchi le socle et le
durcissement sur les quinze machines, et s est arrete sur une seule :

    infra-pki-01 : Echec temporaire dans la resolution du nom
    url: https://packages.smallstep.com/keys/apt/repo-signing-key.gpg

UN TENANT N A PAS DE DNS SORTANT, ET C EST VOULU. Il resout chez lui, sa requete ne
traverse jamais la frontiere. apt s en sort par le mandataire du cache, qui resout a
sa place ; get_url n a pas de mandataire. Mesure sur la machine : DNS bloque, 443
sortant OK par adresse, apt via le cache OK.

Les cinq reprises du role ne pouvaient rien : elles etaient ecrites pour un serveur
INTERMITTENT (2026-08-23), pas pour une resolution fermee. Une reprise ne repare que
ce qui est passager.

UNE DETTE NOMMEE, APPELEE. client_artefacts le disait : les depots tiers en HTTPS
vont en direct, la seconde voie est propre et RESTE A FAIRE. Elle a ete appelee le
jour ou un ecosysteme s est reconstruit derriere une frontiere qui fait son travail.

La cle est versionnee, avec son empreinte et sa procedure de rafraichissement. Meme
idiome que les collections et les roues : le depot porte, la cible n ouvre rien.
C est ce qui rend une lignee reproductible SANS INTERNET.

Cout ecrit : une rotation amont n est plus recuperee toute seule, mais elle ne passe
pas inapercue — apt refuse alors le depot, bruyamment.

TROUVE EN LA RECUPERANT : l URL publique REDIRIGE vers pkgs.infra.smallstep.com.
Sans suivre les redirections on obtient un fichier VIDE, et curl -f sans -L sort en
succes. La procedure le dit, parce que ce piege ne se voit qu au deploiement suivant.

Le correctif du pare-feu a tenu : plus une seule suspension, quinze machines a
ok=66 et 0 unreachable.

make verifier : vert. make prouver : CONFORME, 55 OK, 0 echec, 0 saute.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-08-30 12:13:39 -04:00
6f4833e65c nftables : armer le pare-feu coupait la main qui l arme
Une session SSH ouverte AVANT le demarrage de nftables n a aucune entree
conntrack. Des que le ruleset s installe, ses paquets arrivent en invalid et
tombent sur la regle ct state invalid drop : la session meurt. Les connexions
NEUVES passent sans probleme. Le pare-feu est correct ; c est la continuite qui ne
l est pas.

CE N EST PAS UN VERROUILLAGE, ET C EST CE QUI LE REND DANGEREUX. Rien n echoue :
Ansible attend une reponse qui ne viendra jamais, sans delai. Un deploiement est
reste suspendu CINQUANTE-HUIT MINUTES sur six machines pendant que les quinze
repondaient parfaitement a qui se connectait a neuf.

async + poll 0 : on lance et on ne guette pas une reponse qui ne peut pas revenir.
reset_connection jette la session morte, wait_for_connection en ouvre une neuve,
suivie par conntrack des sa poignee.

Le delai est une ATTENTE, pas un verdict : si l hote ne revient pas, c est un vrai
verrouillage — la garde nftables_admin_ssh a manque sa cible — et il faut le dire
fort plutot que de laisser un playbook pendre.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-08-30 10:00:18 -04:00
bb63865a37 cles : le terrain etait inoccupable — la cle du tenant nait avec ses machines
Le deploiement lance depuis ops-01 s est arrete au premier geste, sur les quinze
machines a la fois : Permission denied (publickey). Les VM neuves n acceptaient que
la cle de l exploitant. Celle du runner EST declaree au plan, mais c est le SOCLE
qui la depose — et le socle doit etre applique par quelqu un qui peut deja entrer.
Boucle fermee : le tenant recevait un terrain qu il ne pouvait pas occuper.

DEUX CLES, DEUX PORTEES :

    la cle du SITE     -> sur le SEUL runner du tenant     l insemination
    la cle du TENANT   -> sur TOUTES ses machines          il va les configurer

POSER UNE CLE AU CLONAGE N EST PAS ENTRER CHEZ LE TENANT. C est un parametre de
creation, au meme titre que l adresse ou le disque : le site ecrit les conditions
de NAISSANCE, il n ouvre aucune session. Le site n obtient aucun acces sur ces
machines ; seul le runner du tenant en obtient un.

Forme tranchee par l exploitant : le site renseigne le seul runner, qui se charge
de toute sa flotte. Le plancher /etc/hosts suit le meme chemin — ops-01 a le sien
depuis son insemination et resout ses quinze voisines par leur nom.

LA REVOCATION EST HONOREE A LA NAISSANCE : une entree a etat absent n est pas
reposee. Sans cette lecture, une cle retiree de la flotte serait ressuscitee sur
chaque VM creee ensuite — panne lente, silencieuse, invisible au plan.

P55 garde les deux moities : la cle du site ne nait que sur un porteur de
serveur_ops_tenant, et aucune machine ne reste sans celle de son tenant. Une
frontiere tenue a une seule couche n est pas tenue. Deux controles negatifs.

make verifier : vert. make prouver : CONFORME, 55 OK, 0 echec, 0 saute.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-08-30 08:54:17 -04:00
f2f6cccd3e clonage : l hyperviseur juge le demarrage, plus le chronometre du module
Quinze VM creees trois a la fois. L une a depasse le delai du module PENDANT QUE
PROXMOX GENERAIT ENCORE SON ISO CLOUD-INIT :

    Reached timeout while waiting for starting VM.
    Last line in task before timeout: generating cloud-init ISO

Elle a demarre juste apres. flotte-creer a rendu « au moins une VM n a pas ete
creee » alors que les quinze tournaient. CE FAUX ECHEC ARRETE UNE RECONSTRUCTION :
reconstruire enchaine flotte-creer puis deployer-tout.

DEUX CORRECTIONS, LA SECONDE EST LA VRAIE. Delai plus genereux, parce que la
generation d ISO sur stockage partage se met en file quand trois clonages tombent
ensemble. Mais un delai reste un pari : son expiration ne conclut plus rien.
L HYPERVISEUR JUGE, en repondant ce qu il fait tourner — meme principe que P52, ou
l agent invite juge la materialisation a la place d une reponse SSH.

Logique verifiee sur quatre cas : seul running passe ; VM arretee, introuvable et
reponse malformee echouent. Une reponse vide ne passe pas en silence. Rejoue sur
une VM deja en marche : idempotent.

CE QUE LA CREATION DE CETTE NUIT N A PAS PROUVE : les quinze VM ont ete
materialisees DEPUIS LE POSTE, pas depuis le runner du SITE. Le chemin existait —
make inseminer, ecrit la veille — et il n a pas servi. Le poste detient
legitimement la voute du site, donc ce n est pas une faute de pouvoir ; c est une
demonstration qui manque.

make verifier : vert. make prouver : CONFORME, 54 OK, 0 echec, 0 saute.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-08-30 08:28:13 -04:00
5c59f19530 voutes : une cle absente n est pas une faute — elle est souvent la bonne reponse
Sur le runner d un tenant, la cle du SITE DOIT manquer : il porte la carte de la
fabric et ne doit jamais pouvoir l ouvrir. Ma premiere version sortait en erreur
des qu une cle manquait — elle presentait une SEPARATION REUSSIE comme un defaut,
et a fait echouer une chaine parfaitement saine.

Seule l absence de la cle de l INSTANCE MONTEE empeche la machine de travailler.
C est elle, et elle seule, qui decide du code de sortie ; le reste s affiche.

Constate sur ops-01 le jour de son armement :

    instance    OPS-Chezlepro     cle presente
    hebergeur   SITE-Chezlepro    sans cle

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-08-28 18:41:21 -04:00
732aff9c70 sdn : le puits avalait le plan d administration — une route, et le chemin existe
CE QUE LA MESURE A ETABLI, saut par saut. Le poste de l exploitant atteint la
frontiere, qui LAISSE PASSER (rule 31/0 match, pass out vlan040). Asgard RECOIT le
paquet sur vlan40. La VM ne le voit jamais. Et le noyau dit pourquoi :

    ip route get 10.17.19.41 from 10.0.31.11 iif vlan40  ->  dev vrf_t17
    ip route get 10.17.19.41 from 10.17.0.17 iif vlan40  ->  Invalid cross-device link

LA SOURCE EST LE DISCRIMINANT, pas l interface. Le chemin de RETOUR, vu du VRF :

    vers 10.0.31.11  ->  via 10.0.4.1 dev vlan40
    vers 10.17.0.17  ->  Invalid argument          <- le puits

LE PUITS A SES RAISONS et on n y touche pas : sans lui, une adresse non attribuee
du supernet sort par le defaut, revient par la frontiere dans la table PRINCIPALE
et repart vers le reseau de gestion (mesure du 2026-08-09). Le defaut n est pas
qu il existe, c est qu il est TROP LARGE : il couvre la bande basse ou D-77 place
justement l underlay d un site. Deux regles justes separement, contradictoires
ensemble.

LA CORRECTION EST DERIVEE, PAS ECRITE : pour chaque tenant, les reseaux
d administration qu il DECLARE (nftables_admin_ssh) et qui tombent dans son propre
supernet recoivent une route vers la frontiere, plus specifique que le puits. Ceux
qui vivent dehors n en ont pas besoin — la route par defaut les joint deja.

    vrf_t17   ip route 10.17.0.0/24    ...     l admin de Chezlepro
    vrf_t23   (rien)                           le sien est hors de son supernet
    vrf_t29   ip route 10.29.19.41/32  ...     l admin de patient 0 : son ops-01

CE QUE CA REPARE AU-DELA DE L ACCES : les regles administration -> tenant de la
frontiere etaient VRAIES et INAPPLICABLES a la fois. Elles correspondaient, elles
laissaient passer, et le paquet mourait un saut plus loin. Un devis vert sur un
chemin qui ne pouvait pas aboutir.

make verifier : vert. make prouver : CONFORME, 54 OK, 0 echec, 0 saute.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-08-28 17:46:40 -04:00
90ea474745 inseminer : le geste sort de mes mains et entre dans le depot
Some checks failed
verifier / verifier (push) Has been cancelled
L insemination avait ete conduite A LA MAIN depuis le runner du site — hors du
depot, donc sans preuve. Elle a maintenant sa cible :

    make inseminer TENANT=OPS-Chezlepro

TENANT= PLUTOT QUE LE SYMLINK instance : le runner du SITE amorce PLUSIEURS
locataires ; pointer un lien global sur l un d eux le ferait se prendre pour ce
tenant. Il en NOMME un par commande. Ca borne aussi le couplage que creer-vm
imposait en silence — rien ne disait sur quels tenants ce lien pouvait pointer.

L HOTE SE DERIVE : celui qui porte serveur_ops_tenant. Meme critere que le flux
d insemination et que la cle SSH du runner — le meme mot borne les trois pouvoirs.

P54 GARDE LA LIGNE DE PARTAGE la ou elle glisserait sans bruit. Les deux couches
retenues sont les seules qui ne reclament aucun secret. Le jour ou l on en
ajouterait une, le deploiement echouerait chez le tenant sur une valeur vide, et ce
message ne dirait pas qu un POUVOIR a ete franchi. Controle negatif : ajouter
client_pki, la couche suivante, fait echouer la preuve.

UN GARDE-FOU EXISTANT A INTERCEPTE UNE INSEMINATION MAL DIRIGEE. Le make parent
exporte SETOPS_INVENTAIRE ; ma resolution en heritait et visait l inventaire d un
AUTRE ecosysteme. Le refus vient d inventory_rules, pas de la cible — exactement
l erreur qu un runner servant plusieurs locataires commettrait en silence.

make verifier : vert. make prouver : CONFORME, 54 OK, 0 echec, 0 saute.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-08-28 17:14:37 -04:00
0595bf03c1 sonder : rapporter ce qui distingue, au lieu d un echec
Some checks are pending
verifier / verifier (push) Waiting to run
Trois faux diagnostics en une journee, tous dus a l instrument et aucun au
composant. curl et bash /dev/tcp ecrasent quatre causes incompatibles dans le meme
mot : ouvert, une POLITIQUE qui refuse, une machine ABSENTE, et la frontiere muette.

LE MEME CODE DIT DEUX CHOSES SELON LE DELAI, et c est la distinction qui a coute le
plus cher : EHOSTUNREACH immediat = pas de route ; le meme apres trois secondes =
il n y a pas de machine, c est l ARP qui renonce. Les confondre a fait appliquer un
pare-feu pour reparer un vide.  est pure, donc gardee par un test qui
exige que les deux ne se lisent jamais pareil.

LA GARDE ECHOUAIT DU COTE SILENCIEUX. L outil dit par quelle SOURCE le paquet part
et refuse de conclure sur une passerelle anycast — le piege qui m a fait declarer
muet un REJECT qui emettait bien ses RST. Ma premiere version rendait « pas
anycast » quand inventory_rules manquait, c est-a-dire sur un hyperviseur ou l on a
copie le seul fichier : precisement la ou le piege se produit. Une garde qui echoue
doit crier, pas se taire.

ET « NON CONCLUANT » EST UN RESULTAT : sur une source anycast et un silence, l outil
ne tranche pas, il dit quoi faire — compter les paquets du cote qui refuse, ou
sonder depuis une VM.

TCP seulement, et c est dit : UDP n a pas de poignee.

Valide contre le reel depuis ops-01 : trois cas sur quatre, le quatrieme attendant
deux machines vivantes dans un meme tenant.

make verifier : vert. make prouver : CONFORME, 53 OK, 0 echec, 0 saute.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-08-28 17:02:20 -04:00
c918a50257 journal : le REJECT est instantane — quatre RST en quatre secondes
Some checks are pending
verifier / verifier (push) Waiting to run
J avais presente 3,05 s comme le signal d un refus. Faux, et l exploitant a eu
raison de le contester : c etait l ARP qui abandonne pour une machine inexistante.

La mesure qui tranche n est pas le delai mais le COMPTEUR : tcp-reset 17 -> 21
pendant une sonde de 4 s, un RST par retransmission du SYN. Le refus part
immediatement, chaque fois.

Ce qui manquait etait le RETOUR : ip route get rendait src 10.17.21.1, une
passerelle ANYCAST. Le RST revient au noeud qui porte cette adresse dans le VRF,
pas a la socket emettrice. Troisieme instrument fautif de la journee, et celui-ci
etait deja documente.

Entre deux VM d un tenant — le cas reel — la source est une adresse propre et le
routage inter-zone ne la reecrit pas : le refus arrive instantanement. L artefact
ne vaut que depuis un hyperviseur.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-08-28 16:51:43 -04:00
4beb7e0724 flux : l interne refuse a voix haute, la bordure se tait (P53)
Some checks are pending
verifier / verifier (push) Waiting to run
Decision de l exploitant : block vers l Internet, reject a l interieur, parce que
c est prudent. Ce n est pas le refus qui informe, c est CE QU IL FAIT AU SILENCE :
sous drop partout, un timeout voulait dire aucune machine, aucune route, ou une
politique. Quand la politique parle, il n en reste qu une.

    nftables par hote   policy drop + reject with icmpx type admin-prohibited
    pare-feu Proxmox    policy_in = REJECT  (POLITIQUE_VM, source unique)
    frontiere OPNsense  block  — INCHANGE, et c est la condition

admin-prohibited ET NON tcp reset : un RST est indiscernable d un port ferme sans
service. La chaine forward reste muette : elle porte le trafic qui TRAVERSE l hote,
et y repondre ferait parler cette machine au nom d une destination qui n est pas
elle.

POURQUOI C EST PRUDENT : l obscurite etait deja nulle a l interieur (chaque machine
porte un /etc/hosts qui liste ses voisines), et la bordure protege le reject —
rien d indeclare ne franchit le perimetre, donc il ne repond jamais a l Internet.
982 000 entrees par jour a la frontiere, dont 82 % un balayage VNC.

LA MESURE A CORRIGE LA MESURE, DEUX FOIS.

Ma preuve interdisait le litteral DROP et a fait echouer un code JUSTE : la
detection d une politique posee AU DATACENTER, qui est un garde-fou. Une preuve qui
interdit un mot au lieu de mesurer une propriete finit par accuser ce qu elle
devrait proteger.

Et l absence parlait deja : EHOSTUNREACH en 3,05 s pour une machine inexistante,
timeout a 6 s pour un refus de la frontiere. Mes deux erreurs de diagnostic ne
venaient pas du drop mais de ma SONDE — curl et bash /dev/tcp ecrasent les deux
dans un meme echec.

Applique : 6 VM en REJECT, 0 creee, 0 retiree. Rien ne se ferme.
Trois controles negatifs verifies.

make verifier : vert. make prouver : CONFORME, 53 OK, 0 echec, 0 saute.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-08-28 16:43:22 -04:00
89c911ef05 journal : l insemination aboutit — un runner de tenant, ne d un runner de site
Some checks are pending
verifier / verifier (push) Waiting to run
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-08-28 16:21:39 -04:00