Set-OPS-Public/docs/conception-contextes.md
Daniel Allaire 8c1e8c725b contexte : etape 3, le pare-feu Proxmox lit la face reseau
La face publie le verdict des flux conditionnels ; devis_proxmox_fw y lit
inventaire, verdicts et administration. Devis identique, octet pour octet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 12:51:37 -04:00

17 KiB

Contextes : un tronc commun, deux classes (SITE et LOCATAIRE)

Pour qui : le mainteneur — comment le moteur sait s'il sert un site ou un locataire, et ce que les deux s'apprennent l'un à l'autre.

Statut : arrêtée avec l'exploitant le 2026-10-04. Rien n'est encore construit ; les décisions sont au §7, le chemin au §6.

1. Le problème, mesuré

Le moteur ne sait pas dans quel contexte il tourne : chaque script le devine. Relevé du 2026-10-04 : 33 scripts font leur propre déduction, à partir de cinq indices différents.

Indice Ce qu'on en déduit Scripts
lien instance/ ou SETOPS_INSTANCE « un locataire est monté » 23
lien underlay.yml ou SETOPS_UNDERLAY « un site est monté » ; son plan est à côté 8
SETOPS_INVENTAIRE quel inventaire de locataire lire 8
../*/plan/nomenclature.yml « la fédération », les locataires frères 11
../SITE-*/underlay.yml « les sites » 2

Les indices ne concordent pas toujours, et chaque désaccord a déjà produit un défaut silencieux :

  • 2026-08-14 : frontiere-plan voulait poser sur la frontière de Technolibre les règles de Chezlepro. « La fédération » valait « les locataires de ce site », jusqu'au second site.
  • 2026-09-16 : la console du runner du site affichait zéro machine, sans erreur. Elle cherchait un inventaire de locataire là où il n'y en a pas.
  • 2026-10-04 : make ci, sur le poste, mélangeait le modèle public et les écosystèmes réels. P74 lisait SETOPS_UNDERLAY (le modèle) ; P82 lisait le lien underlay.yml et les dossiers frères (le site réel).

Les rôles Ansible, eux, ne posent pas ce problème : ils sont déjà le tronc commun. Un même serveur_postgresql sert au site et chez un locataire.

2. Le modèle

                    Ecosysteme            (tronc commun)
                   /          \
               Site            Locataire
                 \              /
                  `-- contrat --'          (associations : un site A des locataires,
                                            un locataire A un site)

2.1 Le tronc commun : Ecosysteme

Ce que tout écosystème possède, quel que soit son contexte :

  • un nom et un dépôt (SITE-Chezlepro, OPS-Technolibre) ;
  • une voûte et sa clé (~/.config/setops-vault-<dépôt>) ;
  • un plan (<dépôt>/plan/) et un index, dont dérive son adressage (le site aussi depuis le 2026-09-20) ;
  • des machines, déployées par les mêmes rôles : socle, durcissement, PKI, journaux, métriques, supervision, sauvegarde de son propre état ;
  • une filiation : le moteur et le commit dont il descend ;
  • les preuves communes : lint, rendu des gabarits, adressage dérivé, etc.

Méthodes abstraites, que chaque classe surcharge : inventaire(), machines(), preuves(), verbes(), console().

2.2 Site(Ecosysteme)

  • Déclaration : underlay.yml (le matériel, les réseaux site et fabric) et plan/ (10-intrants.yml, serveurs, applications, domaines, bases).
  • Inventaire : dynamique (site_inventaire.py). Le site ne dérive rien d'un plan de services ; sa déclaration est sa forme finale.
  • Ce qu'il porte en propre : le matériel (hyperviseurs, commutateurs, frontière), la matérialisation des VM (Proxmox), le SDN, le pare-feu Proxmox, la frontière OPNsense, le DNS public, le dépôt des sauvegardes des locataires, le cache et les artefacts, la forge du génome.
  • Relation : locataires(), la liste de underlay.tenants résolue en objets Locataire. Le site n'en lit que la face réseau (§2.4).

2.3 Locataire(Ecosysteme)

  • Déclaration : plan/ (nomenclature, serveurs, applications, bases, domaines).
  • Inventaire : généré (instancier.py → hosts.yml). C'est la méta-classe de meta-classe.md : une définition qui engendre toute la flotte.
  • Ce qu'il porte en propre : la configuration de ses services, la remise au client.
  • Relation : site(), l'hébergeur que nomme parente.yml, résolu en objet Site. Le locataire n'en lit que les intrants exposés (§2.4).

2.4 Ce que le site et le locataire s'apprennent l'un à l'autre

Les deux entités s'informent mutuellement. Relevé du 2026-10-04 : qui décide de chaque information, où elle vit, et comment elle parvient à l'autre.

Ce que chacun a sous la main. Le runner du site porte le moteur, son dépôt et ceux de ses locataires (sans leurs voûtes). Le runner d'un locataire ne porte que le moteur et son propre dépôt. Le poste porte tout.

Le site informe le locataire, par trois canaux

Canal 1 : des copies écrites à la main dans le dépôt du locataire.

Information Décidée par Tenue chez le site dans Copiée chez le locataire dans Contrôle
son index le site underlay.yml → tenants plan/nomenclature.yml (index) underlay valider
son adresse publique le site opnsense.yml → opnsense_ips_publiques 10-intrants.yml (ip_publique) —
les 10 intrants de service : résolveur, cache, binaires, forge du génome, cible de sauvegarde, DNS public, plan d'administration, passerelle le site (dérivés de son plan, par site_intrants.py) son plan 10-intrants.yml, et serveur_ops.yml pour la forge site_intrants.py --verifier, seulement là où les deux dépôts sont présents (le poste)
sa racine de confiance le site ac-racine-site.crt le même fichier, copié —
hors contrat : un dépôt de la forge du site désigné par son adresse — — serveur_web_dorsal.yml (Chezlepro) aucun

Canal 2 : une lecture directe, au moment de générer l'inventaire. instancier.py ouvre l'underlay.yml et le plan du site pour écrire le hosts.yml du locataire. Mesuré sur Technolibre, inventaire généré avec puis sans le site monté : quatre variables changent.

Variable du locataire Avec le site monté Sans le site
chrony_serveurs 10.0.4.1 (la frontière) absente
proxmox_pont t23appl (le VNet SDN) absente
proxmox_etiquette_vlan aucune (le SDN étiquette) 1236
serveur_resolveur_zones_deleguees genese.internal → 10.37.34.11 absente

Canal 3 : le réseau. Le runner du locataire tire son génome de la forge du site ; il est né de l'insémination par le runner du site.

Le locataire informe le site : le site lit et recalcule

Information Décidée par Tenue chez le locataire dans Parvient au site par
ses zones et son adressage dérivés de l'index plan/nomenclature.yml le site lit le fichier
les VM à matérialiser le locataire plan/serveurs.yml → hosts.yml le site lit les fichiers (placement, clonage, pools, SDN)
ses flux ses rôles et son plan meta/flux.yml des rôles (moteur), croisés avec son hosts.yml le site recalcule lui-même, avec sa version du moteur et la totalité de l'inventaire du locataire → frontière, NAT, pare-feu Proxmox
ses domaines publics le locataire plan/domaines.yml (+ applications.yml, serveurs.yml) le site lit les fichiers → DNS public secondaire
sa clé de sauvegarde le locataire inventories/*/group_vars/serveur_backup.yml le site lit le fichier → compte Unix sur le dépôt
ses accès d'administration le locataire plan/acces.yml, nftables_admin_ssh le site lit les fichiers → pairs WireGuard, règles d'administration

Le SDN, lui, ne prend aucun flux : il ne filtre pas. Il ne reçoit que l'index, dont il dérive la zone, les 6 VNets et les 6 sous-réseaux.

À l'exécution, entre machines

Ces échanges-là passent par le réseau, pas par les dépôts. Ils sont déjà déclarés en flux : le locataire dépose ses sauvegardes chez le site (SFTP), tire ses paquets, ses binaires et son génome, entre par le tunnel d'administration du site ; le site réplique les zones publiques du locataire (AXFR signé TSIG).

Ce que le relevé montre

  1. Le site fouille l'intérieur du locataire. Six fichiers de son plan et de son inventaire, group_vars compris. Rien ne dit ce que le locataire accepte de montrer. Renommer un champ chez le locataire casse le site sans bruit.
  2. Les flux sont calculés deux fois, par le locataire pour ses nftables et par le site pour la frontière et Proxmox, chacun avec sa version du moteur. Ils concordent tant que les deux runners tiennent le même commit (c'était le cas le 2026-10-04), mais rien ne l'impose.
  3. Le locataire vit de copies : douze valeurs et un certificat, recopiés à la main, plus une valeur hors contrat. La garde qui compare ne tourne que sur le poste ; le runner du locataire ne peut pas savoir que sa copie a vieilli.
  4. L'inventaire d'un locataire dépend du site monté au moment de le générer. Généré sur le runner du locataire, qui n'a pas le dépôt du site, il perdrait son serveur de temps, son SDN et sa délégation DNS. Ça ne s'est jamais vu, parce que l'inventaire est toujours généré sur le poste puis versionné.
  5. Deux décisions du site (l'index, l'adresse publique) vivent en double.

Proposition : deux fiches, une dans chaque sens

Chacun publie ce qu'il donne à l'autre, dans une fiche générée par le moteur, jamais écrite à la main. Chacun ne lit que la fiche que l'autre lui destine. Plus aucune lecture croisée, plus aucun recalcul.

  • La fiche du site pour un locataire. Tout ce que le site lui attribue (index, adresse publique) et lui offre : les 10 intrants, sa racine de confiance, et ce que l'instancier allait lire en douce (serveur de temps, délégation DNS, mode SDN et nom des VNets). Une fiche par locataire : aucun ne voit le plan du site ni ses voisins. L'instancier ne lit plus que cette fiche, et l'inventaire devient identique où qu'on le génère.
  • La face réseau du locataire. Ce qu'il demande au site : VM à matérialiser, zones, domaines publics, clé de sauvegarde publique, accès d'administration, et ses flux déjà résolus (adresses, ports, protocoles), ceux avec l'extérieur pour la frontière et ceux de chaque VM pour Proxmox. Le locataire génère déjà ses flux résolus (flux-genere/*.nft, *.connectivite.json) : la face réseau en est la partie destinée au site. Le site ne recalcule plus rien : il applique ce que le locataire publie, après l'avoir confronté à sa propre politique.
  • Chaque fiche porte l'empreinte de sa source, et une preuve de chaque côté vérifie que la fiche reçue correspond à ce que l'autre a publié.

Où en est l'étape 2 (2026-10-04). La fiche du site (P84), les faits de la face réseau (P85) et les flux de chaque machine (P86) existent, et disent exactement ce que les lectures croisées produisent. La première mesure des flux a trouvé une information que le locataire jetait : les clients nommés d'un port aussi public, que Proxmox doit admettre nommément. Il les publie désormais (sources_declarees). La frontière, en trois temps : les identités (P87), les entrées publiques (P88), l'administration (P89) et les sorties (P90) sont faites. L'étape 2 est terminée (2026-10-05). Étape 3 : l'instancier lit la fiche déposée par le site, et l'inventaire d'un locataire se génère sans son site, à l'octet près (P91). Le locataire publie sa face réseau (P92) ; les comptes de sauvegarde, le DNS public et le pare-feu Proxmox du site la lisent.

Méthodes du contrat : site.fiche_pour(locataire), locataire.face_reseau(). Une classe n'ouvre jamais les fichiers de l'autre ; une preuve vérifiera la règle.

Ce que les fiches donnent : la portabilité

Un locataire qui change de site, pour un déménagement, un plan de reprise ou une émancipation, n'a plus qu'à recevoir la fiche de son nouveau site. Son dépôt ne contient plus rien d'interne à l'ancien : ni copie d'adresse, ni inventaire généré avec l'ancien site monté. En face, le nouveau site n'a qu'à lire sa face réseau. Aujourd'hui, la même bascule demande de corriger des copies dans plusieurs fichiers, puis de régénérer l'inventaire avec le nouveau site monté sur le poste.

3. Le poste : un sélecteur

Aujourd'hui, le poste monte les deux contextes en même temps (instance/ + underlay.yml), et ConsolePoste hérite de ConsoleLocataire. Désormais :

  • Le poste choisit un contexte actif : un site ou un locataire. La console ouvre celui-là, et seulement celui-là.
  • Une opération qui traverse les deux nomme ses objets au lieu de les deviner. reconstruire-locataire en est l'exemple : le site matérialise, puis le locataire monte. L'orchestration devient site.materialiser(locataire) puis locataire.monter().
  • Les runners ne changent pas : celui du site n'a qu'un Site, celui d'un locataire qu'un Locataire. Leur contexte est désormais dit, plus déduit.

4. Qui surcharge quoi

Méthode Tronc commun Site Locataire
inventaire() abstraite script dynamique (underlay.yml) hosts.yml généré du plan
machines() abstraite VM du site + équipements VM du plan
adressage() dérivé de l'index zones site dérivées, liens fabric écrits 6 zones dérivées
sauvegardes() son propre état, vérifié par restauration + héberge les dépôts des locataires dépose chez son site
supervision() sondes déclarées par les rôles + matériel, fabric, frontière —
raser() / reconstruire() — site_raser.py raser.py, reconstruire_locataire.py
preuves() preuves communes + preuves de site (P23, P74, P82…) + preuves de locataire
verbes() verifier, publier… site-*, frontiere-*, proxmox-* appliquer, instancier, remise-*
console() — console SITE console LOCATAIRE

5. Ce qui ne change pas

  • Les rôles Ansible, déjà communs.
  • Les formats de plan : pas dans ce chantier.
  • La doctrine d'AGENTS.md.

6. Le chemin, chaque pas prouvé avant le suivant

  1. scripts/contexte.py : Ecosysteme, Site, Locataire, contexte_actif(), Site.charger(nom), Locataire.charger(nom). Tests unitaires. Rien ne l'utilise encore.
  2. Les deux fiches, générées à côté de l'existant sans rien remplacer : site.fiche_pour(locataire) et locataire.face_reseau(). Une preuve vérifie que chaque fiche dit exactement ce que les lectures croisées d'aujourd'hui produisent.
  3. Les consommateurs basculent sur les fiches, un par un : l'instancier sur la fiche du site (l'inventaire généré doit rester identique, octet pour octet) ; la frontière, Proxmox, le DNS public et les comptes de sauvegarde sur la face réseau (chaque devis doit rester inchangé).
  4. prouver.py : chaque preuve déclare son contexte (commun, site, locataire) et reçoit son écosystème du module.
  5. Les autres scripts, un par un, vérifiés par make verifier et par un devis inchangé.
  6. Une preuve « aucune devinette, aucune lecture croisée » : les indices du §1, et toute ouverture d'un fichier de l'autre contexte, interdits hors de contexte.py.
  7. Les verbes du Makefile rangés par contexte.
  8. Les consoles : le sélecteur et deux consoles (chantier suivant).
  9. OPS-Modele : un locataire modèle, et sans doute un site modèle, vérifiés chacun dans son contexte.

Après les étapes 3 et 5, une reconstruction prouve que la flotte n'a pas bougé.

7. Décisions et questions ouvertes

Tranché par l'exploitant

  • Deux classes, Site et Locataire, qui héritent d'un tronc commun (2026-10-04).

  • Le poste est un sélecteur : un contexte actif à la fois (2026-10-04).

  • On commence par le moteur, les consoles viennent ensuite (2026-10-04).

  • Le site dépose sa fiche dans le dépôt du locataire (2026-10-04), comme il y amorce déjà son runner. Le runner du locataire n'a besoin d'aucun accès au dépôt du site, et ne voit ni le plan du site ni ses voisins.

  • Le contexte actif se nomme dans un fichier contexte explicite (2026-10-04), une seule valeur : site:SITE-Chezlepro ou locataire:OPS-Technolibre. Un sélecteur qui monte deux liens à la fois contredirait sa propre règle. Les liens instance/ et underlay.yml restent le temps de la bascule, lus par le seul contexte.py.

  • Un modèle SITE public, SITE-Modele, à côté d'OPS-Modele (2026-10-04). Sans site, la CI ne peut exercer ni les preuves de site (P74, P81, P82) ni la fiche que le site dépose chez le locataire.

  • Le site a aussi son parente.yml (2026-10-04) : la filiation est dans le tronc commun.