Quatrieme reconstruction depuis zero, 32 min 14 s, 14/14. Elle avait un but precis : trois roles dependent desormais d une variable que le generateur pose. A froid, si instancier ne la posait pas, le defaut du role reprendrait la main en silence — juste pour forge et cloud, faux pour observatoire. Le certificat ne a froid porte observatoire et vigie, aucun ancien nom, et les deux retours SSO derivent juste sans reprise. Un seul echec, sans rapport : sur une machine des quatorze, get_url a rendu 0 octet SANS ERREUR — le cache a servi un 200 au corps vide. Le defaut s est lu deux cents lignes plus loin, dans un apt qui accusait la signature. La garde posee hier ne mordait pas : retirer une ressource vide ne vaut qu au passage suivant, quand le fichier existe deja. Les cinq roles qui telechargent une cle la mesurent maintenant dans la meme execution. P68 garde le motif. infra-mail-01 redeployee, make valider a 0 echec sur 14 machines. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
13 KiB
Carte d'orientation Set-OPS
Pour qui : le mainteneur — celui qui va modifier le moteur. À lire avant d'ajouter quoi que ce soit.
Tu viens plutôt exploiter un écosystème déjà déployé ? Wiki → Reprendre l'écosystème. Tu apprends le métier ? Wiki → Accueil.
Point d'entrée vers le corpus documentaire, et catalogue des mécanismes transverses — ceux qui vivent dans le code et qu'on re-découvre sinon. Créée le 2026-07-03 après un audit du dépôt, revue le 2026-07-29. But : ne plus re-déterrer ce qui existe.
Le dépôt est déjà bien documenté. Le manque n'était pas la doc du modèle, mais (a) un index « par où commencer » et (b) une carte des mécanismes (dispersés dans le code + les README de rôles). Cette page comble ces deux trous.
Le dépôt en chiffres
Ces valeurs sont mesurées, pas recopiées : P48 les recompte et refuse tout écart. Elles étaient toutes fausses le 2026-08-26 — de 6 rôles, de 12 pièces d'audit, de 8 décisions. Aucune ne faisait travailler personne ; mais une carte dont les faits vérifiables sont faux cesse d'être consultée, et c'est alors ses pointeurs qu'on perd.
| Ce qu'on compte | Combien | Comment on le mesure |
|---|---|---|
| rôles | 67 | roles/*/ |
| README de rôles | 67 | roles/*/README.md — l'écart avec la ligne au-dessus est la dette |
| documents | 40 | docs/*.md |
| pièces d'audit | 42 | docs/audit/* |
| unités de wiki | 27 | wiki/*.md |
| décisions en vigueur | 85 | lignes | **D-nn** | de decisions-architecture.md |
| décisions renversées | 3 | lignes | **D-nn** — du même document |
1. À lire d'abord (dans l'ordre)
| Sujet | Documents |
|---|---|
| Autorité / gouvernance | AGENTS.md (source d'autorité), CLAUDE.md, docs/MISE-A-JOUR-CODEX-CLAUDE.md |
| Le modèle (plan) | docs/architecture-set-ops.md (survol) → docs/plan-et-generation.md (à fond) → docs/meta-classe.md (concept) |
| Services, maturité, dette | docs/catalogue-services.md (la carte de maturité + la cruft y sont déjà) |
| Exploitation / VM | docs/vm-lifecycle.md, docs/procedure-template-debian13-proxmox.md, docs/config-proxmox.md, docs/nomenclature-vm.md, docs/multi-instances.md |
| Conceptions de domaine | docs/identite-sso.md, docs/courriel-conception.md, docs/bindings-conception.md, docs/dns-interne.md, docs/dimensionnement-ressources.md, docs/integrations-vm.md |
| Réseau / pare-feu | docs/flux-conception.md (le modèle) → docs/registre-flux.md (généré, matrice d'audit) → docs/frontiere-opnsense.md (la bordure nord/sud) ; underlay : underlay.yml.example + make underlay |
| Ordre de déploiement | docs/couches-deploiement.yml (couches) + docs/dependances-groupes.yml (graphe) → playbooks/site.yml (généré, make site) |
| Conformité du déployé | docs/devis-services.md — les cinq devis de service (make identite-plan, certificats-plan, expositions-plan, postgresql-plan, courriel-plan) et les cinq devis d'infrastructure (frontiere-plan, proxmox-fw-plan, sdn-plan, underlay-plan, placement-plan). Répondent à ce que make prouver ne demande jamais : ce qui tourne correspond-il à ce qui est déclaré ? |
| Preuve / recette | docs/audit/affirmations.md (registre), make prouver → docs/audit/preuve-<date>.md — statique : lit le dépôt, aucun appel réseau ; la conformité du déployé est l'affaire des devis de service (ligne au-dessus), docs/audit/plan-de-recette.md (généré du wiki), docs/audit/protocole-operateur-independant.md |
| Décisions d'architecture | docs/decisions-architecture.md — les décisions en vigueur (comptées ci-dessus), pourquoi, où lire le détail, et ce qui les garde ; plus les décisions renversées et leur cause |
| SDN / routage | docs/sdn-evpn.md — décision du 2026-08-02 : le routage inter-zone passe des commutateurs aux hyperviseurs (zones EVPN = VRF). En service depuis le 2026-08-03 (routage_tenants: sdn dans l'underlay.yml du site) : les trunks ne portent plus que deux VLAN d'underlay au lieu de quinze, les passerelles .1 sont anycast sur chaque hyperviseur. Écart mesuré par make sdn-plan |
| Migration de tenant | docs/migration-tenant.md — recette en neuf étapes (0 à 8), machine à états, gardes ; le receveur se construit avant tout gel |
| Exploitation courante | docs/runbooks-exploitation.md, docs/intrants-communs.md, docs/intrants-base-gui-conception.md, docs/theme-forgejo-hors-flotte.md |
| Pédagogie (le wiki) | wiki/ — les unités (comptées ci-dessus) publiées par make wiki-publier ; entrer par wiki/Home.md |
| Vision / positionnement | docs/ecosysteme-chezlepro.md, docs/positionnement.md, docs/pouvoirs-set-ops.md |
2. Les mécanismes transverses (et OÙ ils vivent)
Ce que je re-découvre sinon. Consulter avant de concevoir un nouveau mécanisme.
| Mécanisme | Ce que c'est | Où, dans le code | Doc |
|---|---|---|---|
| Plan → inventaire | plan/*.yml → hosts.yml généré |
scripts/instancier.py, scripts/inventory_rules.py |
plan-et-generation.md |
| Schéma des registres | la forme des six registres — champs, types, énumérations, requis — dérivée des validateurs, jamais écrite à la main. Sert à générer les formulaires plutôt qu'à les écrire. La cohérence reste aux valider_* : un schéma ne sait pas dire qu'un consommateur désigne une application inexistante |
scripts/schema_plan.py (make schema) → docs/audit/schema-plan.json ; garde P61 |
plan-et-generation.md |
| Nomenclature dérivée | VMID / IP / VLAN / FQDN dérivés | inventory_rules.deriver_nomenclature + plan/nomenclature.yml |
nomenclature-vm.md |
| Dimensionnement | RAM/CPU/disque sommés par logiciel | roles/*/meta/empreinte.yml → deriver_ressources |
dimensionnement-ressources.md |
| Bindings app→app | lien côté app (liens) résolu en host_vars |
plan/applications.yml liens: + roles/*/meta/liens.yml + instancier.resoudre_liens |
bindings-conception.md |
| Bindings app→base | lien côté base (consommateur/portee) résolu dans le rôle |
plan/bases-donnees.yml + rôle utilitaire resoudre_base (lookup('vars', secret), no_log) inclus par le consommateur |
bindings-conception.md §5 |
| Résolution d'annuaire | connexion LDAP (uri/base DN/bind) dérivée, jamais recopiée | rôle utilitaire resoudre_annuaire (inclus par dovecot/postfix/keycloak/icingaweb2 et amorcage_acces) |
identite-sso.md |
| Plancher de résolution | /etc/hosts généré depuis l'inventaire + alias d'expose → l'écosystème se résout DNS éteint |
rôle hosts_statiques (appliqué dans la couche socle) |
dns-interne.md |
| Pont de certificat | cert step_ca → service, resync au renouvellement | script *-cert-sync + unité .path, dans chaque rôle serveur ; cert déposé par client_pki |
— |
| Ordonnancement socle-first | socle/durci avant les client_* |
serveur_debian/serveur_durci d'abord (posent /etc/hosts via hosts_statiques) |
— |
| Sûreté check-mode | dry-run fiable | when: not ansible_check_mode sur les tâches de service + handlers |
— |
| Voûte au déploiement | secret jamais en clair ; une voûte, une clé depuis le 2026-08-28 | ANSIBLE_VAULT_IDENTITY_LIST construit par scripts/voutes.py (clé nommée ~/.config/setops-vault-<dépôt>) ; déréférencé par lookup('vars', <nom>) |
autorisation.md §6.1 |
| Multi-instance | un dépôt par écosystème ; l'active = symlink instance/, les autres découvertes par convention (dossiers frères, aucun registre) |
active : symlink instance/ ; découverte : scripts/instances.py / devis_reseau.py (glob ../*/plan/nomenclature.yml avec index) ; garde-fou collision : preuve P21 |
multi-instances.md |
| Exposition → edge | app expose un FQDN public servi par un edge | plan/domaines.yml + expose (applications) |
bindings-conception.md §4 |
| Exploitation de l'hébergeur | ses opérations n'appartiennent à aucun tenant et restent hors overlay | à moitié construit : les VM du site ont leur inventaire (scripts/site_inventaire.py), leur socle, leur durcissement, leurs sauvegardes et leur supervision (site-mon-01, 2026-09-02). Les équipements — hyperviseurs, commutateurs, frontière — n'ont toujours ni inventaire, ni sauvegarde de configuration, ni supervision |
hebergeur-exploitation.md |
| Authentification | web → Keycloak ; LDAP source unique ; secours par sudo, formulaire local non annoncé |
<rôle>_connexion_locale: false (grafana, forgejo, nextcloud) ; garde de version Forgejo ≥ 10 |
authentification.md |
| Accès & habilitations | Set-OPS amorce un accès sysadmin puis se retire ; les appartenances aux groupes ne sont jamais réconciliées — c'est une personne qui gouverne | construit et éprouvé (2026-08-08) : rôle amorcage_acces (idempotence par existence, D-67), groupes projetés en rôles par serveur_keycloak, meta/acces.yml dans les 5 rôles web ; chaîne LDAP → Keycloak → groupe → service exercée de bout en bout sur Icinga Web 2 |
autorisation.md (§6 = runbook de reprise) |
| SDN EVPN | ajouter un tenant implique 1 zone + 6 VNets + 6 sous-réseaux, tous dérivés du seed | scripts/devis_sdn.py (make devis-sdn) ; nommage dérivé du seed (t17, t17serv), ≤ 8 caractères ; garde P30 |
sdn-evpn.md §2 |
| Pools Proxmox | un pool par tenant : les noms courts de VM sont volontairement identiques d'un tenant à l'autre (même fonction, même nom), et seule la console Proxmox en souffrait | scripts/devis_proxmox_pools.py (make devis-proxmox-pools) ; nom dérivé de l'index ; garde de collision = preuve P28 |
decisions-architecture.md D-37 |
| Routage | aucun commutateur ne route : la frontière est le seul équipement L3 ; les switches commutent | passerelle dit qui porte la passerelle, le SVI se dérive du rôle du porteur |
decisions-architecture.md D-49/50 |
| Devis de service | LIT le système en marche et le compare à ce que le plan dérive ; n'écrit rien (D-23/D-24 portés du réseau aux services). Le playbook relève, Python compare | playbooks/maintenance/devis-*.yml + scripts/devis_*.py ; cible make <sujet>-plan |
devis-services.md |
| Frontière nord/sud | les flux pair: externe — sautés par le pare-feu d'hôte — sont la politique de bordure |
scripts/devis_opnsense.py (make devis-opnsense) ; garde d'accès admin = preuve P24 |
frontiere-opnsense.md |
⚠️ Deux directions de binding, assumées :
app→appcôté app (instancier),app→basecôté base (registre, résolu en rôle pour que le secret ne quitte jamais le rôle). Ne pas unifier l'un dans l'autre sans raison. Cf.bindings-conception.md.
3. Maturité & dette
- Maturité des rôles, échafaudages, rôles-catégories inertes : voir
docs/catalogue-services.md« État d'implémentation » (source de vérité, tenue à jour). - Dette (audit 2026-07-03, revu le 2026-07-29) :
- ✅ soldé — README de rôles : tous les rôles en ont un (les 12 manquants écrits le
2026-07-29 :
serveur_debian,hosts_statiques,resoudre_base,resoudre_annuaire,serveur_dovecot,serveur_postfix,serveur_rspamd,client_backup,serveur_backup,client_resolveur,serveur_oauth2_proxy,serveur_icingaweb2). - ✅ soldé —
exposeest consommé au déploiement :plan/applications.yml→ filtreexpositions_des_applications→ vhosts nginx dérivés (roles/serveur_nginx/tasks/main.yml, templateexpositions.conf.j2, drapeauserveur_nginx_publier_expositions), + alias/etc/hostsposés parhosts_statiques,- SANs des certificats d'edge dérivés par
scripts/instancier.py.
- SANs des certificats d'edge dérivés par
- ⏳ ouvert —
meta/liens.ymlseulement surserveur_postfix(mailstore,milter) : les autres liens app→app viendront. - ⏳ ouvert —
requiert: câblé côté GUI (édition + validationvalider_applications) mais pas consommé au déploiement ; c'est un indice de dépendance applicative. Les dépendances de groupes (celles qui pilotent l'ordre) vivent dansdocs/dependances-groupes.yml+docs/couches-deploiement.yml, et sont bel et bien consommées par l'orchestrateur (scripts/orchestrer.py→playbooks/site.yml).
- ✅ soldé — README de rôles : tous les rôles en ont un (les 12 manquants écrits le
2026-07-29 :
4. Discipline (pour ne plus re-déterrer)
Avant de concevoir ou d'ajouter un mécanisme :
- lire cette carte + le doc du sujet ;
- arpenter le code (
grep) et lire les README des rôles concernés ; - éprouver l'outil AVANT d'écrire le rôle qui l'enveloppe — une machine jetable hors
plan se fabrique en deux minutes (
make cloner-vm, cf.vm-lifecycle.md§4bis). C'est ce qui a évité les bugs de premier déploiement de rspamd, et ce qui a tranché le pivot Stalwart → Postfix/Dovecot. Elle est invisible au moteur : à détruire à la main ; - étendre / factoriser l'existant plutôt qu'ajouter un chemin parallèle.
Un seul agent IA travaille dans le dépôt à la fois (Codex ou Claude) — cf. AGENTS.md.