CHANGELOG : les trois entrees manquantes des 13 et 14 aout

Regle 5 — « rien n'est pret sans l'entree CHANGELOG correspondante ». Trois commits
etaient passes sans la leur, dont le plus instructif de la serie.

- 2026-08-14, la PORTEE de la frontiere : `underlay.tenants`, le defaut silencieux qui
  aurait pose trente objets de Chezlepro sur la frontiere de Technolibre. La note dit
  aussi ce qui n'est PAS corrige : `devis_sdn` et `devis_reseau` partent du meme
  `decouvrir()`.
- 2026-08-13, le formulaire encode : `hasPost()`, le `{"result":"failed"}` NU, et la
  regle qui reste vraie quelle que soit la version — un refus sans validation n'est pas
  un refus, c'est une absence. Le point ouvert (re-eprouver apres mise a jour) est ecrit.
- 2026-08-13, le runbook d'implantation sur un site neuf : les six phases, et surtout ce
  que lui seul porte (adressage cible d'emblee, le piege `make flux`, le site a un noeud).

Aucun code touche. make test 0 ; prouver 37/37 (invocation directe).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Daniel Allaire 2026-08-18 14:02:21 -04:00
parent 7339cc64b4
commit 29148831bf

View file

@ -1,5 +1,144 @@
# CHANGELOG — Set-OPS
## 2026-08-14 — Une frontière ne police que les tenants de **son** site
Premier `make frontiere-plan` sur le second site. Le devis voulait poser sur la frontière
de Technolibre les règles **et** les routes de **Chezlepro** : trente objets de plus, dont
six routes vers des sous-réseaux `10.17.x` qui n'existent pas là-bas.
### Le défaut est de portée, et il est silencieux
Les devis partaient de `devis_reseau.decouvrir()`, qui rend **toute la fédération** — tout
dossier frère portant une nomenclature avec un index. C'était juste tant qu'il n'y avait
qu'un site : l'hébergeur unique portait bien tous les tenants. Dès le second, c'est faux.
Et rien ne l'aurait dit. Le boîtier aurait **accepté** ces trente objets ; aucun n'aurait
jamais correspondu à un paquet ; aucune erreur, aucun avertissement. Une politique qui a
l'air complète et ne protège rien — encore le **chèque vert sur un périmètre vide**.
### Le correctif : l'hébergeur nomme ce qu'il porte
```yaml
underlay:
tenants: [OPS-Technolibre] # les tenants HÉBERGÉS ici, pas la fédération
```
`devis_opnsense` s'y limite (`underlay.tenants_du_site()`). **Clé absente = ancien
comportement**, toute la fédération : un site unique n'a rien à déclarer, c'est le second
qui doit se nommer. Un nom déclaré qu'aucun dossier frère ne fournit est **signalé**, pas
ignoré silencieusement ; et si le filtre ne retient aucun tenant connu, le devis refuse
plutôt que de rendre une politique vide.
**Même hypothèse ailleurs, non corrigée** : `devis_sdn` et `devis_reseau` partent du même
`decouvrir()`. À traiter le jour où ils serviront sur un second site — dit ici pour ne pas
le redécouvrir.
### Au passage, un défaut du document écrit la veille
Le squelette d'`underlay.yml` d'[`implanter-un-tenant-sur-un-site.md`](docs/implanter-un-tenant-sur-un-site.md)
omettait `index`. Sans cette clé, `make underlay` refuse le réseau de gestion en le prenant
pour le supernet d'un **autre** site : message déroutant, cause triviale. Trouvé en s'en
servant, moins de vingt-quatre heures après l'avoir écrit.
`prouver` 37/37, `make test` 0.
> **Ce qui marche avec un seul écosystème n'est pas prouvé.** Comme les six défauts moteur
> qu'avait révélés le second tenant, celui-ci n'existait que parce qu'un second **site**
> existe enfin. Une hypothèse implicite ne se voit qu'au moment où elle cesse d'être vraie.
## 2026-08-13 — La frontière poste en formulaire encodé : `hasPost()` et le `failed` nu
Mesuré sur un OPNsense **24.7** (version ancienne, mise à jour depuis) : **toute écriture**
du moteur y échouait. Alias, règles, NAT, routes — `make frontiere-appliquer` n'aurait rien
posé sur ce boîtier. Ce n'est donc pas un défaut universel du moteur : il écrit correctement
sur la frontière de Chezlepro, plus récente. C'est un problème de **compatibilité**, et le
correctif vaut surtout comme garantie de **portabilité** — le jour où l'on arrive sur un
site dont on ne choisit pas le firmware, ce qui est exactement le cas.
### Le symptôme mérite d'être retenu, lui, quelle que soit la version
Le contrôleur d'OPNsense lit ses champs avec `hasPost(<racine>)`. Si le corps arrive en
`application/json` et que le boîtier ne le décompose pas en variables de POST, ce test est
**faux** :
```
HTTP 200 {"result":"failed"} ← nu : aucune redirection, aucune validation
```
Le contrôleur ne dit pas quel champ manque, **parce que de son point de vue il n'y avait
aucun champ**.
Trois fausses pistes avant la bonne : valeur invalide (un corps **vide** échouait pareil),
racine de payload erronée (elle était juste), privilèges de la clé (la lecture passait). Ce
qui a tranché : *un corps vide aurait dû produire des validations.* Leur absence disait que
le contrôleur n'avait rien reçu.
### Le correctif
`Frontiere` poste désormais `racine[champ]=valeur` — la forme que poste **l'interface web
elle-même**. Aucune version d'OPNsense ne la refuse, alors que le JSON dépend du boîtier.
Tous les corps du moteur sont des dicts plats de chaînes (alias, `rule`, `route`) : un seul
niveau d'imbrication suffit.
**Éprouvé sur le boîtier, dans les deux sens** : `addItem` d'un alias sonde → `saved`,
`delItem` → `deleted`, aucune trace laissée, lecture intacte (11 alias). **Reste ouvert** :
ré-éprouver après la mise à jour, pour vérifier que le formulaire reste bon sur la version
récente.
> **Un `failed` sans validation n'est pas un refus, c'est une absence.** Quand un contrôleur
> ne nomme aucun champ fautif, cesser de chercher le mauvais champ : il n'a rien reçu.
## 2026-08-13 — Implanter un tenant sur un site neuf : le runbook de l'intervention
Le dépôt couvrait déjà « l'hébergeur prépare son matériel » et « un tenant **vivant** change
d'hébergeur, sans coupure ». Il manquait le troisième cas, qui est celui qu'on s'apprête à
faire : un tenant dont le plan existe déjà **prend corps sur un site qui n'a jamais rien
porté**. Rien à migrer, rien à interrompre — donc ni la séquence de gel/bascule de
`migration-tenant.md`, ni le bootstrap du QUICKSTART.
[`docs/implanter-un-tenant-sur-un-site.md`](docs/implanter-un-tenant-sur-un-site.md),
**six phases**, chacune fermée par une commande qui **interroge le système** :
```
0 bureau index du site = index du tenant, dépôt hébergeur, gabarit, voûte
1 reconnaître make underlay, make placement-plan
2 frontière make frontiere-plan
3 gabarit convertir en template — un clone de VM vivante en ferait 14 copies
4 composer les DEUX symlinks (D-80) + les 4 valeurs de placement
5 matérialiser make sdn-appliquer, puis make reconstruire
6 recette devis, restauration éprouvée, supervision
```
### Ce que le runbook porte et qu'aucun autre document ne dit
- **Un site neuf se bâtit d'emblée dans l'adressage cible** (D-77/D-78). Le site historique
est encore en `10.0.x` et migrera par les runbooks §6 ; sur un site vierge, la cible ne
coûte rien. Deux sites en `10.0.0.0/24` rendraient la **reprise mutuelle impossible** :
deux plans de gestion identiques ne peuvent pas s'atteindre.
- **L'ordre de `reconstruire`, ligne à ligne**, dont le piège `make flux` : sans lui,
nftables tombe en `policy drop` **sans aucune règle** — la flotte monte, SSH répond, et
tout le reste est mur (trouvé le 2026-08-10).
- **Un site à un seul nœud** : ce nœud est aussi nœud de sortie, aucun pont ne peut être
partiel, aucune haute disponibilité. À dire, pas à laisser supposer.
- **PVE 9 n'a jamais été éprouvé ici** : tout écart est inconnu jusqu'à mesure.
- **« Ce qui n'est PAS fait en repartant »** — resserrer le compte d'API, le lien
inter-sites, le gabarit des deux côtés, la voûte hors de son propre site.
**En annexe**, les squelettes d'`underlay.yml` et de `proxmox-hebergeur.yml` pour un site à
un nœud, à remplir depuis la **reconnaissance** et jamais de mémoire — c'est en recopiant
des listes chez chaque tenant qu'elles avaient divergé.
Sixième porte dans la table de routage du README. Les 21 cibles `make` citées ont été
vérifiées comme existantes. `prouver` 37/37 (dont P34 : 40 documents déclarent leur
lecteur), `make test` 0.
> **La règle qui commande tout l'ordre du document** : *rien n'est fait tant que ce n'est
> pas mesuré sur place.* Une valeur transmise par courriel, une liste relevée dans
> l'interface web, un `vmbr` cité de mémoire — chacun des trois a déjà produit une panne ici.
## 2026-08-13 — La machine d'épreuve jetable existait, et n'était écrite nulle part
L'exploitant a découvert par hasard, en creusant l'intrant « pont réseau », qu'on peut