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
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
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
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
`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
`opnsense.yml` decrivait le monde physique depuis les group_vars d'un tenant. Le devis et
le GUI le cherchent desormais d'abord a la racine du depot de site, comme underlay.yml et
proxmox-hebergeur.yml, par la meme derivation depuis le symlink.
CE QUE LE MAUVAIS RANGEMENT A COUTE : l'adresse d'API de la frontiere y etait restee a
10.0.0.1 apres migration vers 10.17.0.1. `make frontiere-appliquer` restait suspendu sur
une adresse morte, sans aucun message — trouve par l'exploitant en lancant la commande
dans son terminal, apres que j'aie moi-meme conclu deux fois a tort.
DEUX LECONS, ecrites plutot que corrigees en silence :
- un objet range chez celui qui n'en est pas responsable derive sans que personne le voie ;
- mes commandes s'executent dans ma session : l'exploitant ne voit pas leur sortie. Une
commande lente ressemble alors a un blocage, et un blocage a une commande lente. Pour
toute ecriture longue sur du materiel, c'est a lui de la lancer.
Les anciens emplacements restent lus : un site pas encore migre continue de fonctionner.
make verifier 41/41.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Cinq jours, cinq defauts, tous de la meme famille : « quelle instance, quel inventaire ? »
Neuf modules portaient chacun leur reponse.
- 18 aout : P03 comparait chaque instance a l'inventaire d'une AUTRE ;
- 19 aout : verifier_ports codait `principal/` en dur ; verifier_intrants et
_frontiere_absente lisaient le symlink au lieu de la variable ;
- 20 aout : devis_placement rendait un verdict juste sur le mauvais tenant ;
- 22 aout : P35, puis P36 — la dixieme, trouvee par la preuve elle-meme.
Aucune n'etait une faute d'inattention : chacune avait ete ecrite de bonne foi, a un
moment ou le besoin semblait local. C'est le mode de panne de la duplication — pas
l'erreur, mais la DERIVE, invisible depuis l'interieur d'un fichier.
LA RESOLUTION UNIQUE. `inventory_rules` porte instance_courante(), inventaire_de(),
dossier_inventaire() et plan_de(). Trois niveaux de repli, dont le TROISIEME manquait a la
moitie des copies : un hosts.yml existant, puis un REPERTOIRE existant (instance neuve —
c'est ce qui faisait echouer `make instancier` sur le modele public), puis le defaut.
Vingt-huit modules y sont branches.
CE QUI REND CE REFACTOR SUR : avant de toucher quoi que ce soit, chaque module a ete
interroge sur ce qu'il resolvait, pour les DEUX ecosystemes. Apres refactor, meme mesure :
17 modules x 2 instances, diff VIDE. Aucune resolution n'a change — prouve, pas suppose.
P41 echoue des qu'un module reintroduit une copie. Eprouvee en negatif : une copie
replacee dans genome.py est signalee avec son numero de ligne. Trois exemptions nommees :
instances.py et inventory_gui.py manipulent le SYMLINK lui-meme (bascule d'instance), et
devis_opnsense lit deliberement quelle instance est ACTIVE. Elles parlent du lien, pas de
la resolution.
make verifier 41 OK, 0 echec, 0 saute ; make ci idem ; lint vert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le 121 etait le total des lignes SUPPRIMEES au diff — il comptait aussi les lignes de
valeur deplacees par le retri des clefs. Compte des seules lignes de commentaire, les
quatre fichiers de l'enregistrement de 13:48 : 129 -> 35, dont ce qui reste est l'entete
que le panneau reecrit lui-meme. Soit 94.
Rien ne change au correctif ni a sa mesure (41 -> 41 lignes, 30 -> 30 commentaires, diff
d'une ligne). Un depot qui mesure ce qu'il affirme ne garde pas un chiffre approximatif.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Un enregistrement du panneau « Intrants de base », a 13:48, a emporte 121 lignes
d'explications dans quatre fichiers — dont celle qui disait POURQUOI la valeur qu'on
venait de changer avait ete choisie (le plan de gestion reste en 10.0.0.0/24 tant que la
frontiere ne sait pas classer un second CIDR, D-61). Ces phrases sont la seule trace de
raisonnements qu'aucun code ne redit. `safe_dump` les effacait toutes a chaque
sauvegarde, en retriant les clefs au passage.
LE DEPOT CONNAISSAIT DEJA LE GESTE JUSTE. `_ecrire_intrants_fabric` (underlay.yml) et
`_ecrire_index_nomenclature` remplacent LA LIGNE sans toucher au reste ; leur commentaire
dit meme « un safe_dump les effacerait toutes ». Les quatre fichiers d'intrants n'avaient
jamais recu ce traitement. Ce qui manquait pour l'etendre : savoir remplacer une valeur
de LISTE, qui tient sur plusieurs lignes.
`_fusion_chirurgicale`, trois regles :
- une clef dont la valeur ne change pas n'est PAS reecrite (zero bruit au diff) ;
- les commentaires internes a un bloc remplace sont conserves, jamais juges ;
- une clef absente du fichier est ajoutee a la fin, jamais inseree au hasard.
La deuxieme est assumee : une explication devenue fausse survit a la valeur qu'elle
explique. Corriger une phrase est un geste humain ; l'effacer parce qu'un champ a bouge,
non. Meme principe que la fusion des clefs posee le 2026-08-10.
EPROUVE SUR LE FICHIER REEL, pas sur un exemple : l'enregistrement de 13:48 rejoue sur la
version d'avant (tiree de git) rend 41 lignes -> 41, 30 commentaires -> 30, 0 perdu, et un
diff d'UNE ligne. Neuf tests dans scripts/tests/test_gui_intrants.py, branches sur
`make test`.
DEUX FOIS LE MEME GESTE DESTRUCTEUR, SUR LE MEME CHEMIN : le 10 aout ce panneau perdait
des CLEFS (dns_amorcage, amorcage_acces_courriel — une VM qui nait sans resolution), le
18 des COMMENTAIRES. La premiere fois avait valu une fusion, pas un test. C'est le test
qui manquait.
Non touche, et dit comme tel : les registres (bases, applications, serveurs, domaines)
passent toujours par safe_dump. Ils sont structures et n'ont pas perdu de commentaires au
meme enregistrement — a reprendre si l'un d'eux en porte un jour.
make test 9/9 sur le nouveau fichier ; prouver 37 OK, 0 echec, 0 saute.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Question de l'exploitant apres la decouverte de vmbr3 : a quoi sert cet intrant ?
Mesure : a presque rien.
proxmox_pont 14 occurrences dans l'inventaire -> DERIVE par hote (VNet de zone)
proxmox_noeud 0 -> proxmox_clone_noeud est la vraie valeur
proxmox_stockage 0 -> proxmox_clone_stockage est la vraie valeur
instancier pose le VNet de chaque zone dans proxmox_pont, et l'hote l'emporte sur
le defaut. proxmox_clone_pont n'est donc consulte que par un `make cloner-vm`
manuel, hors flotte. C'est exactement pourquoi vmbr3 a pu y etre faux dix jours.
C'est le pire genre d'intrant : visible dans le GUI, on le corrige, on redeploie,
rien ne change. L'intitule dit desormais sa portee.
D-80 CORRIGEE : j'y avais ecrit « trois cles : noeud, stockage, pont ». Faux pour
le pont. La liaison de placement reelle est noeud, stockage et gabarit ; le pont
se derive comme le reste.
Verifier avant d'enumerer : j'avais liste les cles en lisant le fichier du tenant,
sans regarder lesquelles sont reellement consultees. Deux le sont, une ne l'est
pas — et c'est celle qui etait fausse.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
supernet_de rendait 10.(10+index).0.0/16. Personne ne savait plus pourquoi : ni le
commentaire de la constante, ni le wiki, ni le commit fondateur 36a882b ne le
justifiaient. Trois endroits consultes, zero raison ecrite.
Ses deux effets constates :
- il reservait 10.0-10.9 sous la plage tenant. Utile tant que l'underlay vivait
la — mais D-77 l'a fait entrer dans la bande basse de son propre /16, ce qui a
vide cette reserve de son role la veille ;
- il eloignait le premier tenant de 10.0.0.0/16, la plage la plus repandue en
reseau domestique. Ce risque revient donc aux index bas, et c'est ASSUME.
En echange l'index se lit directement dans l'adresse (17 -> 10.17.x.x) et le
plafond passe de 245 a 255 ecosystemes federes.
Chezlepro (17) 10.27.0.0/16 -> 10.17.0.0/16
Technolibre (11) 10.21.0.0/16 -> 10.11.0.0/16
lab (1) 10.11.0.0/16 -> 10.1.0.0/16
Doc alignee : les trois pages du wiki, multi-instances.md (plafond et exemple
devenus faux arithmetiquement), sdn-evpn.md, le libelle de la GUI, la docstring
d'underlay.py, D-77, et le document de preparation d'un site hebergeur. Les
CONSTATS DE TERRAIN dates sont laisses tels quels : ce sont des mesures.
CE COMMIT NE RENUMEROTE RIEN. Il change ce que le plan DERIVE ; l'inventaire
applique porte toujours 10.27.x.x et les quatorze VM tournent dessus. Appliquer
sans reconstruire rendrait la flotte injoignable — le renumerotage est une
operation a part, a mener a froid.
Au passage, retire un debris : une copie de conflit Nextcloud de
serveur_powerdns/defaults/main.yml, IDENTIQUE a l'original, commitee par accident
dans 1295eea et jamais chargee par Ansible.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Les deux reconstructions de la semaine rebatissaient Chezlepro sur son propre
materiel : une preuve de reproductibilite, pas de portabilite. La vraie
epreuve est un second tenant — Technolibre, index 11, plan distinct, voute
separee, meme cluster.
1. LE CLONAGE RESOLVAIT PAR NOM. proxmox_kvm cherche une VM portant le `name`
demande ; s'il en trouve une il rend `ok` et ne clone RIEN — aucune tache
cote cluster. Or les noms courts sont VOLONTAIREMENT identiques d'un tenant a
l'autre. `backup-01` de Technolibre tombait sur celui de Chezlepro. Mesure :
id-ldap-01 et sup-01 (noms absents chez Chezlepro) passaient du premier coup,
backup-01 echouait toujours. Le clonage cible desormais par VMID, via l'API.
Deux defauts de ce correctif, trouves en le mesurant : le corps assemble en
Jinja avec `>-` rendait une CHAINE et perdait le `pool` (VM nee hors de son
pool, sans un mot) ; et l'application du gabarit de calcul expirait a 5 s,
laissant la VM aux valeurs du gabarit — 2 coeurs/2 Go au lieu du plan, en
silence. Corps en mapping YAML avec omit ; six tentatives espacees.
Et `no_log` a masque la cause au moment ou elle servait. Les deux attentes
disent maintenant ce qu'elles ont constate.
2. LE GUI DETRUISAIT DES INTRANTS. Dans ecrire_intrants, `identite` etait la
seule branche sur quatre a ecrire par-dessus le disque au lieu de fusionner.
Un enregistrement a supprime dns_amorcage et amorcage_acces_courriel. Le meme
geste sur Chezlepro aurait mange les memes cles.
3. LE VERROU DE `raser` N'ETAIT PROUVE QUE POUR UN TENANT. Le faux cluster
codait en dur les VMID de Chezlepro ; monte ailleurs, le test rendait 0 au
lieu de 2. Il fabrique desormais la collision sur le plan courant.
Verifie : prouver.py 34 OK sur Technolibre, test_raser vert sur les deux
tenants, ansible-lint production sur le playbook de clonage.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Pas de refonte : 54 roles / 54 README, 34 documents, une carte, un registre de
decisions. Le retard etait ailleurs — le travail du jour vivait dans son coin,
les cinq devis n'existant que dans deux fichiers. Donc decouvrables seulement
par qui connait le Makefile, ce qui contredit « exploitable sans IA ».
Tisses dans les quatre points d'entree : ligne « Conformite du deploye » dans
la carte, section « Ecrire, puis relire (D-68) » dans AGENTS.md, §6.0 du
runbook (le premier reflexe), vue Reconstruction de la GUI.
Le tissage a fait tomber trois affirmations perimees :
- la carte annoncait 28 decisions, il y en a 66 en vigueur (D-01 -> D-69) ;
- elle disait les acces « decides, non construits, ou=people et ou=groups
restent vides » — mesure : un compte, un groupe, chaine exercee de bout en
bout sur Icinga Web 2 le jour meme ;
- la GUI parlait des « deux » devis d'infrastructure ; il y en a quatre.
Formation et wiki differes : la reconstruction from-zero est le test de cette
documentation, et enseigner une procedure que personne n'a executee serait
enseigner une hypothese.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le devis rangeait tout flux entrant sur le WAN, en supposant que
l'administration revenait par l'adresse publique. Vrai pour 192.168.255.0/24,
faux depuis que c'est 10.0.0.0/24 — directement attache sur `lan`. Les deux
regles SSH etaient mortes deux fois : mauvaise interface, et « Block private
networks » les aurait filtrees. Le devis conseillait meme de decocher ce filtre
sur le WAN, ce qui aurait affaibli l'interface publique pour rien.
`reseaux_locaux_frontiere()` derive de l'underlay les sous-reseaux ou la
frontiere porte une adresse, hors transit. Un alias par interface : Technolibre
a les deux cotes, Chezlepro seulement la gestion. Sans underlay, tout retombe
sur le WAN — comportement inchange.
P24 confronte desormais chaque regle d'administration a l'attachement de sa
source, et refuse l'ancien comportement.
Nouvel intrant `opnsense_if_gestion`, au catalogue du GUI.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Deux corrections de propriété, l'une dans le plan, l'autre dans les intrants.
1. Intégrations universelles (D-33/D-34, P26)
Le plan portait 57 lignes d'intégration écrites à la main, dont 28 disaient oui à
quelque chose de vrai pour tous les hôtes. Elles n'existaient que pour être
oubliées — et elles l'avaient été : dans Chezlepro, backup-01 et infra-pki-01
n'étaient ni supervisés, ni journalisés, ni certifiés.
Le rôle déclare désormais sa politique une fois, dans meta/integration.yml ; le
plan ne garde que les vrais choix et refuse la recopie. Les exemptions se
dérivent du service rendu (sauf_role), jamais d'un nom d'hôte : l'AC ne s'enrôle
pas auprès d'elle-même, et l'exemption suit step-ca si on le déplace.
Une seule fonction de résolution — integrations_de() — lue par l'inventaire, la
voûte et le panneau. Sans le passage par la voûte, les secrets des intégrations
universelles auraient cessé d'être exigés et P18 serait passé au vert sur une
voûte incomplète.
Vérifié : diff vide sur Technolibre (la politique reproduit exactement les 41
lignes retirées) ; sur Chezlepro, exactement les groupes manquants, et pas
client_pki sur infra-pki-01.
2. Vue Intégrations : la matrice
La fiche montrait les intégrations d'UN serveur ; le trou de Chezlepro n'a pas
été trouvé par le panneau mais par le devis de pare-feu. Matrice serveurs x
intégrations : colonnes de politique en lecture seule, facultatives cochables sur
place, ligne de couverture n/N qui rend le motif visible sans le juger.
3. Propriété des intrants (D-35/D-36, P27)
Le cluster Proxmox appartient à l'hébergeur, comme sa fabric et sa frontière.
Recopié chez chaque tenant, son inventaire avait déjà divergé : deux listes de
stockages contradictoires pour le même matériel. API/nœuds/stockages/ponts vont
dans proxmox-hebergeur.yml, à côté d'underlay.yml, dont le chemin se dérive —
l'hébergeur reste non déclaré (D-17). Restent au tenant son golden template et
ses défauts de placement.
Le panneau nomme désormais le propriétaire de chaque section : éditer une section
« hébergeur » vaut pour tous ses tenants, et l'écran ne le disait pas.
26 preuves OK, 0 échec. --syntax-check des deux playbooks Proxmox.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
La vue Réseau n'affichait que le devis des commutateurs. Le devis frontière
était totalement absent de l'interface, alors qu'il porte les règles de la
bordure et ses avertissements — dont celui sur « Block private networks »,
invisible dans les règles elles-mêmes.
Ajouté `/api/devis-opnsense` et son bloc d'affichage avec bouton de copie.
Vérifié par HTTP que les deux points d'API servent exactement ce que le CLI
produit : 181 et 125 lignes, identiques au caractère près. Import paresseux
et gardé — ce module lit l'underlay et les inventaires de tous les tenants,
une erreur aurait sinon vidé la vue entière.
Aide corrigée sur trois points périmés : les VLAN tenants annoncés « uniques
sur le trunk » (faux en SDN, aucun n'y circule) ; le dialecte renvoyé à une
variable d'environnement alors que c'est un intrant de la section Fabric ; et
la route par défaut dite « à adapter » alors que la section 5 l'émet.
Elle dit maintenant ce qui reste à nommer à la main : les ports physiques et,
en SDN, le nœud de sortie EVPN.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Un hébergeur sert plusieurs tenants et n'a qu'une frontière. Ses intrants
étaient lus chez le tenant actif : basculer sur un invité — Technolibre, qui
n'a pas de frontière à lui — faisait perdre au devis l'URL de gestion,
l'adresse publique et les deux interfaces. Il repartait en marqueurs, comme
si le boîtier n'existait pas. Vérifié en simulant la bascule.
Même famille que le défaut de l'underlay corrigé plus tôt ; c'est la
distinction hébergeur/tenant qui le fait apparaître.
Le devis et le panneau lisent maintenant la frontière chez l'hébergeur, qui
n'est pas déclaré pour autant : le symlink `underlay.yml` le désigne déjà.
Repli sur l'instance active sans underlay monté.
Vérifié : devis identique avec l'hébergeur actif, intrants conservés avec un
invité actif.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Il vivait gitignoré à la racine du moteur : consommé par deux générateurs,
validé par P23, versionné nulle part. La dizaine de modifications de la
journée n'était récupérable d'aucune façon, et un clone frais repartait du
gabarit.
Il appartient à l'hébergeur — ses switches, ses câbles — pas au moteur, qui
est générique, ni à un tenant, qui n'en possède pas. Chezlepro est ici
hébergeur ET tenant, d'où la confusion initiale.
Le moteur le monte par symlink comme il monte le plan par `instance/`. Ce
lien NE SUIT PAS `make instance-utiliser` : la fabric reste celle de
l'hébergeur quel que soit le tenant actif.
Vérifié : les deux devis sortent identiques octet pour octet avant et après,
P23 verte, 24 preuves. Symlink brisé (clone sans dépôt frère) : dégrade
proprement, sections omises au lieu d'échouer. Cas exercé.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Dans OPNsense une règle est toujours `in` sur l'interface d'arrivée : posée
ailleurs elle ne s'applique jamais, et le trafic est bloqué sans que la
configuration paraisse anormale. Les règles n'en portaient aucune, alors que
le champ est obligatoire dans l'API.
L'attribution se dérive du sens du flux : `ingress`/`externe` arrive par le
WAN, `egress`/`externe` par le lien de transit. Le SSH d'administration suit
la première ligne — le VPN est hébergé sur le pfSense voisin et revient par
l'adresse publique. Le montage parallèle décrit ce jour lève la dernière
inconnue.
Le rendu abandonne `pass out` pour `pass in on <interface>`, l'idiome réel
d'OPNsense et ce que le client d'API devra envoyer.
Ajouté aussi :
- `opnsense_wan_ip`, la face publique, en section Frontière du panneau ;
- invariant du dernier octet (P23) : un point de routage porte le même
dernier octet sur tous ses sous-réseaux. Le chiffre vient de
`reservations.passerelle`, pas d'une constante. Exemption des liens plus
étroits qu'un /24 — sur le /29 de transit l'adressage est dicté par les
participants. Vérifié que l'invariant tenait déjà sur les 13 sous-réseaux
routés avant d'écrire la garde.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`opt1`, `igb1` et `TENANTS` désignent le même port — identifiant interne,
périphérique FreeBSD, libellé affiché. L'API REST ne parle que du premier.
Les libellés des deux intrants ne le disaient pas, et la question s'est
posée en pratique.
Ils le disent maintenant, et docs/frontiere-opnsense.md explique les trois
couches et pourquoi `opt1` est le plus stable : il survit à un changement de
carte comme à un renommage.
La note « prochain_saut dérive de l'underlay » est repliée dans l'en-tête
régénéré par le panneau : une sauvegarde l'effaçait, le fichier étant
réécrit depuis le YAML analysé. Vérifié qu'une sauvegarde préserve valeurs
et références de voûte.
Corrigé au passage : après le renumérotage du /29, deux passages de la doc
annonçaient encore 10.0.4.2 comme prochain saut et contredisaient le devis.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le rôle dérive l'empreinte à chaud depuis l'autorité, parce qu'un from-zero
régénère l'AC avec une empreinte neuve. Figée en voûte, elle serait périmée
dès la première reconstruction — et une empreinte périmée fait échouer le
bootstrap de chaque hôte.
Or `defaults/main.yml` portait encore un défaut lisant
`vault_step_ca_fingerprint`. Ce défaut était mort : la tâche suivante écrase
le fait sans condition, donc la valeur de la voûte n'avait aucun effet.
Le recensement de voute.py s'y laissait prendre — il cherche la chaîne
`vault_*` sans pouvoir savoir qu'un défaut n'est jamais lu. La « source
unique » avait hérité de l'erreur, et le panneau réclamait un secret
impossible à fournir avant que l'AC n'existe.
Défaut retiré : la clé disparaît du recensement (25 -> 24 exigés), du
gabarit et du panneau. `client_pki_ca_fingerprint_override` reste le moyen
d'épingler une empreinte.
`docs/intrants-communs.md` §H était une troisième copie manuelle de la liste
des secrets, avec les deux mêmes erreurs ; elle renvoie maintenant à
`voute.py lister` et à la preuve P18.
Preuves : 24 OK, 0 échec ; voute.py verifier --strict passe.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`SECRETS_ATTENDUS` était une liste tenue à la main, en parallèle du
recensement que voute.py fait déjà depuis le plan, les rôles et les
group_vars. Deux sources pour la même vérité, et la manuelle avait divergé :
elle annonçait `vault_ldap_sssd`, que rien ne consomme — aucun rôle sssd
n'existe — et omettait `vault_step_ca_fingerprint`, réellement lu par
client_pki pour vérifier l'empreinte de l'AC racine.
Un opérateur qui suivait le panneau créait un secret inutile et en oubliait
un nécessaire.
Le rappel dérive maintenant de `voute.secrets_exiges()` — la même source que
la preuve P18 — augmentée de SECRETS_HORS_MOTIF pour les jetons Proxmox, qui
n'ont pas le préfixe `vault_`. Vérifié : 27 noms, écart nul avec le gabarit.
Dépôt public nu : liste vide plutôt qu'une erreur d'affichage.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Tout le modèle d'underlay bâti aujourd'hui s'éditait à la main dans un YAML,
pendant que la doctrine dit qu'un sysadmin doit exploiter l'outil sans IA.
La frontière avait eu sa section ; l'underlay, non.
La section couvre les valeurs plates : switch routeur, dialecte de CLI, mode
et topologie de spanning-tree. Les listes de tables (`reseaux`, `hotes`, donc
les ports) restent hors de portée du panneau — elles demandent une vue
dédiée, comme celle des serveurs.
Le dialecte devient un intrant déclaré : il ne vivait que dans
SETOPS_DIALECTE. C'est une propriété du matériel, donc de la fabric.
Précédence : `--dialecte` > environnement > intrant déclaré > cisco.
Écriture chirurgicale plutôt que safe_dump : underlay.yml porte 23 lignes de
commentaires qui expliquent des décisions d'architecture, et un dump les
aurait effacées comme c'est arrivé à plan/applications.yml. Vérifié : trois
valeurs modifiées, 73 lignes et 23 commentaires avant comme après. Une clef
absente du fichier est refusée plutôt qu'inventée à un endroit arbitraire.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`opnsense_prochain_saut` quitte le panneau Intrants. Il dérive du réseau de
transit de l'underlay — la `passerelle` du réseau portant `passerelle_sortie`.
Un seul bloc alimente les deux devis : 10.0.4.1 est le SVI côté switch ET le
prochain saut des routes tenants côté frontière ; 10.0.4.2 est la route par
défaut du switch. Le saisir en doublon rouvrait la possibilité de deux
valeurs contradictoires pour un seul lien — le mode de panne qu'on venait de
fermer pour les réseaux d'administration.
Le devis frontière expose le bloc `transit` (JSON compris) et cesse de
réclamer en section 0 les routes que `devis-reseau` émet désormais : il dit
lesquelles sont déjà émises, ou signale l'absence de transit déclaré. Sans
underlay, le marqueur revient — le repli reste explicite.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
La bordure devient un artefact dérivé, comme le devis switch — et le chemin
qui y mène est enfin déclaré.
`make devis-opnsense` (+ preuve P24) dérive la politique de bordure du
registre des flux : les flux `pair: externe`, que `resoudre_flux.py` saute
volontairement parce qu'ils relèvent de la frontière et non du pare-feu
d'hôte. Aucun port, aucune adresse, aucun nom d'hôte dans le générateur.
Le lien manquait dans tous les fichiers : le devis switch ne contenait pas
une seule `ip route`. Un réseau underlay portant `passerelle_sortie` le
déclare — il vit dans l'underlay et non dans un tenant parce que la
frontière route vers TOUS les supernets tenants par le même saut, donc il
ne peut dériver d'aucun `index`. `devis-reseau` en tire deux routes :
l'aller (sortie générale) et le retour vers l'administration, dont l'absence
a coûté la passe de déploiement du 2026-07-29 — la réponse revient au
pare-feu par une autre interface que celle où l'état a été créé, et se fait
jeter en silence.
Les réseaux d'administration viennent de l'intrant `nftables_admin_ssh` :
même source unique que la garde anti-lockout des nftables et l'alias
SETOPS_ADMIN. Les trois pare-feux et les routes ne peuvent plus diverger.
La frontière est réglable depuis la console (section « Frontière » du
panneau Intrants) ; les identifiants d'API restent interdits d'écriture par
le GUI et vivent dans la voûte.
Correctifs de la même passe :
- le panneau refusait d'enregistrer les intrants de la frontière : le
garde-fou confondait une référence de voûte `{{ vault_* }}` préservée
avec un secret soumis. Il regarde désormais la valeur, pas le nom.
- `supprimer_vm_debian.yml` ne chargeait que `proxmox.vault.yml` pour ses
secrets ; retirer ce reliquat aurait cassé `make detruire`. Aligné sur le
playbook de clonage, voûte unique en dernier.
- documentation : la voûte est unique, `proxmox.vault.yml` n'est qu'un
reliquat de compatibilité.
Preuves : 24 OK, 0 échec. Cas de rejet du validateur d'underlay exercés un
par un ; résolution du jeton Proxmox vérifiée en exécution réelle.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le switch Binardat differe de Cisco sur deux points que le devis ignorait :
- masque d'ACL normal (255.255.0.0) au lieu du wildcard inverse (0.0.255.255) ;
- pas de commande 'remark' (commentaires d'ACL).
devis_reseau.py gagne un dialecte (defaut cisco, option binardat) : masque_acl()
et remarque() adaptent la sortie. Reglable par --dialecte, SETOPS_DIALECTE, ou
make devis-reseau DIALECTE=binardat. Le GUI suit l'env. Public reste generique.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Le moteur reste independant des instances ET des modeles assembles (prives) :
il ne contient que le modele public socle ; les modeles assembles sont
DECOUVERTS au runtime via SETOPS_MODELES (depot prive de l'operateur), jamais
embarques dans le code public.
- scripts/instance_creer.py : copie un modele (exemples/modeles/* + SETOPS_MODELES)
vers un depot frere ../<nom>, y fixe l'index, et refuse un nom existant, un
modele inconnu, ou un index en collision avec une instance federee (verifie
AVANT toute copie). .git et hosts.genere.yml non copies.
- make instance-creer NOM=.. MODELE=.. [INDEX=N] ; make instance-modeles.
- GUI (vue Reseau) : formulaire « Creer une instance depuis un modele » sous la
flotte. POST /api/instance-creer ; /api/instances renvoie aussi modeles +
index_pris. Ne bascule pas l'active.
Corrige : gabarit de voute du labo complete (P18 a echoue en passant l'active
sur le labo, dont le vault.yml.example etait reste a 17 cles ; aligne sur 23).
Valide : garde-fous de creation testes en isolement (collision refusee avant
copie, ecrasement refuse, modele inconnu refuse, aucune pollution) ; node --check ;
make verifier rc=0 CONFORME 21/21 (active = labo).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Demande explicite et repetee de l'operateur : gerer les instances DEPUIS le GUI.
- Inventaire resolu DYNAMIQUEMENT : le serveur figeait l'inventaire au demarrage
(args.inventaire.resolve()). Il est desormais relu depuis le symlink instance/
a chaque requete (propriete Gestionnaire.inventaire) -> la bascule prend effet
sans redemarrer. Les FICHIER_* de plan suivaient deja le lien. SETOPS_INVENTAIRE
force encore un inventaire fixe (CI).
- POST /api/instance-utiliser + basculer_instance(nom) : repointe le symlink avec
les garde-fous de make instance-utiliser + validation STRICTE (nom doit etre une
instance decouverte -> pas de traversee de chemin ; ../etc refuse, teste).
- Vue Reseau : bouton « Activer » par instance dans la table de flotte. Confirme
(avertissement renforce si production), bascule, recharge -> toutes les vues et
les deploiements visent la nouvelle instance.
L'edition de federe reste au CLI (rarement change) ; la bascule est CLI ET GUI.
docs/carte-set-ops.md : la ligne Multi-instance du tableau des mecanismes documente
la decouverte (glob des dossiers freres, aucun registre) et le garde-fou P21.
Valide : node --check ; bascule testee de bout en bout (repoint -> inventaire
dynamique suit -> traversee refusee -> restauration) ; make verifier rc=0
CONFORME 21/21.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
GUI (vue Réseau) :
- nouvelle vue FLOTTE (endpoint /api/instances) : liste les instances, marque
l'active (star), montre index / plage VLAN / statut federe-local / prod, et
affiche une banniere de COLLISION d'index — le pendant visuel de make instances
et de la preuve P21.
- le devis n'affiche plus que les instances FEDEREES (coherent avec le filtre
federe ; le labo local en est ecarte).
- retire les deux mentions perimees de « ip-miroir » (concept supprime a la
rupture ; la decouverte se fait desormais sur `index`).
La bascule d'instance et l'edition de `federe` restent au CLI (chirurgie de
symlink / drapeau rarement change) : plan de controle gele.
docs/multi-instances.md :
- section « Gerer la flotte » (make instances, bascule, les deux reflexes).
- section « Comment le moteur decouvre les instances » : convention de dossiers
freres (glob ../*/plan/nomenclature.yml), pas de registre.
- « Adressage federe » reecrit : il mentait encore (VMID compact 21101/1CSNN,
sandbox « sans index », plafond 9). Remplace par le modele derive du seed
(tableau des formules, VMID ip-miroir, drapeau federe: false, plafond reel 245).
- corrige les mentions residuelles de `supernet` a saisir (c'est `index`).
Valide : node --check du GUI, make verifier rc=0 CONFORME 21/21.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Principe : les valeurs de configuration se derivent des intrants, elles ne se
reecrivent pas a la main. La nomenclature dupliquait ce qu'index determine deja
(supernet, sous-reseaux, passerelles, VLAN). Corrige en rupture nette.
- inventory_rules : source unique de derivation — supernet_de, base3_de,
sous_reseau_de, passerelle_de, vlan_de. Modele 6 zones encode une fois
(2e octet = 10+index, 3e octet zone = 15+categorie, VLAN = 1000+index*10+zone).
deriver_nomenclature ne lit plus aucun adressage stocke ; mode compact supprime.
- devis_reseau : importe ces helpers (fin de la duplication) ; decouvre les
tenants sur `index` present (filtre vmid_schema retire).
- GUI : `index` devient un INTRANT (section Reseau). Il vit dans la nomenclature
(plan reseau uniforme, contrairement aux intrants des modeles heterogenes) et
le GUI l'ecrit chirurgicalement (une ligne, sans reformater). Le miroir JS
derive le VLAN du seed (fin de la lecture de c.vlan stocke).
- socle public : nomenclature au format maigre.
Preuve P20 (preuve_nomenclature_derivee) : aucune nomenclature ne stocke
d'adressage — garde-fou permanent, teste en negatif.
Valide : DIFF VIDE sur les 3 instances (la derivation reproduit exactement
l'adressage stocke), 7 modeles valident, devis_reseau genere les memes VLAN
(1011-1016 derives), make verifier rc=0 CONFORME 20/20, node --check du GUI OK.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Le harnais ne verifiait qu'UNE instance et le seul modele socle. Tout ce qui vit
a cote du moteur echappait au controle. Trois preuves ferment ces angles morts :
- P17 (scripts/modeles.py) : TOUS les modeles valident, pas seulement socle.
SETOPS_MODELES=../Set-OPS-Modeles inclut les modeles assembles prives. A trouve
6 modeles invalides sur 7 (corriges dans Set-OPS-Modeles).
- P18 (scripts/voute.py) : le gabarit vault.yml.example couvre EXACTEMENT les secrets
que le plan exige (bases + roles actifs + group_vars). Ne dechiffre jamais la vraie
voute : compare des noms.
- P19 (scripts/couverture_gui.py) : tout champ present dans un plan reel est editable
par le GUI. A trouve applications.websocket (comble). Nomenclature toleree (trou connu).
GUI :
- champ « Liens (bindings) » dans l'inspecteur d'application : role -> cible en listes
deroulantes, les roles proposes = ceux que le role porteur accepte (meta/liens.yml).
Comble un manque : les bindings ne se declaraient qu'en editant le YAML a la main.
- champ « WebSocket » (Collabora).
- CHAMPS_ECRITS_PAR_GUI : declaration de ce que le GUI sait ecrire, verifiee par P19.
Garde-fou de fond : valider_applications refuse une application posee sur un hote non
declare (l'hote fantome exact qu'integral portait). Cable partout + POST du GUI.
liens_acceptes()/catalogue_liens() dans inventory_rules : source unique partagee par le
validateur, le GUI et instancier.py (dont la copie locale est retiree).
Valide : make verifier rc=0, CONFORME 19/19, ansible-lint 0 echec, 7 modeles valident,
DIFF VIDE, node --check du GUI OK. Piece justificative : docs/audit/preuve-2026-07-22.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Le dépôt fait / explique / prouve ce qu'il affirme, vérifiable en une commande.
- Phase 1 : docs/audit/affirmations.md — 54 affirmations publiques tracées vers
une commande de preuve et un statut (✅/🟡/❌/⚪).
- Phase 2 : CLAUDE.md réduit à un pointeur mince ; contradiction SSH levée (le
code applique déjà PasswordAuthentication no + AuthenticationMethods publickey,
conforme à AGENTS.md) ; section AGENTS « Codex » → « agents IA ».
- Phase 3 : parcours démarrage réparé (QUICKSTART renvoyait à un modèle absent,
chemins de voûte faux, commandes make périmées) ; make verifier vert
(ansible-lint 33 → 0 : site.yml généré nommé, pipefail, name[template]) ;
voûte Proxmox unifiée lue par le clonage (all/vault.yml).
- Phase 4 : make prouver → docs/audit/preuve-<date>.md, harnais rejouable qui
rappelle l'outillage existant (aucune validation réimplémentée).
- Phase 5 : parcours QUICKSTART prouvé hors-ligne sur le socle ; modèle socle
rendu valide (autorite interne → auto-heberge) ; split-brain d'inventaire
corrigé (repli sur le répertoire existant, pas principal/).
make prouver : 15 OK, 0 échec, 1 sautée (voûte). ansible-lint : 0 failure.
Écarts découverts en cours de traitement (AFF-097..100) : tous résolus.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Nouvel onglet lecture seule affichant le devis make devis-reseau (VLANs+SVIs+ACLs)
via GET /api/devis-reseau, avec bouton Copier. node --check OK, endpoint eprouve
(2 tenants, 121 lignes). Un operateur genere et transmet la config sans CLI.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Quand une action échoue, un encadré résume les tâches en erreur (étape ·
hôte · type · message clair), avec un bouton Copier pour transmettre à un
humain — au lieu d'obliger à lire le dump Ansible.
Backend _extraire_echec : parse fatal:/UNREACHABLE!, privilégie stderr sur le
générique 'non-zero return code', gère no_log (secret masqué) et injoignables.
Marqueur __RESUME__ streamé, rendu côté JS. Éprouvé sur les 4 échecs réels du
from-zero du jour. node --check OK, smoke serveur OK.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Le registre des domaines publics — seul sans éditeur — a désormais son onglet :
ajouter/retirer une zone, autorité (sélecteur), edge, DNSSEC, mail, secondaires,
et affichage des expositions (FQDN) sous la zone. Backend : route /api/domaines,
ecrire_domaines, validation valider_domaines (rejette une autorité inconnue).
Éprouvé : round-trip backend (valide/écrit/rejette interne), node --check, smoke serveur.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Chaque action (creer-vm/verifier/deployer) écrit un journal horodaté
<instance>/logs/<hote>-<action>-<date>.log en plus du streaming live.
Si le navigateur se déconnecte, l'exécution continue et le log est capturé
jusqu'au bout (au lieu d'être interrompue). node --check OK, serveur HTTP 200.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SECRETS_ATTENDUS listait 16 secrets ; en manquaient 8 (OIDC des 3 clients,
cookie oauth2, restic, clé SSH sauvegarde, + nextcloud admin/oidc/bd). Le
panneau Intrants du GUI montre désormais la vraie liste (noms seulement, jamais
de valeur — la garde d'écriture des secrets reste inchangée).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Deux onglets qui rendent visibles dans l'interface deux registres jusque-là
en CLI seulement :
- Flux : matrice d'audit réseau (rôle/sens/port/pair/chiffrement coloré/raison,
depuis les meta/flux.yml), + légende chiffrement dans le panneau latéral.
- Couches : ordre de reconstruction (socle -> pki -> services -> apps -> agents),
groupes triés topologiquement par couche.
Alimentées par resoudre_flux/orchestrer via l'API inventaire. Éprouvé :
node --check OK, smoke test serveur (63 flux, 6 couches servis, boutons présents).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Le panneau « Intrants de base » expose désormais nftables_admin_ssh (type liste,
section Sécurité) — la garde anti-lockout du pare-feu, éditable en fichier mais
absente du GUI. Retrait de vault_step_ca_fingerprint des secrets attendus
(empreinte du root CA dérivée dynamiquement maintenant). node --check OK.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fin des dernières poches de 'codé en dur' liées à l'instance d'origine :
- realm SSO centralisé sur l'intrant identite_realm (defaut chezlepro,
retro-compatible) ; les 4 rôles (keycloak/forgejo/grafana/oauth2_proxy)
en dérivent. Expose dans la GUI (panneau Intrants).
- vars brandees renommees generiques : chezlepro_timezone -> fuseau_horaire,
chezlepro_organisation -> organisation (chrony, openldap, GUI, docs).
Aucune reference fonctionnelle aux anciens noms. Le moteur ne porte plus le
nom d'un tenant. Technolibre a son propre realm (technolibre).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Audit des boutons : « Pousser » était surchargé — servait à cloner la VM
(🖥 Pousser = creer-vm) ET à déployer (= make deployer, identique à
« Déployer »). Corrigé :
- 🖥 Pousser -> « 🖥 Créer la VM » (il clone) ;
- tous les Pousser/Déployer -> « Déployer » (une action = un mot) ;
- dry-run obligatoire AUSSI sur le déploiement des pieds (évite un
déploiement à l'aveugle ; cohérent avec un seul « Déployer ») ;
- titres/messages/console : Poussée -> Déploiement.
Chaque bouton fait désormais ce qu'il dit. GUI déjà entièrement francisée.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- TLS LDAPS/STARTTLS : pont du cert step_ca (client_pki, root:root 600)
vers /etc/ldap/tls lisible par openldap ; script + unité path systemd
qui re-synchronise et recharge slapd au renouvellement ; olcTLS* dans
cn=config ; SLAPD_SERVICES expose ldaps://. Dégrade proprement sans cert.
- Organisation : intrant chezlepro_organisation (remplace « Exemple Inc »).
Validé statiquement (ansible-lint, syntax) ; déploiement réel à suivre.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sans lui, le dry-run ne peut pas déchiffrer les secrets → les rôles à
secrets (step_ca, etc.) échouaient au Vérifier même avec des secrets
présents, et une voûte chiffrée aurait bloqué TOUT Vérifier. La modale
autorise un mot de passe vide (hôtes sans secrets = comportement actuel).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bug racine : /api/instancier appelait « instancier appliquer » SANS
--force → refus silencieux dès que le plan divergeait de l'inventaire.
Après une édition (disque, état, auto-actif), l'inventaire n'était jamais
régénéré → compteurs figés (« 0 actifs » alors qu'un hôte était actif au
plan). Le clic « Appliquer » = intention explicite → on force.
Aussi :
- après matérialisation (creer/deployer/pousser), le frontend régénère
l'inventaire (appliquerPlan) au lieu d'un simple rechargement ;
- compteur « vivantes » dans l'en-tête (depuis la sonde).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Le badge planifié/actif = intention du plan, pas existence de la VM.
- Sonde de vie : /api/sondes teste la joignabilité SSH de chaque hôte
(parallèle, non bloquant) → point ● vivante/injoignable sur les tuiles
et dans le détail (« Plan : … · Réel : … »).
- Auto-actif : un hôte cloné (creer) ou déployé passe actif dans le plan
automatiquement (matérialisé = actif).
Répond au constat « serveur planifié alors qu'il est vivant ».
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Chaque objet du détail se matérialise sur son hôte, en streaming console
(vault + confirmation prod) :
- Serveur → 🖥 Pousser clone la VM (make creer-vm), même sur un planifié —
comble le trou : le GUI ne clonait pas.
- Application → Pousser déploie l'hôte porteur.
- Base → Pousser déploie l'hôte du serveur de BD (crée la base).
Nouveaux modes creer/pousser dans executer_flux + routes /api/creer et
/api/pousser. Flux complet : éditer → Appliquer → Pousser → Vérifier →
Déployer, sans quitter le navigateur.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Depuis la fusion maître-détail, elle ne répétait que le socle (universel),
les rôles des applications (déjà listées) et les intégrations (déjà
cochées). Le prérequis bloquant — son seul signal unique — est désormais
nommé dans le pied. Code mort retiré (renduGroupe, detailGroupe,
titreGroupe, CSS .groupe*).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Le VMID n'est plus codé 9CSNN en dur : il prend le préfixe d'un `index`
déclaré en tête de plan/nomenclature.yml. Convention : supernet =
10.(index*10).0.0/16, VMID = index·CSNN. Permet à N écosystèmes de
coexister sans collision (Chezlepro=1, Technolibre=2). Sans index →
9CSNN (rétro-compatible, bacs à sable en 172.19.x). deriveServeur JS
mort retiré. Doc multi-instances.md mise à jour.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
make instance-utiliser NOM=… repointe le symlink 'instance' (prod ↔ bac à
sable) ; make instance-courante affiche le montage. Le panneau Intrants lit
l'identité dans group_vars/all/10-intrants.yml de l'inventaire monté (réel
ou symlink partagé), donc compatible avec la séparation par instance comme
avec l'ancien partage lab/production.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Le déploiement réel est permis sur TOUTE instance (un bac à sable déploie
sur son infra lab : isolé ET fonctionnel). Le drapeau ne bloque plus ;
il marque la PROD pour exiger une confirmation renforcée (badge/bouton
rouge, re-saisie du nom d'hôte). executer_flux ne refuse plus selon le
drapeau. Badge « PROD » / « bac à sable » dans l'en-tête.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>