diff --git a/CHANGELOG.md b/CHANGELOG.md index 94b3706..678f877 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,32 @@ # CHANGELOG — Set-OPS +## 2026-08-03 (suite 4) — un index des décisions d'architecture + +`docs/decisions-architecture.md`. Les décisions étaient écrites là où elles s'appliquent, et +leur histoire dans ce journal — mais « pourquoi le `/29` et pas le `/30` ? » demandait de +relire vingt entrées. Le registre ne répète rien : il dit **quelles décisions existent, +pourquoi, où lire le détail, et ce qui les garde**. + +**28 décisions** en quatre familles — le réseau, qui possède quoi, les secrets, la méthode. +Chaque ligne porte sa raison en une phrase et sa preuve quand il y en a une. Une décision peut +n'être gardée par aucune preuve : elle reste une décision, et le registre le montre plutôt que +de laisser croire à une couverture complète. + +### La section qu'on omet d'habitude : les décisions renversées +Trois y figurent — l'isolation par ACL de commutateur, le routage inter-zone sur les +commutateurs, l'underlay gitignoré à la racine du moteur. Les garder évite de refaire le +chemin, et **explique pourquoi le code porte encore des branches qui semblent inutiles** : +`acl_inter_tenant: true` et `routage_tenants: switch` restent les défauts, parce qu'une autre +fabric peut en être capable. + +> Aucun de ces renversements ne vient d'un changement d'avis : les trois viennent d'un fait +> découvert **après** la décision — une commande absente de l'aide du matériel, une capacité +> manquante, une dizaine de modifications irrécupérables. C'est l'argument le plus fort pour +> éprouver avant de figer. + +Les 30 renvois internes du registre ont été vérifiés : aucun document ni aucune section citée +n'est introuvable. + ## 2026-08-03 (suite 3) — les preuves réseau sont rattachées à de vraies affirmations Trois preuves — **P21**, **P23**, **P24** — renvoyaient à `AFF-001`, qui affirme que *« Set-OPS diff --git a/docs/carte-set-ops.md b/docs/carte-set-ops.md index d7eaa73..0de25d6 100644 --- a/docs/carte-set-ops.md +++ b/docs/carte-set-ops.md @@ -22,6 +22,7 @@ code + les README de rôles). Cette page comble ces deux trous. | **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`) | | **Preuve / recette** | `docs/audit/affirmations.md` (registre), `make prouver` → `docs/audit/preuve-.md`, `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` — **28 décisions**, 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). **Non éprouvé** : spike avant génération | | **Migration de tenant** | `docs/migration-tenant.md` — recette en 8 étapes, 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` | diff --git a/docs/decisions-architecture.md b/docs/decisions-architecture.md new file mode 100644 index 0000000..84f007d --- /dev/null +++ b/docs/decisions-architecture.md @@ -0,0 +1,79 @@ +# Registre des décisions d'architecture + +> **À quoi sert ce document.** Les décisions sont écrites là où elles s'appliquent — +> `frontiere-opnsense.md`, `sdn-evpn.md`, `migration-tenant.md`, `underlay.yml.example` — et +> leur histoire vit dans le `CHANGELOG`. Ce registre ne les répète pas : il dit **quelles +> décisions existent, pourquoi, et où lire le détail**. Sans lui, « pourquoi le `/29` et pas +> le `/30` ? » demande de relire vingt entrées de journal. + +**Ce n'est pas** la doctrine de conduite (`AGENTS.md` §Principes), ni le registre des +affirmations prouvables (`docs/audit/affirmations.md`). Une décision peut n'être gardée par +aucune preuve : elle reste une décision. + +--- + +## 1. Le réseau + +| # | Décision | Pourquoi | Détail | Garde | +|---|---|---|---|---| +| **D-01** | OPNsense est une frontière **nord/sud**, pas la passerelle des zones | le routage inter-zone reste au débit ligne ; la bordure ne voit pas l'est-ouest | `frontiere-opnsense.md` §1 | — | +| **D-02** | Le **lien de transit** vit dans l'underlay, pas dans un tenant | la frontière route vers tous les tenants par le même saut : il ne peut dériver d'aucun `index` | `frontiere-opnsense.md` §6 | P23 | +| **D-03** | Le `/29` de transit : frontières en bas (`.1`,`.2`), SVI en haut (`.6`), `.3` **réservée** | deux pare-feux cohabitent pendant une transition ; `.3` attend une IP virtuelle CARP | `frontiere-opnsense.md` §6 | P23 | +| **D-04** | Un point de routage porte **le même dernier octet** sur tous ses sous-réseaux | on retient une adresse, pas treize | `frontiere-opnsense.md` §4 | P23 | +| **D-05** | **Un seul commutateur route** ; les autres restent en L2 pur | sans MLAG, dupliquer les SVI créerait autant de conflits d'adresses que de zones | `underlay.yml.example` | P23 | +| **D-06** | Les réseaux déclarent leur **fabric** ; un devis ne parle que de la sienne | le stockage jumbo vit sur ses propres commutateurs — un devis est une configuration, pas un inventaire | `sdn-evpn.md` §4 | P23 | +| **D-07** | **Pas d'ACL** sur cette fabric (`acl_inter_tenant: false`) | le matériel ne sait pas lier une ACL à un SVI ; des règles jamais liées auraient l'air d'isoler | `frontiere-opnsense.md` §1 | — | +| **D-08** | Le **routage tenant passe au SDN EVPN** — une zone par tenant | une zone EVPN est un VRF : l'isolation devient structurelle, pas réglementaire | `sdn-evpn.md` | P23 (MTU) | +| **D-09** | Le **filtrage inter-zone d'un tenant** se fait au même niveau (EVPN) | là où le routage a lieu | `sdn-evpn.md` §3 | — | +| **D-10** | L'**inter-tenant passe obligatoirement par la frontière** | il sort du VRF, donc traverse une bordure en `block` par défaut : il ne peut plus être oublié | `sdn-evpn.md` §3 | — | +| **D-11** | La **sortie générale est déclarée** dans le registre des flux | `block out` est un vrai default-deny ; un besoin oublié ne se manifeste pas par un refus clair | `frontiere-opnsense.md` §8 | P09 | +| **D-12** | Nommage : **`bifrost`** aux frontières, **`sleipnir`** à la fabric | Bifröst est le pont vers l'extérieur ; Sleipnir traverse les mondes sans en sortir | `frontiere-opnsense.md` §6 | — | + +## 2. Qui possède quoi + +| # | Décision | Pourquoi | Détail | Garde | +|---|---|---|---|---| +| **D-13** | Un **hébergeur** sert plusieurs **tenants** et a son tenant par défaut | Chezlepro est les deux à la fois, ce qui masquait la distinction | `frontiere-opnsense.md` §2 | — | +| **D-14** | `underlay.yml` appartient à l'**hébergeur**, monté par symlink | ce sont ses commutateurs, ses câbles ; le moteur est générique, un tenant n'en possède pas | `sdn-evpn.md`, `underlay.yml.example` | — | +| **D-15** | Ce symlink **ne suit pas** `make instance-utiliser` | basculer le tenant actif ne change pas la fabric | `frontiere-opnsense.md` §2 | — | +| **D-16** | Les intrants de la **frontière** se lisent chez l'hébergeur | un hébergeur n'a qu'une frontière pour tous ses tenants | `frontiere-opnsense.md` §2 | — | +| **D-17** | L'hébergeur **n'est pas déclaré** : le symlink le désigne | une seconde déclaration ouvrirait deux valeurs contradictoires | `frontiere-opnsense.md` §2 | — | +| **D-18** | Chaque tenant a un **responsable désigné** | sans lui, « qui peut décider de déménager cette organisation ? » se pose au pire moment | `migration-tenant.md` §3 | — | + +## 3. Les secrets + +| # | Décision | Pourquoi | Détail | Garde | +|---|---|---|---|---| +| **D-19** | **Une seule voûte** par instance (`group_vars/all/vault.yml`) | un mot de passe, un endroit | `config-proxmox.md` | P18 | +| **D-20** | La liste des secrets se **recense**, elle ne s'écrit pas | trois copies manuelles ont existé, toutes ont divergé | `intrants-communs.md` §H | P18 | +| **D-21** | L'**empreinte du root CA n'est pas un secret** : elle se dérive à chaud | un `from-zero` régénère l'AC ; figée en voûte, elle serait périmée | `roles/client_pki/README.md` | — | +| **D-22** | Migrer un tenant, c'est **révoquer**, pas transmettre | sinon l'ancien hébergeur garde à vie l'accès aux secrets d'un client parti | `migration-tenant.md` §6 | — | + +## 4. La méthode + +| # | Décision | Pourquoi | Détail | Garde | +|---|---|---|---|---| +| **D-23** | Les cibles **hors flotte** reçoivent un **devis**, pas un rôle | commutateurs, frontière et SDN sont des objets de cluster ; un rôle boucle sur des hôtes | `sdn-evpn.md` §7 | — | +| **D-24** | Un devis **n'écrit rien** : il se relit, puis s'applique | proportionné au risque — une config ratée partitionne tout un cluster | `frontiere-opnsense.md` §7 | — | +| **D-25** | Le dépôt **n'affirme pas que ses devis s'appliquent**, il affirme qu'ils **dérivent** | leur syntaxe dépend d'un matériel que le dépôt ne possède pas | `audit/affirmations.md` §10 | — | +| **D-26** | Un modèle peut porter un **underlay** ; tous les hébergeurs n'ont pas le même matériel | générique public, étoffés en privé | `exemples/modeles/socle/README.md` | P17 | +| **D-27** | **Migration** : le receveur est prouvé prêt **avant** tout gel | l'interruption se réduit au delta et à la propagation DNS | `migration-tenant.md` §4 | — | +| **D-28** | Le mandat de migration est **signé par le tenant**, pas convenu entre hébergeurs | il n'y a pas de registre central pour arbitrer ; une organisation n'est pas la propriété de son hébergeur | `migration-tenant.md` §2 | — | + +--- + +## 5. Décisions renversées + +Les garder évite de refaire le chemin, et explique pourquoi le code porte encore des branches +qui semblent inutiles. + +| Décision | Tenue du … au … | Pourquoi renversée | Ce qu'il en reste | +|---|---|---|---| +| L'isolation inter-tenant est portée par des **ACL de commutateur** | 2026-07-29 → 08-02 | le matériel ne sait pas lier une ACL à une interface de routage | `acl_inter_tenant: true` reste le défaut : une autre fabric peut en être capable | +| Le **routage inter-zone** est porté par les commutateurs L3 | 2026-07-29 → 08-02 | sans ACL, l'isolation devenait déclarative ; EVPN la rend structurelle | `routage_tenants: switch` reste le défaut et reste généré | +| L'`underlay.yml` vit à la racine du moteur, gitignoré | 2026-07-31 → 08-02 | consommé par deux générateurs, validé par une preuve, versionné nulle part | `SETOPS_UNDERLAY` permet toujours de le pointer ailleurs | + +> **Ce que ces renversements ont en commun.** Aucun ne vient d'un changement d'avis : les trois +> viennent d'un fait découvert **après** la décision — une commande absente de l'aide du +> matériel, une capacité manquante, une dizaine de modifications irrécupérables. C'est +> l'argument le plus fort pour éprouver avant de figer.