LE NOM D UN POOL EST CELUI DE SON DEPOT. Chezlepro-17 devient OPS-Chezlepro :
le seed se lisait dans le nom, ce qui obligeait a connaitre le codage — et
surtout le nom CHANGEAIT si l index changeait, ce que la renumerotation du
site a montre le jour meme.
Site-OPS ne derive de rien, et c est le point : les machines du genome ne
dependent d aucun index, elles sont l infrastructure SUR laquelle les index
vivent. Sans ce bloc elles restaient hors de tout pool.
P69 — l amorcage d un tenant designe-t-il le site REEL ? dns_amorcage et
artefacts_amorcage sont ecrits a la main, volontairement : au moment ou ils
servent la machine ne resout aucun nom. Mais ils designent des machines DU
SITE, et n ont pas suivi son renumerotage. La reconstruction du locataire
s est arretee sur Failed to update apt cache, a quinze couches de sa cause.
La preuve ne juge que les valeurs qui PRETENDENT designer le site : viser
9.9.9.9 est un choix, pas un oubli.
LES CLES SORTENT DU POSTE, EN CLAIR, ET C EST RAISONNE. Support perdu : LUKS
s en charge. Poste compromis : la seconde couche n aide pas, les originaux
sont dans ~/.config sur ce meme poste. Elle coutait une phrase de passe
stockee nulle part — le seul point que la procedure ne couvre pas. Option
--support-chiffre explicite ; le defaut reste GPG, parce qu un support non
chiffre est le cas le plus frequent.
Et ma note qui disait les cles sorties depuis le 5 septembre etait fausse :
le support ne portait que le depot hors site du 1er.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
Quatrieme reconstruction depuis zero, 32 min 14 s, 14/14. Elle avait un but
precis : trois roles dependent desormais d une variable que le generateur
pose. A froid, si instancier ne la posait pas, le defaut du role reprendrait
la main en silence — juste pour forge et cloud, faux pour observatoire.
Le certificat ne a froid porte observatoire et vigie, aucun ancien nom, et les
deux retours SSO derivent juste sans reprise.
Un seul echec, sans rapport : sur une machine des quatorze, get_url a rendu
0 octet SANS ERREUR — le cache a servi un 200 au corps vide. Le defaut s est
lu deux cents lignes plus loin, dans un apt qui accusait la signature. La
garde posee hier ne mordait pas : retirer une ressource vide ne vaut qu au
passage suivant, quand le fichier existe deja.
Les cinq roles qui telechargent une cle la mesurent maintenant dans la meme
execution. P68 garde le motif. infra-mail-01 redeployee, make valider a
0 echec sur 14 machines.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
Le renommage deploye, nginx servait observatoire, le certificat le portait,
les deux zones le publiaient — et Grafana fabriquait toujours son URL de
retour OIDC avec l ancien nom.
Quatre roles portaient en defaut une devinette du nom sous lequel ils sont
servis. Tant que le plan suit la meme convention, la devinette tombe juste et
rien ne revele qu il y a deux sources. Keycloak avait deja son remede, dans le
role — donc trois roles sans remede.
instancier derive desormais <groupe>_hostname de l exposition unique declaree
par le plan. Six services en heritent. P67 garde la derivation. Les quatre
instances sont regenerees, 67 preuves vertes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
grafana.chezlepro.internal et tableaux.genese.internal nommaient le meme
service de deux facons. Les deux deviennent observatoire.
Un nom de produit dans une URL se grave aussi dans les SAN du certificat et
dans les URI de redirection du SSO : remplacer Grafana obligerait alors a
renommer le service. Le nom dit desormais la fonction.
Le renommage a revele que les URI des clients Keycloak repetent a la main les
FQDN declares dans expose:. Rien ne les reliait. P66 garde ce lien, ecrite en
meme temps que le premier renommage.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
Deux mouvements d une seule doctrine : le site fournit tout ce dont un
tenant a besoin pour venir au monde.
1. LES DEPOTS TIERS. Grafana, Icinga, Smallstep et Collabora ne publient
qu en HTTPS ; apt-cacher-ng ne relaie pas un tunnel, et le socle pose donc
Acquire::https::Proxy DIRECT. Chaque machine sortait elle-meme sur
Internet. Le cache declare desormais un Remap par fournisseur, et chaque
role demande en {{ ..._depot_schema }}:// - http des qu un cache est
declare, https sinon. Le TLS n est rompu nulle part : il est TERMINE au
cache, qui est notre machine, et l integrite vient des signatures.
avant : 4 fournisseurs en HTTPS direct, 14 machines sortant seules
apres : 0 source en HTTPS direct, 0 erreur apt sur 14 machines
Trois lecons. Le remap appartient au cache qui SORT : pose sur un cache
chaine, il tente le HTTPS a travers son amont et rend 503. apt_repository
AJOUTE au lieu de remplacer, donc l ancienne ligne https sortait toujours.
Et la liste des fournisseurs ne se devine pas - j en avais trois, l audit
en a revele un quatrieme.
2. LE CACHE DU TENANT. Sa ligne portait son propre retrait depuis toujours
- service MUTUALISABLE, un ecosysteme au premier age peut pointer sur celui
de son hote. Retiree. Ce qu on perd, dit franchement : plus de trafic
inter-zone et plus de charge sur site-cache-01, contre un service de moins
a poser, superviser et reproduire.
3. UN ROLE QU ON RETIRE DOIT DEFAIRE CE QU IL A FAIT. Le retrait a montre
que rien ne nettoie derriere. Le fichier apt visait un cache eteint en
ecrasant le plancher qui fonctionnait - le defaut deja paye a quinze
machines. Et les sondes du cache restaient, le porteur poussant pour des
services qu Icinga ne definit plus (404). Le socle retire le premier,
client_sante derive les sondes attendues et retire les orphelines.
P65 refuse tout role visant un depot relaye en https ecrit en dur. Sa
limite est dite : elle empeche une regression sur ce qui est connu, elle ne
decouvre pas l inconnu.
Mesure : Icinga 87 OK sur 96, prouver 65 OK, lint 0 defaut.
Reste, et c est dit : apt-cacher-ng tourne toujours sur forge-01 que plus
aucun plan ne declare.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
LE CONSTAT. 39 roles declarent leurs flux, 32 leur empreinte, 32 leur
authentification — tous derives. Et 19 groupes sur 19 declaraient une
surveillance en prose que RIEN n executait ; Icinga en surveillait deux.
La carte disait ce qui etait surveille, et personne ne surveillait.
LE MECANISME. Un role declare ses sondes dans meta/supervision.yml et
depose lui-meme son script dans /usr/local/lib/setops/sondes/. Le porteur
client_sante les fait toutes tourner et pousse un resultat passif par
sonde, sans savoir ce qu elles mesurent. serveur_icinga derive les objets
Service ET le filtre de permission d API des memes declarations. Ajouter
une sonde ne demande de toucher ni au porteur ni a Icinga.
PREMIERE SONDE : client_pki/certificat. Heures restantes sur le certificat
reellement pose, chaine verifiee, et empreinte SERVIE comparee au disque
quand un service le consomme. 14/14 au tenant, 7/7 au site.
QUATRE OBSTACLES, ET TROIS SONT LA MEME LECON.
La sonde a rendu 14/14 en CRITIQUE sur une PKI saine : openssl verify
-CAfile racine ne trouve pas l intermediaire qui signe nos certificats.
step certificate verify, lui, repond VALIDE.
Deployee au site, elle a rendu 5/7 : le seuil d avertissement (12 h)
etait AU-DESSUS du point de renouvellement (8 h, le tiers restant). Elle
criait avant que le mecanisme ne soit cense agir. Seuils ramenes a 6 h et
3 h. Un seuil se DERIVE du moment ou le mecanisme surveille agit.
Une alarme toujours allumee ne vaut pas mieux qu une alarme jamais
allumee : elle apprend a ne plus regarder. Une sonde se prouve DEUX FOIS,
verte sur le sain et rouge sur le casse.
Le filtre d API etait ecrit avant la lecture des declarations : les
services auraient existe et Icinga aurait refuse leurs resultats.
Et mon controle negatif a casse un service reel : substituer le certificat
d hote a fait propager un cert sans sa clef vers node_exporter. Un controle
negatif se fait sur une COPIE.
P64 tient les deux bouts : declaree sans etre deposee, ou deposee sans
etre declaree. Trois controles negatifs rejoues.
make prouver : CONFORME, 64 OK, 0 echec, 0 saute.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
cloud-init n est pas un logiciel d installation : c est une SOURCE DE
VERITE EXTERNE. Il se reveille a chaque demarrage et relit le lecteur
attache par l hyperviseur, qui peut redefinir comptes, cles SSH, mots de
passe et reseau. Sur une machine que le plan possede, c est un second
maitre — que le plan ne decrit pas, que make valider ne mesure pas, et
qui parle en premier.
Sa tache est finie a la premiere seconde : c est parce qu il a REUSSI a
poser l adresse et les cles qu Ansible a pu entrer.
TROIS MOITIES, ET ELLES SE DEFONT SEPAREMENT.
- le GABARIT le garde : sans lui un clone n a ni adresse ni nom ;
- le SOCLE ne l installe plus : le garder produisait un va-et-vient a
chaque deploiement, deux changed par passage, idempotence perdue ;
- le DURCISSEMENT le retire (roles/cloud_init_retrait, en dernier).
P63 garde les trois, plus le CONTENU du role : une coquille vide passerait
les trois premiers controles sans rien fermer. Quatre controles negatifs
rejoues.
CE QUI REND LE RETRAIT SUR EST MESURE, PAS SUPPOSE (obs-01, 2026-09-09) :
/etc/network/interfaces.d/50-cloud-init n appartient a aucun paquet — dpkg
-S ne le trouve pas — et le postrm ne le nomme jamais, meme en purge.
L adresse survit. Le role le verifie quand meme, avant et apres, et n
accuse que si le retrait l a emporte : une VM qui perd ce fichier ne se
plaint pas, elle repart sans adresse et plus personne ne peut entrer.
DEUX CHOIX DITS FRANCHEMENT. cloud-guest-utils reste (growpart : ni
service, ni port, ni source de donnees). Les ~29 paquets orphelins ne sont
pas retires par defaut : autoremove deciderait a partir des drapeaux dpkg,
et un durcissement ne doit pas pouvoir surprendre.
NON DEPLOYE : le code est ecrit, valide et prouve ; il n a pas ete
applique a la flotte. Essai a blanc sur obs-01 : cloud-init a retirer,
configuration reseau intacte.
make prouver : CONFORME, 62 OK, 0 echec, 1 saute (63 preuves).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
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
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
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
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
Question de l'exploitant : « ne devrait-on pas prendre les versions les plus
recentes ? » Non — resoudre « la derniere » au deploiement detruirait la
reproductibilite qu'on vient de prouver en rasant et remontant deux
ecosystemes, et ferait de chaque reconstruction une loterie. Six majeures de
Forgejo ne s'avalent pas en effet de bord d'un `make`.
Mais il avait raison de tirer le fil : L'ECART ETAIT INVISIBLE. Il a fallu
quatre requetes a la main pour decouvrir Forgejo 10.0.0 contre v16.0.2 publie,
Keycloak 26.0.7 contre 26.7.1, Nextcloud 34.0.1 contre 34.0.2, oauth2-proxy a
jour.
Le devis ne juge pas, il RENSEIGNE : un retard n'est pas une faute, il sort
donc en 0. Monter de version reste un acte explicite.
Ce qui le distingue d'une liste ecrite a la main : les versions epinglees sont
DERIVEES du depot. La source amont, elle, ne peut pas se deriver — elle depend
de l'editeur — et vit dans une table. TOUTE VERSION EPINGLEE SANS ENTREE DANS
CETTE TABLE FAIT SORTIR EN ERREUR : sans cette garde, un epinglage ajoute
demain vieillirait sans que personne ne le voie. Une exemption reste possible,
mais ecrite et motivee (deux le sont : la serie PHP de Debian, la version
PostgreSQL vide).
Eprouve dans les deux sens : un serveur_redis_version ajoute temporairement
fait sortir en 1 en le nommant ; retire, retour a 0.
Et il rappelle ce qu'on n'epingle PAS. Grafana n'a aucune version dans le
depot — le 13.1.1 vu dans l'historique apt etait ce que son depot servait ce
jour-la. Debian, Grafana, smallstep et Icinga prennent ce qu'on leur donne.
Deux regimes coexistent dans la meme flotte, et les taire donnerait l'illusion
que tout est maitrise.
Verifie : versions-mesurer code 0, garde eprouvee, prouver.py 35 OK.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Question de l'exploitant : « je ne vois nulle part un MTU a 1450 ». Fondee.
Le 1450 existait bien — MTU_OVERLAY_DEFAUT dans underlay.py, dont P23 derive
sa garde (transport >= overlay + 50), pose par le devis SDN sur chaque zone,
et verifie sur le cluster. Mais il ne se propage pas a la carte de l'invite :
les 14 VM tournaient a 1500, et cloner_vm_debian.yml n'avait aucune occurrence
de `mtu`. La VM emettait donc des trames que son propre chemin ne pouvait pas
encapsuler — la panne que le registre des flux appelle « la plus couteuse a
diagnostiquer ».
Invisible parce que les 14 VM vivent sur asgard : deux VM du meme hyperviseur
communiquent par le pont local, sans encapsulation. Le defaut serait apparu au
premier eclatement de la flotte — c'est-a-dire quand il faudra heberger les
deux tenants ensemble.
Corrige en DEUX endroits, avec `mtu=1` (« herite du pont ») plutot que 1450 en
dur : juste en SDN comme hors SDN, et encore juste le jour des trames jumbo.
Le GABARIT le porte — proposition de l'exploitant, meilleure que la mienne :
elle couvre les clonages qui ne passent pas par le playbook. Et le playbook le
repose a chaque clone, parce qu'un gabarit se RECAPTURE et que ce qui n'est pas
versionne se perd en silence.
make mtu-mesurer (7e devis) rattache chaque hote a sa zone par son pont derive
et lit le MTU attendu dans devis_sdn.py. Ecart trouve sur 14 hotes du premier
coup, puis confirme corrige.
ET J'AI CASSE LA FLOTTE EN CORRIGEANT : applique aux cartes de VM EN MARCHE,
le changement detache et rebranche la carte sans que l'invite reconfigure son
interface. 0/14 joignables pendant trois minutes. L'agent qemu, independant du
reseau invite, a permis de constater et de redemarrer par l'API. Dans le
playbook la meme tache s'execute AVANT le demarrage du clone : aucun risque.
Sur une VM en service : poser la config, puis redemarrer — une seule d'abord.
Verifie : mtu-mesurer CONFORME 14/14 a 1450, prouver.py 35 OK, ansible-lint
production.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
L'epreuve de portabilite est passee. Un second ecosysteme souverain complet,
monte depuis zero par le meme moteur : 14 hotes, 2583 taches ok, 331 changed,
0 failed. Plan distinct, voute separee, realm technolibre, sa propre AC — et
une topologie differente : LDAP et SSO sur des machines separees la ou
Chezlepro les co-localise.
Cinq devis CONFORME (identite, certificats, PostgreSQL, courriel, frontiere —
55 lignes 0 ecart). Le sixieme dit exactement la bonne chose : les 6 services
repondent depuis l'edge, aucun depuis le poste, qui ne resout pas encore
technolibre.internal (6 entrees /etc/hosts absentes — le plancher).
SIXIEME DEFAUT MOTEUR. Le devis d'identite interrogeait LDAP en `ldapi:///`
— un socket UNIX LOCAL — depuis l'hote serveur_keycloak. Cela ne marchait que
par CO-LOCATION ACCIDENTELLE. Un tenant qui separe l'annuaire du SSO echouait
sur « Failed to import python-ldap » : l'hote SSO n'a pas de client LDAP. Les
deux lectures sont deleguees a l'hote DERIVE par resoudre_annuaire.
P35 (D-75) : toute application dont le role exige une base en a une au plan.
La garde de resoudre_base existait deja, mais s'est declenchee a la 92e tache
de collab-01, apres quarante minutes, pour un ecart entierement lisible dans
le plan.
Rien n'y est code en dur : les roles concernes sont ceux qui INCLUENT
resoudre_base, et le groupe reclame est lu dans le DEFAUT de la variable
passee — jamais deduit du nom. serveur_icingaweb2 reclame la base de
serveur_icinga ; une preuve supposant « role = groupe » aurait crie sur un cas
sain.
Eprouvee dans les deux sens ET sur les deux tenants, dont les registres n'ont
pas la meme portee : base retiree -> ECHEC la nommant ; restauree -> OK.
Verifie : prouver.py 35 OK sur les deux instances, ansible-lint production.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
La refonte de ce matin posait une convention. Une convention qu'on n'outille
pas tient tant que quelqu'un y pense : c'est le raisonnement de D-70, applique
au corpus documentaire.
Etat de depart mesure : 2 documents sur 34 declaraient leur lecteur. Les 32
autres disaient leur SUJET — ce qui avait enfoui le runbook de reprise le plus
utile du depot au §6 de autorisation.md.
Les 38 le declarent desormais, lecteur determine document par document et non
colle au gabarit : l'exploitant (devis, migration de tenant, cycle de vie,
gabarit d'or), le mainteneur (conceptions, registres, carte), le lecteur
externe (ecosysteme-chezlepro), l'agent IA (MISE-A-JOUR-CODEX-CLAUDE).
Deux exemptions DERIVEES, pas listees — un chemin en dur aurait vieilli a la
premiere page ajoutee : un document qui s'annonce genere, et un fragment sans
titre. Les 13 exemptes verifies un par un ; aucun document ecrit a la main
n'est exempte par accident. La preuve ne lit que l'EN-TETE, ce qui empeche
frontiere-opnsense.md et plan-et-generation.md — qui parlent de generation
dans leur corps — d'etre exemptes a tort.
Eprouvee dans les deux sens. Elle a echoue seule des sa premiere execution en
nommant deux documents que mon inventaire avait manques (docs/audit/). Puis
test negatif delibere : declaration retiree de meta-classe.md -> ECHEC la
nommant ; restauree -> OK.
Ce qu'elle ne teste pas : que le lecteur declare soit le BON. Ca se juge en
revue ; elle garantit qu'on a du y penser.
P01–P34. Comptes perimes corriges au passage (AGENTS.md et devis-services.md
annoncaient encore 30 preuves).
Verifie : prouver.py 0 (34 OK), plan-recette inchange.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
L'exploitant a retire la derniere regle heritee. `make frontiere-mesurer`
rend CONFORME (code 0) : tout ce qui est declare est livre, tout le reste est
refuse — y compris collab-01:9980, le seul qui livrait vraiment un HTTP 200
depuis le poste.
Et mon instrument avait tort, pas la frontiere. Il comptait 38 ecarts en
concluant depuis le client : « connexion etablie => la bordure a relaye ».
Faux, verifie A LA DESTINATION : pendant que le poste tenait une connexion
« etablie » vers idm-01:389, idm-01 n'en voyait aucune ; collab-01 n'en
voyait aucune sur 9980. La frontiere repond a la poignee TCP sans relayer.
Le devis raisonne desormais sur la LIVRAISON seule, et le controle ne rend le
releve NUL que s'il LIVRE des donnees — qu'il ressorte AMBIGU est attendu ici
et le rapport le dit a chaque execution. Cette relaxation rend aussi le sens
sortant mesurable : il etait declare NUL en permanence.
Nouveau mot-cle `poste: false` dans meta/flux.yml : un service publie n'est
pas forcement fait pour un poste de travail. Le 25 entrant de Postfix est un
flux serveur a serveur ; la frontiere l'etendait au VLAN d'administration ou
le nftables de l'hote le refusait. Deux regles retirees. Le mot-cle vit avec
le role qui sait ce que son port veut dire ; le generateur ne connait
toujours aucun numero de port.
Verifie : frontiere-plan sans ecart (41 regles, 12 routes), flotte 14/14,
frontiere-mesurer CONFORME, prouver.py 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
devis_expositions pose la question positive (une exposition declaree
repond-elle). Celle-ci manquait, et ce n'est pas la meme : un pare-feu peut
servir tout ce qu'on lui demande ET laisser passer tout le reste.
Rien n'y est saisi. Les cibles sont les ports REELLEMENT en ecoute (sonder un
port ferme ne prouverait rien du pare-feu) ; la politique attendue est lue
dans devis_opnsense.py, la source qui configure la frontiere.
scripts/sonde_tcp.py refuse de conclure : un CONTROLE (adresse ou personne
n'ecoute) avant tout verdict — s'il repond, le releve est declare NUL. Et
etablir n'est pas livrer : banniere, sinon requete HTTP, sinon poignee TLS ;
sans reponse le verdict est AMBIGU, jamais « ouvert ».
Deux pieges rencontres en le construisant, corriges et commentes sur place :
la sonde « tenant » s'executait sur le poste (dans un play connection: local,
delegate_to herite de cette connexion) ; et 2 s de lecture faisaient ressortir
un Postfix sain en AMBIGU (postscreen retarde sa banniere expres) — porte a
8 s, compense par du parallelisme.
Premier verdict, frontiere en l'etat : 38 ecarts, dont collab-01:9980 qui
livre vraiment un HTTP 200 depuis le poste. Releve sortant declare NUL.
Verifie : ansible-lint Passed, prouver.py 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Question de l'exploitant apres deux pannes causees par kcadm. La reponse est
non, et elle se fonde sur la mesure : sur six familles de defauts du jour,
deux seulement viennent d'un CLI ; un module Ansible (ldap_entry) a commis la
meme faute, et trois autres viennent d'un grep de fichier, de la precedence
Ansible et de mon propre comparateur.
D-68 — ecrire, puis relire et comparer, quelle que soit l'interface ; choisir
celle dont le chemin de lecture parle le meme langage que celui d'ecriture.
Une API est souvent preferable parce qu'elle rend la ressource ENTIERE, ce qui
permet le patron de chaque devis. Mais la plupart de la flotte n'a pas d'API,
et postconf -h / -e sont parfaitement symetriques.
D-69 — sur Keycloak : l'API pour toute map ou collection, kcadm ailleurs
(vocabulaire de la doc du produit, donc lisible sans IA).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Arbitrage rendu : le courrier local est route par uid, plus par l'attribut
mail. Dovecot le faisait DEJA (mail_home = /var/vmail/%{user | username}) ;
les deux moities ne s'accordaient que par coincidence, tant que mail valait
uid@<domaine_interne>. Cout assume : l'adresse interne est derivee et ne se
choisit plus ; en echange mail redevient libre de porter la vraie adresse de
la personne.
Puis la preuve de bout en bout a revele bien pire : toute livraison interne
etait DIFFEREE.
SSL_connect error to infra-mail-01:24: Connection timed out
status=deferred (Cannot start TLS: handshake failure)
Postfix etait durci (lmtp_tls_security_level = verify), le port LMTP de
Dovecot ecoutait en clair — son ssl = required global ne concerne que les
services de connexion. Les deux cotes d'un meme flux avaient ete traites
separement. Corrige et accorde : ssl = yes sur l'inet_listener (TLS implicite)
et lmtp_tls_wrappermode = yes cote client ; l'un sans l'autre ne marche pas.
Livraison prouvee : status=sent (250 ... Saved), message dans
/var/vmail/sysadmin/Maildir/.INBOX/new/.
Le devis disait CONFORME pendant ce temps : il verifiait la resolution et les
dialectes, jamais si le courrier BOUGE. Un devis qui ne regarde que les
reglages ne dit pas si le service rend son service. Il releve desormais la
file d'attente et ses raisons ; test negatif : la panne est nommee.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
PostgreSQL — rend visibles deux defauts deja vecus ici : un reseau ECRIT dans
pg_hba au lieu d'etre derive, et une ligne host en clair la ou il faut hostssl
(verrou qui saute sans bruit, les clients verify-full continuant de marcher).
Interroge pg_settings, jamais le fichier : un grep de postgresql.conf annonce
ssl_cert_file=snakeoil alors que le serveur sert le certificat de l'AC — la
valeur vient d'un conf.d que le grep ne voyait pas. L'instrument etait
incomplet, pas la configuration.
Courriel — chaque maillon interroge la ou il dit la verite : postmap -q pour
la resolution LDAP de Postfix, doveadm user pour celle de Dovecot (le maillon
exact ou la livraison avait bloque), vraies conversations SMTP/IMAP. Verifie
aussi qu'une adresse INEXISTANTE ne resout pas — sinon la boite est un
fourre-tout et le devis ne mesure plus rien.
Trouve immediatement une divergence reelle : Dovecot connait la boite de
sysadmin@chezlepro.internal, Postfix ne sait pas y router (query_filter
(mail=%s), et l'attribut mail porte sysadmin@chezlepro.ca). Collision de
roles : une identite ne porte qu'une adresse mail et on lui en demande deux —
notification joignable hors du systeme, et cle de routage local. Arbitrage a
rendre avant correction.
Tests negatifs : PostgreSQL 3 ecarts nommes, code 1. Une variable morte
trouvee dans une branche que le cas nominal n'emprunte jamais.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Troisieme devis de service. Chaque expose: du plan repond-il, et si non, ou
ca casse.
- requete HTTPS complete avec la racine de l'AC, jamais un connect() : a
travers l'OPNsense (anti-spoofing) toute connexion TCP reussit, et en TLS
le silence apres connect() ne distingue pas un service sain d'un trou.
- deux points de vue : depuis l'edge (edge + dorsal) et depuis le poste
(DNS + frontiere + edge + dorsal). Leur difference diagnostique.
- un code n'est pas un verdict : mon premier comparateur laissait passer un
502 des deux cotes. Trouve par le test negatif, pas par la relecture.
Etat : les 6 expositions repondent des deux cotes. Test negatif (frontiere
qui bloque + dorsal tombe) : 2 ecarts nommes distinctement, code 1.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Deuxieme application du patron devis/applicateur aux services. Trouve a la
premiere execution : sur infra-pki-01 — l'autorite elle-meme — le certificat
etait expire depuis plus de 8 h et le renouvellement echouait toutes les 14
minutes sur « 'step ca renew' requires the '--ca-url' flag ». Rien ne le
signalait.
Cause : sur l'hote de l'AC, /etc/step est le STEPPATH du SERVEUR, pas un
amorcage client — pas de defaults.json, et l'unite de renouvellement en
dependait. La lecon etait deja ecrite dans le commentaire de la tache
d'emission (« l'autorite ne bootstrape pas »), jamais reportee sur l'unite.
Le role ne pouvait pas non plus se soigner : la re-emission ne regardait que
la FORME (cert absent ou SAN manquant), jamais la validite. client_pki
verifie desormais l'echeance (client_pki_marge_renouvellement).
Ce qu'il a fallu desapprendre : les certificats vivent 24 h et se renouvellent
toutes les ~14 min ; « empreinte servie != empreinte disque » est l'etat
NORMAL. Comparer les empreintes aurait donne un verificateur qui crie en
permanence. Le signal est l'echeance de ce qui est SERVI, plus l'absence de
client_pki_reload_services.
Le devis a d'abord menti, du defaut meme qu'il traque : include_vars au niveau
du play prime sur les group_vars. Et le premier correctif a PARU marcher —
set_fact accepte un dictionnaire entier en argument libre sans erreur et n'en
fait rien. Il faut reimposer cle par cle. Les deux devis sont corriges et le
piege est consigne dans docs/devis-services.md avant d'ecrire le prochain.
Verifie dans les deux sens : CONFORME sur 14 hotes ; sur un releve ou l'on
rejoue une copie perimee en memoire, 2 ecarts et code de sortie 1.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>