Ses machines n existaient plus depuis le 2026-09-06 (D-83), mais son plan
restait sur disque : la federation lui reservait l index 29 et quatre machines
du site lui ouvraient SSH, apt, DNS et HTTPS. Les commentaires et documents
vivants gardent leur lecon sans le nommer ; les archives restent telles quelles.
Pas encore sur le reseau : les regles regenerees attendent le runner du site.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Un outil tiers veut n'offrir de ce moteur que ce qui n'agit pas. Le seul champ
qui le lui dise est `nature`. Il fallait donc qu'il ne mente pas, et il mentait
dans les deux sens.
`filiation` → `emancipation-prouver` se declarait « mesure » en portant
`fixes: {CONFIRMER: "true"}`. La cible tranche elle-meme — son refus dit
« cette preuve COUPE l'amont quelques secondes pour mesurer ». Un constat
rapporte ne rend pas inerte le geste qui l'obtient : l'etape passe a
`ecriture`.
Dans l'autre sens, trois cibles — flotte-creer, deployer-tout, reconstruire —
refusent sans confirmation sans que le registre le declare : un assistant les
lancait telles quelles, et sortait en 2. Comparer `nature` a `fixes` ne les
voyait pas, car c'est comparer deux champs ecrits par la meme main. `verifier`
lit desormais la RECETTE, qui ne ment pas.
Effet de bord traite : une etape qui agit barre celles qui la suivent dans la
console, alors qu'une « mesure » se debloque d'office. `depots-perimes` est
marque `facultative` — il nettoie ce que la filiation a laisse, il n'en est pas
le prealable.
`lister --json` rend le registre assemble d'un bloc, et le drapeau est refuse
hors de son action : rendre la prose humaine a qui demande du JSON est pire
qu'un refus. L'affichage humain ne bouge pas, et la preuve mesure ce qu'il
PORTE — un identifiant, un libelle, les marques de nature — et non seulement
qu'il n'est pas du JSON.
Valide : `verifier` a 0 ecart, test_runbooks.py 25/25, et sept des neuf
epreuves de `make test` ; test_raser et test_raser_resultat echouent sur
« Aucune instance montee », avant comme apres. Mesure : 127 etapes, dont 84 de
nature « mesure » — la surface qui n'agit pas.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RbXr5vv4Rat6PCPGG9GoG8
Le wiki publie datait du 14 septembre (392da4d). Un commit avait touche wiki/ depuis —
f33b5be, qui documente la vue Assistants — et P60 le signalait. Republie depuis d461492
vers le wiki du depot public sur eregion, l'adresse que le temoin precedent avait consignee.
Relu apres ecriture : un clone du wiki distant porte bien la section Assistants.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
serveur_ops clone ce que le plan declare et ne retire rien : le runner de
Chezlepro-locataire portait encore le clone de SITE-Chezlepro, la carte de son hebergeur,
longtemps apres que son plan ait cesse de la declarer.
Le retrait n'entre pas dans le role. Un role qui efface des dossiers a chaque passage est
une grenade degoupillee : une faute de frappe dans serveur_ops_depots suffirait a perdre du
travail local. C'est donc un geste separe, qui regarde par defaut et n'efface que sur
CONFIRMER=true. Trois choses ne sont jamais retirees, meme confirmees : ce qui n'est pas un
depot git, ce qui porte des modifications non validees, ce qui porte des commits qu'aucun
distant ne porte.
La garde a servi au premier essai : le releve a nomme SITE-Chezlepro et venv, et venv a ete
ecarte parce que ce n'est pas un depot git. Une version naive aurait efface l'environnement
Python du runner en se disant satisfaite.
Passe deux fois sur le runner reel : regarder (changed=0), puis confirmer (changed=1) avec
relecture. Le runner ne porte plus que Set-OPS-public et OPS-Chezlepro ; sa console rend
portee=tenant, materialiser=False, fabric=False — le pouvoir de LIRE la fabric est tombe
avec la carte, et c'est juste.
Valide : syntax-check du playbook, runbooks.py verifier a 0 ecart (la cible neuve est portee
par le runbook Filiation), make test a 0 echec. P02 reste en echec pour la raison anterieure.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Mesure sur la console de TechnoLibre, portee tenant : 6 runbooks conduisibles sur 17, et
parmi les onze fermes, locataire-deployer et machine-une — c'est-a-dire le travail
quotidien d'un locataire. La cause : flotte-creer et creer-vm engendrent des VM et
exigent la fabric, et une portee declaree pour toute la sequence faisait basculer avec eux
des etapes voisines qui ne demandent que ce que le locataire possede deja.
Le runbook ne donne plus que le defaut ; l'etape qui exige davantage le declare. Le
locataire conduit sa sequence et bute precisement la ou il faut : sur la machine a
engendrer, pas sur le deploiement qui suit. La page ferme l'etape seule avec sa raison, et
la garde de la route lit la portee de l'etape visee par son index.
Un droit calcule sur l'ensemble se trompe toujours dans le meme sens : il refuse a
quelqu'un ce qu'il a le droit de faire, et le refus parait fonde puisqu'il nomme un vrai
manque. Il a fallu une console de locataire reelle pour le voir — sur le poste, qui porte
les deux liens, les dix-sept sequences s'affichaient conduisibles.
Valide : runbooks.py verifier a 0 ecart, make test a 0 echec (4 tests neufs, dont un qui
nomme le cas exact), P83 verte. P02 reste en echec pour la raison anterieure deja consignee.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
La console offrait des boutons sans sequence. Rien n'y apprenait que site-creer precede
forge-amorcer, que le premier passage de site-deployer-tout s'arrete sur une forge vide
sans que ce soit un echec, ni que rien n'est pret avant valider : cet ordre vivait en
prose dans des documents que la console ne porte pas.
La vue Assistants conduit 17 runbooks et 126 etapes. Les 132 cibles documentees y sont,
chacune portee par un assistant ou exemptee avec son motif — une exemption muette est
refusee. Le registre ne recopie pas le Makefile : il declare l'ordre, la nature, la portee
et le pourquoi, et le libelle de chaque etape est lu dans le Makefile au moment de servir.
P83 est ecrite en meme temps que la liste, pas apres, parce qu'une liste qui suit une
autre prend du retard. Onze tests lui presentent des registres faux, un par forme de
retard, et exigent qu'elle les refuse.
Le navigateur ne nomme pas une commande, il nomme une place : la route lance ce que le
registre declare a cet index-la, avec les seules variables declarees. L'index compte, le
premier jour d'un site jouant site-deployer-tout deux fois. Une etape qui ecrit attend que
la precedente ait reussi ; une mesure reste toujours offerte, parce que mesurer apres un
echec est exactement ce qu'on fait ensuite.
Valide : runbooks.py verifier a 0 ecart, make test a 0 echec, les 83 preuves rejouees, et
la console lancee pour de vrai — 17 runbooks servis, six requetes malformees refusees une
a une, une etape de mesure executee de bout en bout avec son journal.
Limite, anterieure a ce travail : P02 (test_ecriture_plan) echoue sur domaines.yml, a
l'identique sur une copie de HEAD. Ajouter ou retirer un domaine public depuis la vue
Domaines leverait a l'enregistrement. Non corrige ici.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`pair-nouveau --locataire` refusait de proposer une adresse a un ecosysteme sans aucun pair,
soit exactement le premier. Le kit remis au client derive tout ce qu'il affiche.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
plan/acces.yml chez le locataire (cles publiques), reseau/port/instance derives de l'index.
La garde du devis refuse qu'un tunnel de locataire vise autre chose que son supernet : sans
elle, un ecosysteme s'ouvrirait un acces chez un voisin depuis son propre plan.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Eprouve en production : les locataires repondaient, le site non — son SSH vient de `flotte`
et vivait du rebond par la frontiere. Et rien ne designait la frontiere elle-meme, si bien
que monter le tunnel faisait perdre le moyen de le corriger. Une regle par port : OPNsense
refuse deux ports dans un champ.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le runner detient la voute et les cles SSH : en faire la porte des humains reunirait deux
pouvoirs que le depot separe. Instance `admins` a cote du tunnel site-a-site, un pair par
personne et par appareil, cles publiques seules au plan. Le reseau du tunnel est un reseau
d'administration : pare-feux d'hote, contrat des locataires et regles de bordure en derivent.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
os-node_exporter sur la patte de supervision, declare dans la carte du boitier : cible et
hote Icinga en derivent. Les flux vers la frontiere ne s'ouvrent plus a tous pour un role
qui n'est pas le socle.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Les hyperviseurs exposaient temperatures, ventilateurs, SMART et usure NVMe sans que rien
les regarde. scripts/materiel.py porte capteurs et seuils ; Grafana en tire le tableau
Materiel, Icinga un service materiel par hyperviseur. Premier verdict : le disque sda de
gandalf (OSD Ceph) a 56 secteurs en attente, stables.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Un flux externe porte port (la machine) et port_public (l'Internet). Le devis emet la
redirection et sa regle WAN, garde qu'elles aillent ensemble ; l'applicateur reconcilie
d_nat et encode les champs imbriques. DNS public : WAN:53 -> site-dnspub-01:1053 (dnsdist).
Plan lu : 4 objets a creer, rien d'autre ; pas encore applique.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Cle CSK ECDSA P-256 tiree dans la voute du locataire, importee par le role, DS calcule
sans la machine. Refus de changer la cle ou de retirer la signature tant qu'un DS est
publie. SOA-EDIT EPOCH : INCEPTION-EPOCH aurait laisse expirer les signatures du site.
CAA sur chezlepro.ca, sonde des signatures au site, P18 deplie vault_dnssec_<zone>.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Une zone publique ne portait que des A d'exposition ; basculer chezlepro.ca aurait coupe
son MX, son SPF et son DMARC. Les enregistrements se declarent au plan, valides et rendus ;
le serveur de noms porte le nom que le site declare (dns1.chezlepro.ca), parce que
ns1.chezlepro.ca existe deja en production. make dns-bascule-devis compare le plan au DNS
en service : 0 perdu sur les deux zones, deux prealables restants.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Applicateur de frontiere : ecrire n est pas charger, le chemin rien a faire charge
desormais. site-verifier compare enfin playbooks/site.yml, regenere avec le groupe.
Base TSIG dans un repertoire a pdns et pdnsutil en pdns (journal SQLite). Flux UDP 5300 :
le secondaire demande le SOA avant de transferer, garde dans P82. socket-dir de
pdns@public, et le failed_when qui taisait la notification est retire. Eprouve sur les
machines : 2 zones sur 2 tirees automatiquement, NOTIFY compte, zone interne refusee.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Role serveur_dns_public (secondaire public, transfert signe TSIG, aucune zone interne) ;
serveur_powerdns exerce enfin autorite primaire-cache dans une instance pdns@public a part,
pour que le site ne puisse jamais interroger la zone .internal. Relations derivees des plans
des locataires, mots de flux dns_public_site et primaires_dns_locataires. Eprouve avant
d ecrire : allow-axfr-ips et TSIG sont alternatifs, le primaire notifie aussi ses NS, le
serial fige aurait gele le secondaire. P82 refuse l exposition sans DNSSEC.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Servie par le runner d un SITE, la console montrait zero serveur sans une erreur :
serveur_ops retire le lien instance sur un hebergeur, charger_yaml rend un
inventaire vide sur un fichier absent, et la page dessinait ce vide comme un plan
vide. La portee se DERIVE des deux symlinks (instance = je configure, underlay =
je materialise), jamais d un reglage declare. Une console de site sert desormais
son inventaire dynamique ; POUVOIR_REQUIS exige un pouvoir pour chaque route POST
et le refus dit pourquoi ; la page nomme la console. P81 refuse une portee sans
source d inventaire, un site sans machines, et toute route qui echapperait a la
table.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Qui peut, doit ; qui ne peut pas, ne peut pas etre tenu. Vingt lignes derivees des
trois portees du moteur (calculer, configurer, materialiser), chacune citant son
mecanisme. Deux coupures qui ne se devinaient pas : la verification des sauvegardes
suit la CLE (l hebergeur heberge des octets qu il ne peut pas ouvrir), et l acces de
secours par sudo est dit plutot que tu, avec sa date de fin. Le tableau est canonique
dans le depot ; les chartes remises le LISENT entre ses ancres.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Livrer se terminait par une phrase — tes cles te seront remises separement — et
rien n ecrivait la suite. Temps 1 l identite (sa cle de voute, sa voute, sa racine
d AC), temps 2 la machine a echeance (sa cle entre, la notre sort, voute re-cletee,
secrets tournes). scripts/remise.py refuse une destination interne, un paquet sans
racine d AC, et tout ce qui n est pas l ecosysteme monte. Le registre remise.yml
declare enfin le responsable designe (D-18). P80 refuse un registre incomplet, un
second temps echu, un second temps declare fait sans revocation au plan, et un
secret dans un fichier versionne.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sept replis des deux derniers jours, un temoin chacun, lu hors de la derivation
qu'il juge : jumeaux d'amorcage, patte de zone, edge d'exposition, certificat
bouchon, rechargement, sortie vers l'Internet, regles d'hote par instance.
Eprouvee par reinjection de chaque faute ; a trouve deux flux-genere perimes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Les deux locataires declarent leur console derriere oauth2-proxy. En oidc le
vestibule n ecoute que la boucle locale : le gabarit annoncait cette protection
en s en remettant au pare-feu, qui ouvrait le port depuis l edge — la console
etait joignable sans passer par la passerelle.
COLLISION REELLE : serveur_oauth2_proxy est mono-instance par machine, et la
table des noms publics etait indexee par GROUPE. La passerelle de la vigie se
croyait la console ; son URL de retour OIDC aurait vise l autre machine. Une
application nomme un COUPLE (machine, role). P67 ne le voyait pas parce qu elle
indexait comme la derivation qu elle garde — une garde qui reproduit le
raisonnement qu elle verifie ne verifie rien.
ET J AI ECRASE group_vars/serveur_ops.yml sans le regarder : 68 lignes
detruites, deux preuves tombees, et une explication fausse construite dessus. Le
git diff qui m avait rassure portait un glob developpe par le shell parent : il
n a rien matche et n a rien dit. Une commande qui ne trouve rien et une commande
qui trouve que rien n a change rendent le meme silence.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
console.genese.internal : 401 sans justificatif, la console avec. Le site est le
premier ecosysteme a servir son GUI par un nom, en TLS verifie.
Le vestibule reste, et c est le contraire d une contradiction avec la veille :
Icinga Web 2 a une page de connexion, donc on lui a retire le sien ; le GUI de
Set-OPS sert sa page a qui la demande, jeton inclus, donc le vestibule EST sa
seule serrure. Verifie sur la machine : le GUI n ecoute que 127.0.0.1:8765, seul
le 8090 du vestibule est publie.
Controle dans les deux sens — une serrure ne se prouve qu en la forcant et en
l ouvrant. Et la sonde console-ops exige un REFUS sur une requete anonyme, parce
qu un 200 y serait la pire des reponses.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
L exploitant : Icinga Web 2 permet nativement de gerer des comptes ; ce genre
d intervention n est pertinente que pour integrer Icinga a Keycloak chez les
tenants.
Le mode locale posait un auth_basic nginx devant le backend external. Ca
marchait, et ca reinventait une page de connexion devant une application qui en
a une — en privant l exploitant de la gestion des comptes dans l interface. Un
vestibule n a de sens que devant une application qui ne sait pas
s authentifier ; le GUI de Set-OPS est dans ce cas, Icinga Web 2 non.
Mode db : backend natif, groupes natifs, base a elle (les tables du moteur sont
reecrites par ses migrations). Le moteur pose UN compte d amorcage et ne
l ecrase jamais. D-66 redevient applicable sans annuaire, les groupes vivant en
base.
Deux pieges du renommage : une garde ecrite en negation a cesse de garder, et le
bloc de la base pose apres le rendu des .ini a produit un echec CENSURE par
no_log.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
expositions n etait resolu nulle part — ni par le pare-feu d hote, qui ne filtre
pas l egress, ni par le devis de frontiere. Il n avait jamais eu besoin de
l etre : chez un locataire le trafic edge vers amont ne traverse pas le boitier.
Au site il le traverse. Une declaration peut dormir des mois avant que la
premiere fabric ne la reveille.
Une regle par amont, avec son port. Jamais une regle large : ouvrir l edge sur
tout le site annulerait ce qu une zone de publication separee cherche a obtenir.
La meme faute qu hier, avalee de la meme facon : U au lieu de underlay_mod, et
un except Exception large a transforme le NameError en note plausible. Resserre
a OSError/ValueError — une faute de frappe doit faire du bruit.
Le schema de proxy_pass suit maintenant le port, et l amont TLS est VERIFIE
contre l AC interne : relayer en TLS sans verifier ne fait que deplacer la
confiance.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
Le site servait trois applications web sans jamais les publier : observatoire,
vigie et console ne se joignaient que par adresse:port, en clair, depuis le plan
d administration.
Zone site-publication (VLAN 37) a elle seule. Les autres zones separent ce qui
agit de ce qui est agi ; celle-ci separe ce qui est adresse de l exterieur de ce
qui ne doit jamais l etre. Un edge est la machine qu on attaque en premier : elle
ne partage pas son voisinage.
site-edge-01 : 2 vCPU, 2 Go, 20 Go, sans client_backup — un relais ne porte aucun
etat. Elle ne s expose pas elle-meme : les expose des AUTRES applications
deviennent ses vhosts et les SAN de son certificat.
P23 a refuse une passerelle fantome. appliquer_opnsense pose des regles et des
routes, pas des interfaces : declarer la patte 10.37.37.1 rend le geste manuel
visible et verifiable, et le harnais le relit a chaque passage.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
Le GUI n a aucune authentification : GET / sert la page a qui la demande, jeton
ecrit dedans, et POST /api exige ce jeton que la page vient de donner. Le jeton
garde contre le CSRF, pas contre un visiteur — la seule serrure est
--hote 127.0.0.1. Ce qu il offre a qui entre : deployer, creer, raser, editer le
plan. La fabric entiere.
Le service reste donc sur la boucle locale. Ce qui est publie est un nginx local
qui authentifie d abord : oidc par defaut (oauth2-proxy, donc un groupe
d annuaire qu on revoque sans deploiement), locale en repli pour un ecosysteme
sans annuaire. L authentification est posee au niveau du server, pas d un
location.
La sonde console-ops mesure une SERRURE : une requete anonyme doit etre REFUSEE.
Un 200 y est la pire des reponses, et il ne fait echouer personne.
P54 a attrape une contrainte ratee : serveur_ops est insemine par le SITE, qui
ne detient pas la voute du locataire. Le role ne nomme donc aucune voute — il
declare un parametre, et la couche qui detient le secret le remplit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
Icinga Web 2 gagne un troisieme mode, locale : nginx authentifie en HTTP Basic
et pose REMOTE_USER, l application le croit. Un SITE n a ni annuaire ni Keycloak
— ce sont des services d ecosysteme. Ni backend LDAP ni backend de groupes : un
backend qui vise une ressource inexistante fait echouer chaque ouverture de
session. L habilitation nomme alors une personne, entorse a D-66 ecrite plutot
que contournee.
Deux pieges en chemin : resoudre_annuaire etait appele sans condition et tombait
sur NoneType has no len (default sans son second argument ne remplace pas None),
et le flux du role ne nommait que l edge — le site n en a pas, donc personne ne
pouvait entrer. serveur_grafana portait deja la reponse.
LE DEFAUT DU DEVIS : une sortie vers un role ABSENT de l ecosysteme retombait
sur !SETOPS_INTERNES, la forme de vers l Internet. La frontiere aurait autorise
la console a parler LDAPS a n importe quelle machine du monde, pour joindre un
annuaire qui n existe pas. C est une source vide ouvre le port, cote
destination. Trois regles du meme defaut etaient DEJA posees pour postfix.
La frontiere n est pas ecrite : elle porte la production, et le devis attend un
mot.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
La supervision du SITE etait morte depuis 11:10 et rien ne le disait. icingadb
refuse par pg_hba — hostssl impose cote serveur, connexion en clair cote client.
icinga2 tournait, redis tournait, les sondes poussaient, et rien n atteignait la
base : les verdicts se calculaient dans le vide.
La cause est une seconde liste tenue a la main. tls_force allume hostssl ; chaque
consommateur avait SON interrupteur a allumer dans les group_vars. Chezlepro
avait les trois, le site avait le premier. serveur_forgejo disait pire que rien :
sslmode disable ecrit en dur, le contraire de ce que le serveur imposait.
resoudre_base expose resoudre_base_db_tls_force, lu dans les hostvars de la
machine qui PORTE la base. Les trois interrupteurs en derivent. Un serveur qui
ne declare rien ne force rien : on ne casse pas un ecosysteme qui n a pas
bascule.
P78 refuse une valeur ecrite chez un consommateur, et nomme les deux roles sans
reglage TLS plutot que de rendre un vert muet sur eux.
Apres : icingadb active, TLSv1.3 vu par PostgreSQL, 16 hotes et 88 services en
base. Les cinq sondes des marqueurs du site, INCONNU faute de deploiement,
rapportent leur phrase.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
Le plan du site expose observatoire.genese.internal ; serveur_grafana devinait
grafana.<domaine>. Grafana fabriquait son root_url, ses liens d alerte et son
URL de retour OIDC avec un nom que rien ne sert.
C est mot pour mot le defaut du 2026-09-10, quatre jours plus tard, de l autre
cote : instancier derive <groupe>_hostname depuis ce jour-la et P67 le garde,
mais tous deux ne connaissent que l instance montee — donc un locataire.
L inventaire du site n en derivait aucun. Quatre devinettes sur cinq tombaient
juste par convention, et c est ce qui rendait le defaut invisible.
site_inventaire.py derive les cinq noms du plan. P67 lit maintenant les deux
inventaires — celui du site est dynamique, donc elle l EXECUTE au lieu de lire
sa source. Eprouvee dans les deux sens : 10 services sur 2 inventaires.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
Le gabarit de voute reclamait cette cle pour serveur_postgresql ; la voute reelle
du site ne l avait pas. Le gabarit disait quoi mettre, personne ne l avait mis.
40 caracteres alphanumeriques : le mot de passe entre dans une URI de connexion
PostgreSQL, et un @ ou un / y couperait l URI en deux — l echec dirait mauvais
mot de passe au lieu d URI mal formee.
Gardes passees : copie de surete, dechiffrement vers un FICHIER jamais vers un
tube, relecture du YAML, en-tete verifie apres, empreinte comparee, 15 cles
relues, clair passe au shred. Le premier rechiffrement produisait du 1.2
etiquete la ou la voute portait du 1.1 — refait sans etiquette, parce que le
runner du site ouvre avec un unique fichier-cle.
Compte setops_metriques : ni superuser, ni createdb, ni createrole, membre de
pg_monitor et rien d autre.
Les huit panneaux des deux tableaux derives repondent, mesures sur le site.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
Le tableau des panneaux vivait DANS la condition de l exportateur. Le premier
role a declarer des panneaux sans exportateur — client_metrique, dont le job
node est universel — affichait « aucun exportateur declare » et cachait ses
quatre panneaux.
Une fiche qui tait ce qu un role declare est pire qu une fiche absente : elle
affirme que rien n existe.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
La moitie qui manquait. Les roles declaraient leurs panneaux, Prometheus en
derivait ses cibles, les expressions repondaient, et rien ne les assemblait.
serveur_grafana lit les memes meta/metriques.yml que serveur_prometheus et en
assemble un tableau par role — la raison du panneau devient sa description dans
Grafana. Moisson des tableaux orphelins, prefixe setops-role- pour ne jamais
toucher un tableau ecrit a la main, JSON valide avant d etre pose (Grafana ne
tombe pas sur un tableau illisible : il le saute en silence).
client_metrique declare quatre panneaux sans exportateur — le cas symetrique.
Celui qui compte : la memoire disponible, depuis que le ballon est actif.
Eprouve sur le site : les deux tableaux charges dans le stockage unifie, un
temoin orphelin retire, les quatre panneaux de flotte a 10 series chacun.
Un panneau etait faux — /var/lib/lxcfs rapporte toujours zero octet libre et un
min() en meurt. Corrige en liste blanche : un montage inconnu manque au graphe,
ce qui se voit, au lieu de l ecraser.
P77 garde la table des unites : une unite inventee retombe sur short, lisible et
fausse. 76 OK.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
La cible lisait $$remote — une variable de shell que rien ne definit — au lieu
de $(WIKI_REMOTE). L echappatoire que le message d erreur proposait lui-meme
n existait pas.
WIKI_BRANCHE, deux cibles plus bas, etait ecrit correctement depuis le debut.
Une option qui se lit autrement que sa voisine est l endroit ou regarder.
Wiki publie sur eregion : 95 pages, les huit fiches neuves portent leur sonde —
verifie en clonant la forge, pas en lisant le temoin.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
Les 33 roles serveur_* declarent maintenant une sonde. Les huit qui manquaient
sont ceux dont la verite ne ressemble pas a « ce service repond-il ».
Quatre marqueurs du site : ce que tasks/main.yml verifie UNE FOIS au deploiement
cesse d etre vrai sans que rien ne tombe. La racine du cache se retrouve chainee,
la forge du genome repond en n ayant plus rien dedans, un locataire n est plus
admis a resoudre, l isolation d un depot glisse.
serveur_ops_site ne sert rien : il detient un pouvoir. La sonde verifie que la
carte est la, que la voute du site est chiffree et que sa cle est en 0600 — sans
lire le contenu d aucun des trois.
serveur_icingaweb2 surveille la vitrine de la supervision elle-meme : si la
console meurt, tout reste vert et l exploitant est aveugle.
DEFAUT 1 — quatre gabarits qu Ansible aurait refuse de rendre. Jinja lit le
{# de ${#tableau[@]} comme un debut de commentaire. Le depot connaissait le
remede et l appliquait la ou quelqu un s etait fait prendre, nulle part ailleurs.
P76 rend desormais chaque gabarit de role, avec les delimiteurs qu Ansible en
tirerait — pas une recherche de motif.
DEFAUT 2 — la doctrine promettait 54 greffons et citait check_pgsql. La flotte a
monitoring-plugins-basic : 53, sans check_pgsql ni check_dns ni check_ldap. Le
paquet qui les porte traine samba et snmp sur chaque machine. Un greffon absent
sort en 127, qui n est pas un code Nagios.
21 controles negatifs sur les machines reelles du site. ansible-lint production
0/91, harnais 75 OK.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
Trois plans de locataire et cinq modeles perdent leur forge ou leur cache
d artefacts. Une machine de moins par ecosysteme neuf : origine passe de cinq
a quatre, et tout ecosysteme neuf en descend.
Retirer un service, ce sont cinq points d attache — le service, sa machine, sa
base, son client SSO, sa configuration de role. En oublier un laisse un
inventaire qui se genere et un deploiement qui echoue plus tard.
Preuve statique apres coup : 74 OK, 0 echec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
J allais centraliser — un controleur poussant vers N forges, un temoin portant
une liste. Ca aurait demande un acces sur chaque forge depuis un seul poste, et
fait du temoin un fait global que personne ne detient.
Le modele suit la ligne du reste : chacun sert les siens. Sans WIKI_REMOTE,
l adresse se derive du nom que le plan monte expose pour serveur_forgejo.
Deux notions a ne pas confondre : la forge AMONT ou l on LIT le genome, et MA
forge ou l on SERT les siens. Le runner d un locataire lit chez son hebergeur et
sert chez lui.
WIKI_REMOTE l emporte toujours : le poste du mainteneur n est le runner d aucun
ecosysteme et publie vers le domicile public du projet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
make wiki-publier a refuse : le wiki vise par le temoin — la forge du SITE —
n a aucun commit. Le wiki vivant est sur eregion, 26 pages, branche main. Le
temoin affirmait depuis le 12 avoir publie sur une forge dont le depot wiki n a
pas survecu a sa reconstruction.
C est ce que P60 annonce d elle-meme : un temoin dit ce qui est PARTI, jamais ce
qui est ARRIVE. La preuve etait verte et la documentation n existait nulle part
a l adresse qu elle nommait.
Le garde-fou a tenu : publier sur un wiki vide aurait invente un nom de branche.
95 pages en ligne, dont les 68 fiches et leur index.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
Generees depuis ce que chaque role declare, avec un schema mermaid et la raison
de chaque ligne. Une section vide est une information : l index recompte ce que
la flotte ne declare pas — 36 sans flux, 40 sans sonde, 67 sans metrique.
La charte du wiki disait de ne pas recopier le depot, pour eviter la derive. Le
motif ne vaut pas pour une page qui relit sa source ; l exception est nommee
plutot que prise en silence.
La navigation exigeait une citation DIRECTE alors que son motif parle
d atteignabilite. L exigence litterale interdisait toute page d index — la garde
forcait a degrader ce qu elle protegeait. Elle suit maintenant les liens de
proche en proche, et son motif reconnait le souligne : un motif trop etroit ne
rend pas une garde prudente, il la rend aveugle.
Et le compte du wiki serait passe de 27 a 96 : une fiche generee n est pas une
unite d apprentissage.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
Une preuve a mordu : un script qu aucune cible n appelle est du code mort. Elle
avait raison.
68 fiches generees. Le vide y est une information — la carte de ce qui reste a
faire, tenue a jour toute seule.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
68 roles. Autant de pages faites a la main seraient perimees avant la fin du
mois — c est le defaut que ce depot traque partout sous le nom une liste qui
suit une autre prend du retard.
La fiche lit meta/flux, supervision, metriques, empreinte, authentification. Un
schema mermaid au centre, puis les tableaux, et chaque ligne porte la RAISON que
le role a declaree.
Une section vide est une information, pas un defaut de la fiche : elle devient
la carte de ce qui reste a faire, tenue a jour toute seule.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
supervision-conception disait deja qu une metrique a seuil appartient a
Prometheus et Grafana. Ce document est l autre moitie.
Il porte le critere des panneaux — une serie a sa place si elle PRECEDE un
verdict ou si elle n en aura JAMAIS — le compte de service au moindre droit, la
chaine de connexion hors ligne de commande, et la dette du chiffrement ecrite
plutot que tue.
La carte nomme desormais les deux ensemble : deux questions, deux fichiers meta,
un meme patron.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
Aucune metrique de SERVICE n etait collectee — seulement du systeme. Le crochet
serveur_prometheus_cibles_supplementaires existait, documente, et personne ne le
remplissait.
Le pendant de meta/supervision.yml et son contraire : une sonde rend un verdict
avec un TTL, un exportateur expose une serie. Est-ce casse, contre depuis quand
et vers ou.
Le critere des panneaux : une serie a sa place si elle PRECEDE un verdict ou si
elle n en aura JAMAIS. Le taux de succes du cache n en aura jamais — quand les
donnees depassent shared_buffers, rien ne casse et tout devient lent.
Compte en lecture seule (pg_monitor), et flux en clair avec sa dette inscrite.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
playbooks/site.yml ordonne les couches pour n importe quelle instance. Mais
deployer-tout ne vise que l inventaire d un locataire, et le site n avait que
site-appliquer GROUPE=<un seul>.
Un hebergeur qu on ne peut remonter qu en enchainant onze groupes de memoire
n est pas reconstructible : il est reparable par quelqu un qui se souvient.
Et quatre gardes que --check rendait folles, toutes de la meme famille : une
garde qui compare contre ce qu une tache du meme play vient de produire n a rien
a dire tant que rien n a ete ecrit. Sept machines en echec sur un site sain, en
suivant le conseil du Makefile lui-meme.
Apres correction : 7/7, 0 echec, de 174 a 309 taches par machine.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
Les instantanes sont chiffres cote client : ce serveur heberge des octets qu il
ne peut pas ouvrir, donc pas juger. La verification suit la cle, et
client_backup la fait deja depuis chaque noeud.
Ce que la sonde ajoute : elle voit TOUT DE SUITE, et depuis la cause, ce que les
clients ne decouvriront qu a leur prochaine execution. Lecture seule apres une
erreur disque, volume plein, droits derives — le depot refuse alors tout le
monde, et neuf rouges epars ne designent pas une cause commune.
Elle ECRIT vraiment, sous l identite qui depose. Un test -w ment sur un montage
en lecture seule et sur un quota atteint.
Corrige aussi l en-tete de setops-sauvegardes.conf.j2, qui nommait encore
backup-01 comme pousseur — retire le 2026-09-02, et c est tout le sujet. Le code
avait suivi la decision, l en-tete non.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
La doctrine dit qu un greffon Nagios EST une sonde valide et compte sur les 54
de monitoring-plugins. Mesure : aucun n etait installe. Le document decrivait
une possibilite qui n existait pas, et chaque role ayant besoin d un controle
HTTP n avait d autre choix que d ecrire du shell.
Le PORTEUR les installe desormais — c est son affaire de pouvoir executer des
sondes, et les poser role par role en ferait autant de copies de la decision.
Trois sondes, trois enveloppes qui delegue a check_http et rendent son code tel
quel :
collabora edition /hosting/discovery — l adresse que Nextcloud interroge
lui-meme ; sans elle le bouton ouvrir meurt et
Nextcloud reste vert
rspamd filtrage un filtre muet ne bloque pas le courrier, il le laisse
passer
oauth2_proxy passerelle ce qui tombe avec elle n est pas elle : les services
derriere restent debout et deviennent injoignables
Chacune exige une CHAINE que seul le bon service produit — un 200 peut venir
d une page d erreur. Cette chaine est aussi la mise en defaut.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q