Set-OPS-Public/wiki/Le-GUI-console-d-exploitation.md

216 lines
12 KiB
Markdown
Raw Normal View History

# Le GUI (console d'exploitation)
> **Unité d'apprentissage.** Moule : ① concept → ② Set-OPS → ③ transférable → ④ à toi de jouer.
---
## ① Le concept *(générique)*
Un moteur d'infrastructure a besoin d'une **interface humaine** — sinon il n'est utilisable que
par son auteur (ou une IA). Le principe fondateur : **un opérateur doit tout piloter seul**, sans
l'auteur et sans IA (« exploitable sans IA »).
Une bonne console d'exploitation obéit à trois règles :
- **Elle édite la SOURCE, pas l'artefact.** On modifie le *plan* (l'état voulu), jamais l'inventaire
généré. La console montre la vérité, elle ne la contourne pas.
- **Elle prévisualise avant d'agir.** *dry-run* (voir ce qui changerait) avant *apply* (changer).
- **Elle empêche l'invalide.** Le meilleur garde-fou n'est pas un message d'erreur : c'est une
interface où l'erreur est **impossible à saisir**.
---
## ② Comment Set-OPS le fait
`make inventaire-ui` lance une console web **locale** (`127.0.0.1`, protégée par un jeton). Ses vues :
| Vues **éditables** (le plan) | Vues **dérivées** (lecture seule) |
|---|---|
GUI : la vue Nomenclature, et deux fautes que mes bancs ne voyaient pas LA VUE. La nomenclature etait le seul registre que le GUI ne savait pas ecrire du tout : ajouter une fonction exigeait d ouvrir le YAML. Elle a sa vue, et son formulaire est GENERE depuis le schema. Deuxieme registre sur six. couverture_gui verifier passe : les 28 champs des plans reels sont editables. Elle n est pas un registre comme les autres : elle decrit la REGLE dont VMID, VLAN, adresse et passerelle se derivent. Chaque fonction montre ce qu elle derive et les VM qui la portent ; l index est montre mais pas editable, parce qu il est alloue par le site ; valider_nomenclature refuse de retirer une fonction encore portee, ou de designer une zone non declaree. DEUX FAUTES, ET POURQUOI MES BANCS NE LES VOYAIENT PAS. Le formulaire des bases, livre la veille, etait casse dans un navigateur. Il lisait data.schema, or il n existe aucun data global : c est une const locale de charger(). ReferenceError a l ouverture, et zone morte dans sauvegarderBases. Je l avais eprouve sous node EN LUI PASSANT data : le banc reproduisait la fonction, pas sa portee. D ou test_rendu_gui.py, qui charge le JS entier dans un DOM simule et dessine les douze vues, avec son controle negatif. Le schema decrivait reservations comme une table de zones ; le fichier reel est un bloc plat. P61 comparait des NOMS aplatis, donc ne voyait rien. Elle compare desormais aussi la FORME. ECRIRE SANS DEPLACER UN COMMENTAIRE. _fusion_chirurgicale remplace le bloc entier des qu une valeur change : quinze entrees compactes devenaient 42 lignes, et le commentaire du poste d exploitation se retrouvait en tete du bloc, ou il affirmait que collab etait le poste d exploitation. Un commentaire deplace n est pas laid, il est faux. _fusion_table edite les tables ligne a ligne ; le diff fait trois lignes. Au passage : sort_keys triait le schema, donc l ordre des cases a l ecran (reserve_max avant reserve_min) ; et _ecrire_index_nomenclature ecrivait encore par write_text, oubliee au passage des ecritures atomiques. LIMITE : deux registres sur six sont generes, et je n ai toujours pas ouvert cette page dans un navigateur. make prouver : CONFORME, 60 OK, 0 echec, 1 saute. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-08 17:08:45 -04:00
| Serveurs · **Intégrations** · Applications · Bases · Domaines · **Nomenclature** · **Intrants** | Flux · Couches · **Réseau** (flotte + devis) |
Le flux d'exploitation, de bout en bout :
1. **Éditer** une vue du plan → **Sauvegarder** ;
2. **« Appliquer le plan »** → régénère l'inventaire (VMID/IP/VLAN **dérivés**) ;
3. **Vérifier** (dry-run) sur un hôte → prévisualise sans rien changer ;
4. **Déployer** → applique les rôles (confirmation **renforcée** si l'instance est en **PROD**) ;
5. **Créer la VM** → clone sur Proxmox depuis le plan.
**L'erreur rendue impossible.** Le champ « Hôte » d'une application est un `<select>` qui ne
propose que des **hôtes réels** : impossible de pointer vers un hôte fantôme. La console couvre le
schéma du plan — et la preuve **P19** le vérifie.
GUI : les six registres ont un formulaire genere, et la sauvegarde aussi CHAMPS_ECRITS_A_LA_MAIN est vide. Serveurs et applications, les deux plus gros, sont passes au generateur — chargement, rendu et sauvegarde. L EPREUVE QUI COMPTE. Ouvrir chaque vue et enregistrer sans rien toucher doit renvoyer exactement le plan qu on vient de lire : 14 serveurs, 25 applications, 2 domaines, 4 bases, IDENTIQUE partout. C est ce qui separe un formulaire genere d un formulaire qui en a l air — un champ visible a l ecran et perdu en silence a l enregistrement serait le pire des deux mondes. test_rendu_gui.py le mesure a chaque make prouver. TROIS DEFAUTS TROUVES EN CHEMIN. Le formulaire annoncait des defauts INVENTES : 2048 Mo, 2 coeurs, 16G. Il n existe aucun defaut fixe — deriver_ressources calcule depuis les roles portes (1024 et 1 pour infra-pki-01, 5632 et 4 pour collab-01). Un repere faux fait croire qu on connait la valeur. Le schema nomme le champ derive, et l ecran montre la valeur reelle de cet hote. L option vide d un select dit desormais ce qu elle produira : « (defaut : asgard) ». Une SECONDE occurrence du defaut d hier dormait dans sourceDeValeurs : elle lisait encore data.nomenclature. Elle n avait jamais leve parce que la vue Serveurs, seule a emprunter cette source, avait un formulaire ecrit a la main. Elle a leve a la seconde ou le generateur l a prise. Le banc ne voit que les chemins vivants : verifier_gui.py fait donc aussi une verification STATIQUE, qui voit ce qui dort. La validation client s accrochait a data-v, pose a la main sur trois champs. Le formulaire genere l aurait perdu et la validation serait passee au vert sur ZERO champ. Le generateur marque chaque controle, et la sauvegarde refuse si elle n en inspecte aucun. DEUX CHAMPS GARDENT LEUR EDITEUR, et le schema le dit (x-editeur) : la matrice des integrations montre les universelles et les exemptions, et l editeur de liens contraint le role a meta/liens.yml. Le generateur s efface plutot que de remplacer un editeur qui en sait plus que lui. LIMITE : je n ai toujours pas ouvert ces pages dans un navigateur. make prouver : CONFORME, 61 OK, 0 echec, 1 saute. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-08 18:26:33 -04:00
**Les formulaires sont GÉNÉRÉS.** Depuis le 2026-09-08, **les six registres** — serveurs,
applications, bases de données, serveurs de BD, domaines et nomenclature — ne portent plus de
formulaire écrit à la main : ils le construisent depuis `docs/audit/schema-plan.json`, lui-même
dérivé des constantes du moteur (`make schema`). Un champ ajouté au plan apparaît donc à l'écran
**sans qu'on touche à l'interface** — et il arrive au fichier, parce que la sauvegarde dérive du
même schéma. Ouvrir une vue et enregistrer sans rien toucher renvoie **exactement** le plan qu'on
vient de lire ; c'est mesuré à chaque `make prouver`.
Deux champs gardent volontairement leur propre éditeur, et le schéma le déclare : la **matrice des
intégrations** (elle montre aussi les universelles, non décochables, et les exemptions) et
l'**éditeur de liens** (il contraint le rôle à ce que `meta/liens.yml` accepte). Le générateur
s'efface plutôt que de remplacer un éditeur qui en sait plus que lui.
Deux preuves tiennent le schéma : **P61** refuse qu'un champ des plans réels
plan : sauvegarder n emportait plus quarante lignes de commentaire En voulant generer deux formulaires de plus, j ai trouve pire que ce que je cherchais. CE QUI ETAIT DEJA LA. Les quatre ecrivains de registre ecrasaient le fichier au safe_dump. Mesure sur les fichiers reels : domaines.yml 6->3, applications.yml 27->5, serveurs.yml 18->3. Quarante lignes, detruites par n importe quel clic sur Sauvegarder dans les vues Serveurs, Applications ou Domaines. Parmi elles, celle qui explique pourquoi backup-01 a ete retire, et celle qui dit dans quel ordre les deux roles du runner s appliquent. C etait l incident du 2026-08-18, jamais corrige pour les registres du plan. Les quatre passent par _ecrire_registre : aller-retour a vide identique a l octet, sur les quatre fichiers. TROIS ECARTS DE SCHEMA, trouves en confrontant le schema aux VALIDATEURS et non aux seuls plans : - edge designe un GROUPE, pas un hote. Le schema disait serveurs : un formulaire genere aurait offert une valeur qu aucun hote ne reconnait, donc aucun SAN, donc la panne du 2026-08-25 reintroduite ; - exposition, entierement valide par le moteur, manquait au schema ; - liens etait items: {type: object} — une liste d objets sans forme. Et mail, offert par la vue Domaines depuis sa creation, decrit ici comme un booleen, saisi la-bas comme du texte, lu par rien : retire. P62 garde tout ca. Elle separe l entite du reste mecaniquement : un validateur lit son entite par des variables LOCALES, les autres registres par ses PARAMETRES. Controle negatif rejoue. LES FORMULAIRES. Serveurs de BD et Domaines sont generes, chargement et sauvegarde compris. Quatre registres sur six. Le generateur a appris la liste d objets. LIMITE : restent serveurs et applications, les deux plus gros ; et je n ai toujours pas ouvert ces pages dans un navigateur. make prouver : CONFORME, 61 OK, 0 echec, 1 saute (62 preuves). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-08 17:54:49 -04:00
manque au schéma, ou que le schéma décrive une *forme* que les plans n'ont pas ; **P62** refuse
qu'un champ accepté par les *validateurs du moteur* manque au schéma — sans quoi le formulaire ne
saurait pas offrir une fonctionnalité que Set-OPS possède déjà.
**Sauvegarder n'efface plus les commentaires du plan.** Les registres portent la mémoire écrite des
décisions — pourquoi `backup-01` a été retiré, dans quel ordre les rôles du runner s'appliquent.
Jusqu'au 2026-09-08, chaque « Sauvegarder » en détruisait **quarante lignes**. Les quatre écrivains
passent désormais par une fusion ligne à ligne : un aller-retour sans modification laisse le
fichier **identique à l'octet**.
GUI : la vue Nomenclature, et deux fautes que mes bancs ne voyaient pas LA VUE. La nomenclature etait le seul registre que le GUI ne savait pas ecrire du tout : ajouter une fonction exigeait d ouvrir le YAML. Elle a sa vue, et son formulaire est GENERE depuis le schema. Deuxieme registre sur six. couverture_gui verifier passe : les 28 champs des plans reels sont editables. Elle n est pas un registre comme les autres : elle decrit la REGLE dont VMID, VLAN, adresse et passerelle se derivent. Chaque fonction montre ce qu elle derive et les VM qui la portent ; l index est montre mais pas editable, parce qu il est alloue par le site ; valider_nomenclature refuse de retirer une fonction encore portee, ou de designer une zone non declaree. DEUX FAUTES, ET POURQUOI MES BANCS NE LES VOYAIENT PAS. Le formulaire des bases, livre la veille, etait casse dans un navigateur. Il lisait data.schema, or il n existe aucun data global : c est une const locale de charger(). ReferenceError a l ouverture, et zone morte dans sauvegarderBases. Je l avais eprouve sous node EN LUI PASSANT data : le banc reproduisait la fonction, pas sa portee. D ou test_rendu_gui.py, qui charge le JS entier dans un DOM simule et dessine les douze vues, avec son controle negatif. Le schema decrivait reservations comme une table de zones ; le fichier reel est un bloc plat. P61 comparait des NOMS aplatis, donc ne voyait rien. Elle compare desormais aussi la FORME. ECRIRE SANS DEPLACER UN COMMENTAIRE. _fusion_chirurgicale remplace le bloc entier des qu une valeur change : quinze entrees compactes devenaient 42 lignes, et le commentaire du poste d exploitation se retrouvait en tete du bloc, ou il affirmait que collab etait le poste d exploitation. Un commentaire deplace n est pas laid, il est faux. _fusion_table edite les tables ligne a ligne ; le diff fait trois lignes. Au passage : sort_keys triait le schema, donc l ordre des cases a l ecran (reserve_max avant reserve_min) ; et _ecrire_index_nomenclature ecrivait encore par write_text, oubliee au passage des ecritures atomiques. LIMITE : deux registres sur six sont generes, et je n ai toujours pas ouvert cette page dans un navigateur. make prouver : CONFORME, 60 OK, 0 echec, 1 saute. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-08 17:08:45 -04:00
**La flotte, dans la vue Réseau.** Voir toutes les instances, **basculer** (« Activer »), **créer**
une instance depuis un modèle, repérer une **collision** d'index. Cf.
[Multi-instance & fédération](Multi-instance-et-fédération).
### Les vues, annotées
Chaque figure porte ses **repères** (pastilles + légende). Les vues **éditables** d'abord (le plan),
puis les vues **dérivées** (lecture seule) :
**Serveurs** *(éditable)* — inventaire actif et état de la flotte ; cartes serveur et tuiles
VMID/IP/VLAN **dérivées** du seed ; formulaire **Identité** = le seul endroit où l'on édite le plan.
![Vue Serveurs, annotée](img/Set-OPS-Serveurs-annote.svg)
GUI : la vue Nomenclature, et deux fautes que mes bancs ne voyaient pas LA VUE. La nomenclature etait le seul registre que le GUI ne savait pas ecrire du tout : ajouter une fonction exigeait d ouvrir le YAML. Elle a sa vue, et son formulaire est GENERE depuis le schema. Deuxieme registre sur six. couverture_gui verifier passe : les 28 champs des plans reels sont editables. Elle n est pas un registre comme les autres : elle decrit la REGLE dont VMID, VLAN, adresse et passerelle se derivent. Chaque fonction montre ce qu elle derive et les VM qui la portent ; l index est montre mais pas editable, parce qu il est alloue par le site ; valider_nomenclature refuse de retirer une fonction encore portee, ou de designer une zone non declaree. DEUX FAUTES, ET POURQUOI MES BANCS NE LES VOYAIENT PAS. Le formulaire des bases, livre la veille, etait casse dans un navigateur. Il lisait data.schema, or il n existe aucun data global : c est une const locale de charger(). ReferenceError a l ouverture, et zone morte dans sauvegarderBases. Je l avais eprouve sous node EN LUI PASSANT data : le banc reproduisait la fonction, pas sa portee. D ou test_rendu_gui.py, qui charge le JS entier dans un DOM simule et dessine les douze vues, avec son controle negatif. Le schema decrivait reservations comme une table de zones ; le fichier reel est un bloc plat. P61 comparait des NOMS aplatis, donc ne voyait rien. Elle compare desormais aussi la FORME. ECRIRE SANS DEPLACER UN COMMENTAIRE. _fusion_chirurgicale remplace le bloc entier des qu une valeur change : quinze entrees compactes devenaient 42 lignes, et le commentaire du poste d exploitation se retrouvait en tete du bloc, ou il affirmait que collab etait le poste d exploitation. Un commentaire deplace n est pas laid, il est faux. _fusion_table edite les tables ligne a ligne ; le diff fait trois lignes. Au passage : sort_keys triait le schema, donc l ordre des cases a l ecran (reserve_max avant reserve_min) ; et _ecrire_index_nomenclature ecrivait encore par write_text, oubliee au passage des ecritures atomiques. LIMITE : deux registres sur six sont generes, et je n ai toujours pas ouvert cette page dans un navigateur. make prouver : CONFORME, 60 OK, 0 echec, 1 saute. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-08 17:08:45 -04:00
**Nomenclature** *(éditable)* — le **modèle** dont tout l'adressage dérive : zones de sécurité,
fonctions (catégorie · service), réservations. Chaque fonction montre **ce qu'elle dérive** — zone,
VLAN, sous-réseau, bloc d'hôtes — et les VM qui la portent ; on ne retire pas une fonction encore
portée. L'`index` y est **affiché mais pas éditable** : il est *alloué par le site*, pas décidé par
le tenant (cf. l'en-tête de `plan/nomenclature.yml`), et s'édite depuis le panneau **Intrants**.
**Applications** *(éditable)* — catalogue des applis, éditeur (rôle · hôte · port · FQDN exposé),
relations déclaratives (requiert · liens · bases), dépendances causales.
![Vue Applications, annotée](img/Set-OPS-Applications-annote.svg)
**Bases — serveur** *(éditable)* — les moteurs SGBD et les bases applicatives « nom @ serveur »,
avec les bases hébergées sur chaque serveur.
![Vue Bases (serveur BD), annotée](img/Set-OPS-Bases-annote.svg)
**Bases — fiche** *(éditable)* — portée · consommateur · serveur, **secret Vault** (jamais en clair,
un pointeur seulement) et le **DSN dérivé** (secret masqué).
![Vue Bases (fiche de base), annotée](img/Set-OPS-Bases-2-annote.svg)
**Domaines** *(éditable)* — zones DNS publique vs interne, autorité · edge · DNSSEC, expositions.
![Vue Domaines, annotée](img/Set-OPS-Domaines-annote.svg)
intégrations : le rôle déclare sa politique ; le cluster passe à l'hébergeur 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>
2026-08-03 14:09:22 -04:00
**Intégrations** *(éditable)* — la **matrice serveurs × intégrations**. Les colonnes ✓ vertes
sont la **politique des rôles** : elles s'appliquent à tout hôte et ne se décochent pas ; un `—`
barré signale une **exemption dérivée du service rendu** (l'AC ne s'enrôle pas auprès d'elle-même).
Les autres colonnes sont de vrais choix, cochables ici. La ligne **couverture** ne juge pas — elle
rend le motif visible : c'est au lecteur de savoir si les manquants sont des décisions ou des oublis.
> Cette vue existe parce que la fiche de détail ne montrait les intégrations que **d'un serveur à
> la fois**. Deux serveurs sans supervision ni journaux sont restés invisibles jusqu'à ce qu'un
> devis de pare-feu les énumère : l'information était à l'écran, répartie sur quatorze clics.
**Flux** *(dérivée)* — la **matrice d'audit** : la source de nftables *et* la justification lisible
de chaque flux (sens ingress/egress, chiffrement).
![Vue Flux, annotée](img/Set-OPS-Flux-annote.svg)
**Couches** *(dérivée)* — l'**ordre de déploiement** en 6 couches, du socle aux agents ; celui que
`make deployer-tout` déroule.
![Vue Couches, annotée](img/Set-OPS-Couches-annote.svg)
**Réseau** *(dérivée)* — la **flotte multi-instances** : adressage dérivé du seed, bascule
d'instance, devis de configuration des switches.
![Vue Réseau, annotée](img/Set-OPS-Reseau-annote.svg)
> Chaque figure est un **SVG auto-contenu** (la capture y est intégrée) : un seul fichier portable
> qui rend tel quel dans le wiki, un `.md` du dépôt, ou un navigateur.
---
assistants : cent trente-deux cibles, et aucune ne disait dans quel ordre 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>
2026-09-20 16:31:16 -04:00
### La vue **Assistants** — l'ordre des gestes, pas seulement les gestes
Les huit autres vues éditent le **plan**. Celle-ci conduit des **gestes**, et elle répond à un
trou précis : le `Makefile` porte **132 cibles documentées** qui disent chacune *ce qu'elle
fait*, et **aucune ne dit dans quel ordre**. Un exploitant devant un site neuf n'avait aucun
moyen d'apprendre, depuis la console, que `site-creer` précède `forge-amorcer`, que le premier
passage **s'arrête** sur une forge vide, ni que rien n'est « prêt » avant `valider`. Cette
connaissance vivait en prose dans les documents ; la console offrait des boutons sans séquence.
**Dix-sept assistants, 126 étapes, et la totalité des 132 cibles.** Chaque cible documentée est
soit portée par un assistant, soit **exemptée avec son motif** — et `P83` refuse qu'une cible
échappe aux deux. Sans cette garde, la console cacherait un pouvoir que le moteur possède.
**Le registre ne recopie pas le Makefile.** `docs/runbooks-construction.yml` déclare seulement
ce que le Makefile ne peut pas porter : l'**ordre**, la **nature** du geste, la **portée** et le
**pourquoi**. Le libellé de chaque étape est *lu dans le Makefile au moment de servir*. Une
cible renommée se voit donc immédiatement, elle ne s'invente pas — c'est la règle « une liste
qui suit une autre prend du retard », et la garde est écrite **en même temps** que la liste.
**Trois natures, trois comportements** :
| Nature | Ce que la console fait |
|---|---|
| `mesure` | n'écrit rien ; **toujours offerte**, même après un échec — mesurer pour comprendre est exactement ce qu'on fait ensuite |
| `écriture` | exige que l'étape précédente **non facultative** ait réussi ; sinon on bâtit sur un terrain non vérifié |
| `destructif` | exige d'**écrire `DETRUIRE`**, en plus du `CONFIRMER=true` que la cible réclame déjà |
**Le navigateur ne nomme pas une commande, il nomme une place.** `/api/runbook-etape` ne lance
jamais « la cible que la page demande » : il lance ce que le registre déclare **à cet index-là**,
avec les seules variables déclarées. Une page compromise — ou simplement périmée — ne peut donc
pas réclamer `raser` depuis un assistant de mesure. L'index compte : « Le premier jour d'un
site » lance `site-deployer-tout` **deux fois**, et les deux places n'ont pas le même sens.
**La portée décide, et elle se dérive.** Un assistant de site exige `materialiser`, un assistant
de locataire `configurer`, un assistant de poste **les deux**. Un assistant hors de portée reste
**visible et lisible** : savoir que le geste existe, et chez qui il se fait, fait partie du
métier. Ce qui est refusé, c'est de le lancer — et le refus dit sa raison.
---
## ③ Pourquoi c'est transférable
| Set-OPS | Équivalents ailleurs |
|---|---|
| GUI au-dessus d'Ansible | **AWX** / Ansible Automation Platform, **Semaphore** |
| éditer la source de vérité | l'UI de **NetBox** (IPAM/DCIM), une CMDB |
| dry-run → apply | `terraform plan/apply`, tout panneau IaC |
| l'invalide impossible à saisir | *validation à la source*, formulaires contraints |
Tu as appris **la console d'exploitation, éditer-la-source, dry-run-avant-apply, la validation à la
saisie** — pas « le GUI de Set-OPS ».
---
## ④ À toi de jouer
1. **Lance-la.** `make inventaire-ui`, ouvre l'URL affichée. Repère l'inventaire actif et l'état de
la flotte (en haut), les onglets, « Appliquer le plan », « Sauvegarder ».
2. **Édite → prévisualise.** Vue **Serveurs**, change la mémoire d'un hôte, **Sauvegarder**,
**Appliquer le plan**, puis **Vérifier** (dry-run) : tu vois ce qui *changerait* avant d'agir.
3. **Répare un hôte fantôme (l'erreur impossible).** Si une appli pointe un hôte inexistant, ouvre
la vue **Applications**, sélectionne-la : le `<select>` « Hôte » ne montre **que des hôtes
réels**. Choisis le bon, **Sauvegarder**, **Appliquer**. Tu ne *peux pas* re-saisir le fantôme.
4. **Bascule d'instance.** Vue **Réseau**, bouton **« Activer »** sur une autre instance. Toutes
les vues suivent, sans redémarrer.
5. **Sens le garde-fou.** Essaie de sauvegarder un plan incohérent (ex. une base dont le
consommateur n'existe pas) : la console **refuse** avec une raison. L'invalide ne passe pas.
6. **Casse & répare.** Édite `hosts.yml` à la main, reviens dans la GUI, « Appliquer le plan » : ta
modification est **écrasée** par le plan. La source de vérité, c'est le plan — pas l'inventaire.
---
## Pour aller plus loin *(dépôt)*
- Lancer : `make inventaire-ui` ; le code : `scripts/inventory_gui.py`.
- Ce que le GUI sait écrire (et la preuve) : `scripts/couverture_gui.py` (P19).
assistants : cent trente-deux cibles, et aucune ne disait dans quel ordre 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>
2026-09-20 16:31:16 -04:00
- Les assistants : `docs/runbooks-construction.yml`, lus par `scripts/runbooks.py` (P83).
Vérifier le registre sans lancer la console : `python3 scripts/runbooks.py verifier`,
et le lire en texte : `python3 scripts/runbooks.py lister`.
- Le flux plan → apply : unité **[Infra as Code & idempotence](Infra-as-Code-et-idempotence)**.
- La flotte : unité **[Multi-instance & fédération](Multi-instance-et-fédération)**.