Set-OPS-Public/docs/filiation-emancipation.md
Daniel Allaire 5880b22d5a D-87 : ce que l hebergeur n a pas le droit de VOIR
Question posee : quels roles ne pas embarquer dans le site ? La table de
mutualisation existait et repondait presque — mais une de ses lignes
CONTREDISAIT ce qui tourne.

Elle disait « observabilite | oui | l hebergeur surveille ses locataires ».
Or chaque ecosysteme a son propre Icinga, et celui du site ne voit que ses
sept machines. L implementation avait raison, la doctrine avait tort.

Et cette case autorisait ce que la ligne du dessous interdit. LES JOURNAUX
CONTIENNENT DU CONTENU — un mot de passe dans un message d erreur, une
donnee metier dans une trace, qui a fait quoi et quand. Un hebergeur qui
ingere les journaux de son locataire en sait PLUS que s il detenait son
annuaire : l annuaire dit qui existe, les journaux disent ce qu ils font.

Decoupee en trois : disponibilite oui (une VM tombee est un fait de la
fabric), metriques oui avec reserve (elles disent quand et combien, ce qui
suffit a lire l activite d une organisation), journaux NON.

ET LA LIGNE PKI EST TRANCHEE : NON. Une AC intermediaire signee par l hote
lui donnerait le pouvoir d emettre des certificats valides pour les noms
du locataire — donc de se presenter comme n importe lequel de ses
services, devant les propres machines du locataire, qui les accepteraient
puisque c est ce que la chaine de confiance leur demande. Meme pouvoir que
l annuaire, sous une forme moins visible : aucune trace cote locataire.
Une PKI par ecosysteme, jamais derivee de l hote.

LE REVERS, MESURE ET ASSUME : le site n a ni client_journal ni
client_metrique, aucun loki ni prometheus. A refuser de voir ceux des
locataires, il s est prive des siens. Le remede n est pas d assouplir la
regle mais de lui donner sa propre pile.

Verifie par ailleurs : rien d intime n est au site aujourd hui. La
frontiere etait tenue en pratique avant d etre ecrite au net.

make prouver : CONFORME, 64 OK, 0 echec, 0 saute.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-10 04:05:28 -04:00

282 lines
15 KiB
Markdown

# Filiation, mutualisation, émancipation
> **Pour qui :** l'**architecte** de la lignée et l'**hébergeur** qui abrite des
> écosystèmes tiers. Décrit les trois âges d'un écosystème et ce qu'ils impliquent au plan.
## Le constat
Un écosystème ne naît pas autoportant. Il lui faut une forge pour lire son génome, une
source d'artefacts pour ses paquets, un dépôt pour ses sauvegardes — et rien de tout cela
n'existe la première seconde. Il emprunte donc à son hôte.
Jusqu'ici, le moteur a rencontré ce besoin **trois fois sans le nommer** :
```
client_artefacts_actif dérivé : « une source existe-t-elle chez moi ? »
serveur_ops_forge_externe « je lis mon génome ailleurs »
client_backup_cible patient 0 sauvegarde chez eregion — mutualisé, en production
```
Trois astuces, une seule notion. Ce document la déclare.
## Les trois âges
```
FILIATION l'enfant emprunte à son parent ce qu'il ne porte pas encore
MUTUALISATION un état DURABLE et CHOISI — on emprunte parce qu'on le veut
ÉMANCIPATION l'écosystème porte tout lui-même
```
**L'émancipation n'est pas une obligation.** Un petit organisme peut rester mutualisé
toute sa vie ; ce qui compte est qu'il *puisse* s'émanciper, et que la sortie ne soit ni
un piège ni une reconstruction. C'est la différence entre un locataire et un captif.
## Ce que ça veut dire pour les modèles
Un modèle sans forge n'est pas un modèle amputé : c'est un écosystème **au premier âge**,
dont le runner lit le génome chez son hôte. L'ajout d'une forge n'est pas une correction,
c'est une **émancipation**.
```
premier âge serveur_ops_forge_externe: true + l'adresse de la forge de l'hôte
émancipé serveur_ops_forge_externe: false + sa propre serveur_forgejo au plan
```
## Ce que ça veut dire pour l'hébergeur
L'hébergeur ne vend plus seulement des machines : il vend **l'abri pendant la jeunesse**.
Chaque service mutualisé est une ligne de facture, et chaque émancipation est une décision
du client — jamais une rupture technique.
Pour une clientèle d'OBNL et de coopératives, c'est le bon geste : on entre à bas coût, on
grandit vers l'autonomie, et **on n'est jamais captif**, puisque le génome est déjà chez
soi dès le premier jour.
## Ce qui est mutualisable, et ce qui ne l'est pas
> **Le critère, à deux tranchants.** Un rôle appartient au SITE s'il sert la **relation**
> avec les locataires, ou les machines du site elles-mêmes. Il appartient au TENANT s'il
> sert des **utilisateurs** ou des **applications**. À l'usage, ça se décide sur deux
> questions plus dures :
>
> 1. **Ce que le site ne doit pas pouvoir VOIR** — sinon l'hébergement n'est plus
> souverain, quelles que soient les intentions.
> 2. **Ce que le tenant doit pouvoir EMPORTER** — sinon l'émancipation est un mot.
| service | mutualisable | remarque |
|---|---|---|
| forge (génome) | oui | `serveur_ops_forge_externe` |
| source d'artefacts | oui | `client_artefacts` se désactive seul si aucune n'existe |
| sauvegarde | oui | `client_backup_cible` pointe déjà hors de l'écosystème ; le site héberge du **chiffré côté client** et ne peut rien en juger |
| **disponibilité** (est-ce debout ?) | oui | c'est sa fabric : il doit savoir qu'une VM est tombée |
| **métriques** (charge, disque) | oui, avec réserve | révèlent des **rythmes d'activité**, pas du contenu |
| **journaux** | **non** | voir ci-dessous — plus intime que l'annuaire |
| relais courriel | oui | l'enveloppe transite ; le contenu appartient au locataire |
| DNS | partiellement | un sous-domaine délégué, pas la zone entière |
| **PKI** | **non** (tranché le 2026-09-10) | voir ci-dessous |
| **identité** (LDAP/SSO) | le plus intime | mutualiser l'annuaire, c'est confier ses gens |
| **la voûte** | **jamais** | les secrets ne se mutualisent pas, à aucun âge |
### Pourquoi « observabilité » a été découpée en trois
La ligne disait : *« observabilité | oui | l'hébergeur surveille ses locataires »*. Elle
était trop grossière, et elle **contredisait ce qui tourne** : chaque écosystème a son
propre Icinga, et celui du site ne voit que ses sept machines (mesuré le 2026-09-10).
Surtout, elle autorisait en une case ce que la ligne du dessous interdit. **Les journaux
contiennent du contenu** — un mot de passe dans un message d'erreur, une donnée métier dans
une trace, qui a fait quoi et quand. Un hébergeur qui ingère les journaux de son locataire
en sait **plus** que s'il détenait son annuaire : l'annuaire dit qui existe, les journaux
disent ce qu'ils font.
La disponibilité, elle, est légitime : une VM tombée est un fait de la **fabric**, que
l'hébergeur porte. Les métriques sont entre les deux — elles ne disent pas *quoi*, mais
elles disent *quand* et *combien*, ce qui suffit à lire l'activité d'une organisation.
### Pourquoi la ligne PKI est tranchée : **non**
Elle disait « à trancher », en notant qu'*une AC intermédiaire signée par l'hôte est
possible*. Elle l'est techniquement — et c'est précisément ce qu'il ne faut pas faire.
Une intermédiaire signée par l'hôte lui donne le pouvoir d'**émettre des certificats
valides pour les noms du locataire**. Il peut alors se présenter comme n'importe lequel de
ses services, y compris devant les propres machines du locataire, qui les accepteront sans
sourciller — c'est exactement ce que la chaîne de confiance leur demande de faire.
C'est le même pouvoir que l'annuaire, sous une forme **moins visible** : rien n'apparaît
dans la configuration du locataire, aucune trace côté locataire, et la vérification passe.
**Une PKI par écosystème, jamais dérivée de l'hôte.** C'est déjà ce qui tourne ; la ligne
ne fait que cesser de laisser la porte entrouverte.
### Le revers : le site n'a pas d'observabilité du tout
Mesure du 2026-09-10 : le site ne porte **ni `client_journal` ni `client_metrique`**, et
aucun `loki` ni `prometheus`. Ses sept machines n'expédient rien nulle part — l'hébergeur
ne peut pas lire ses propres journaux.
C'est la conséquence directe de la frontière : à refuser de voir ceux des locataires, il
s'est privé des siens. Le remède n'est pas d'assouplir la règle, c'est de lui donner **sa
propre pile** — un `loki` et un `prometheus` du site, pour les machines du site, sans
aucun lien avec ceux d'un tenant.
## L'insémination — le premier lien, et le seul que le SITE ait le droit d'avoir
Entre tenants, le zéro-confiance est le défaut : la frontière refuse tout ce qui n'est pas
déclaré, et c'est cette règle qui rend l'hébergement mutualisé défendable. **La parenté est
la seule exception, et elle est asymétrique** : un écosystème neuf ne peut pas s'amorcer
lui-même. Quelqu'un doit poser sa première machine et lui donner de quoi continuer.
Ce geste s'appelle l'**insémination** :
```
le SITE matérialise VNets, VM, routes, flux — le terrain
le SITE amorce ops-01 la première machine, et elle seule
le TENANT achève ses autres machines, ses rôles, ses intégrations
```
**Le partage d'intelligence qui le rend possible.** Les deux runners portent le même
moteur ; ils n'en consomment pas la même face :
| | ce qu'il lit du moteur | ce qu'il en fait |
|---|---|---|
| runner du **SITE** | `roles/*/meta/flux.yml` — la face **réseau** des rôles | SDN, pare-feu Proxmox, frontière OPNsense |
| runner du **TENANT** | `tasks/`, `templates/`, `meta/integration.yml` — la face **machine** | configure ses services |
C'est ce partage qui permet au SITE de poser les bonnes règles pour un tenant **sans jamais
lire sa configuration ni détenir sa voûte**. Il sait de quoi les rôles ont besoin sur le
fil ; il ignore ce qu'ils font sur la machine.
**Ce que le lien doit être, pour ne pas devenir un pouvoir permanent :**
- **étroit** — vers le seul `ops-01` du tenant, jamais vers ses autres machines ;
- **déclaré** — dans `meta/flux.yml`, comme tout le reste, pour que la frontière et
`nftables` le connaissent au lieu de le subir ;
- **borné par un acte**, et cet acte appartient à un humain (voir ci-dessous).
> **Ce qui existe déjà sans être déclaré, au 2026-08-28.** `make creer-vm` exige
> `_instance-requise` : pour matérialiser les VM d'un tenant, le runner du SITE doit
> basculer son symlink `instance` sur le dépôt de ce tenant — alors que son plan pose
> `serveur_ops_instance: ""` et que la doctrine dit qu'un SITE n'a pas de plan monté par ce
> lien. Le couplage est donc **déjà là**, et rien ne borne sur quels tenants il porte ni
> jusqu'à quand. Le déclarer, c'est le rendre limitable ; le taire ne l'a jamais empêché.
## Ce que l'émancipation exige — et de qui
Basculer une déclaration ne suffit pas. Mais l'erreur symétrique est plus tentante encore :
faire de l'émancipation un **automatisme**, que la machine déclencherait dès qu'elle
constate que le tenant se reproduit sans son parent.
**L'émancipation exige toujours la gouverne d'un humain.** Le fruit de l'émancipation est
livré à quelqu'un — un client qui reprend ses clés, sa forge, son écosystème. Sans
quelqu'un pour le recevoir, il n'y a pas de livraison : seulement un lien qui tombe. *Une
émancipation qui se déclencherait toute seule ne serait pas une émancipation, ce serait une
expulsion.*
C'est déjà la forme des gestes lourds de ce dépôt : `raser` exige `CONFIRMER=true` et le
nom de l'instance ; le mot de passe de la voûte se tape à l'exécution — « le seul objet que
la reproduction exige d'un humain ». **L'émancipation est le deuxième objet de cette
liste.**
D'où la répartition, qui ne se confond pas :
| | qui l'exécute |
|---|---|
| **le lien de filiation** — déclaré, étroit, visible au plan | le moteur |
| **le constat d'aptitude** — « ce tenant se reproduit sans son parent » | la machine — elle **instruit**, elle n'agit pas |
| **l'acte d'émancipation** — retirer le lien, remettre les clés | **l'humain**, garde `CONFIRMER=true` |
| **la preuve que le lien est coupé** | la machine, **après** l'acte |
La quatrième ligne est celle qu'on oublie, et c'est elle qui décide si les trois autres ont
servi. Sans elle, on croirait s'être émancipé en restant dépendant sans le savoir —
exactement le défaut que ce dépôt traque partout ailleurs : un vert sur un périmètre vide.
**Une émancipation non prouvée est une émancipation non faite.**
### L'instrument de la quatrième ligne — `make emancipation-prouver`
Il existe depuis le 2026-09-01, et il **coupe** au lieu de sonder.
Vérifier que le service local répond ne prouve rien : *tant que l'amont répond, une
fonction qui marche ne dit pas d'où vient l'octet.* L'instrument bloque donc l'amont —
table `nftables` dédiée, retirée par un bloc `always` quoi qu'il arrive — puis refait
marcher la chose.
**Le même essai rend les deux verdicts, et c'est ce qui le rend honnête :**
```
coupé, la fonction marche -> ÉMANCIPÉ, et c'est prouvé
coupé, la fonction casse -> PAS ÉMANCIPÉ, et la dépendance est prouvée RÉELLE
```
Le second n'est pas un échec de l'outil : c'est son **contrôle négatif**, rendu par la
même commande. Une preuve d'émancipation incapable de montrer la dépendance qu'elle mesure
ne prouverait rien le jour où elle passerait au vert.
Un témoin précède la coupure — *la fonction marchait-elle seulement avant ?* Sans lui, une
panne préexistante se lirait comme une dépendance.
```
make emancipation-prouver SERVICE=artefacts HOTE=forge-01 CONFIRMER=true
```
*Mesuré le jour de sa naissance : `obs-01` ne résout plus rien dès que le résolveur du
site est coupé — dépendance réelle ; `forge-01` installe des paquets le cache du site
coupé, listes vidées — émancipation prouvée sur ce service.*
## Forme attendue d'une déclaration
Tout service mutualisable doit se déclarer de la même façon, pour qu'un exploitant lise
l'état de son écosystème d'un coup d'œil plutôt qu'en fouillant chaque rôle :
```yaml
<service>_externe: true # je l'emprunte
<service>_amont: "https://…" # à qui
```
`false` — ou l'absence du couple — signifie *chez moi*. Le rôle **refuse** de démarrer si
le drapeau est levé sans que l'amont soit nommé : emprunter sans dire à qui, c'est ne rien
déclarer du tout.
## Qui est le parent ? — tranché le 2026-08-31 (D-82)
Le dilemme est resté ouvert trois jours. Les faits l'ont tranché plus que le raisonnement :
**personne n'est le parent, et la forge du SITE est l'autorité.**
Trois issues avaient été posées. C'est la deuxième qui l'emporte, non parce qu'elle était
la plus élégante, mais parce que les deux autres avaient cessé d'être disponibles :
- **patient 0 redevient la source** — impossible sans le rendre atteignable depuis *tout*
site, alors qu'il vit dans la fabric d'un seul, en locataire de son propre descendant ;
- **le SITE est la source** ✔ — c'est ce que D-81 avait déjà fait, et que le reste du dépôt
a suivi sans qu'on le déclare : `serveur_forge_site`, puis `serveur_cache_site`, puis
`serveur_resolveur_site`. Trois services prêtés, un seul patron ;
- **une famille de pairs** — reste vraie *pour la redondance*. C'est ce que patient 0
gardait : un miroir du génome, comme chaque écosystème. Il perdait le rang, pas la place.
> **Il a perdu la place aussi : ses machines n'existent plus (2026-09-06).** La famille du
> génome compte donc **deux** copies vivantes au lieu de trois — `eregion` et la forge du
> site. Or c'est précisément le raisonnement ci-dessus qui portait tout : *on n'échappe pas
> à la boucle par la ruse, mais par le nombre.* Le nombre a baissé, et le SPOF que patient 0
> devait absorber (`eregion`, hors flotte) est toujours là.
**Ce que ça ne règle pas.** Le point unique de défaillance que patient 0 devait éliminer —
`eregion`, hors flotte — n'a pas disparu : il alimente maintenant l'autorité. La dette a
changé de propriétaire, pas de nature. Elle appartient au SITE.
## État — revu le 2026-09-06
Seule la forge porte cette déclaration complète (`serveur_ops`). Les autres services
mutualisés le sont par des mécanismes antérieurs, cohérents mais non uniformes. Les
aligner sur la forme ci-dessus reste à faire.
Des trois manques que cette section listait au 2026-08-28, **deux sont comblés** :
| Manque du 2026-08-28 | Aujourd'hui |
|---|---|
| l'**instrument de preuve** de la dernière ligne — « le lien est coupé » — n'existe pas | **`make emancipation-prouver`**, depuis le 2026-09-01 (§ ci-dessus). Il *coupe* au lieu de sonder, et rend les deux verdicts. |
| la séparation des voûtes est **organisationnelle, pas cryptographique** — un seul mot de passe ouvre celle du site et celles des tenants | **Séparé le 2026-08-28 même** : une voûte, **une clé** (`~/.config/setops-vault-<dépôt>`, `scripts/voutes.py`). L'ancien mot de passe unique n'ouvre plus rien. La séparation précédait nécessairement la distribution des clés aux runners : sinon, poser « la » clé sur le plus petit locataire lui donnait les secrets de l'hébergeur. |
| le **lien d'insémination** n'est pas déclaré | **Toujours vrai.** Il existe, par le symlink `instance` du runner du SITE, sans borne ni visibilité. C'est le manque qui reste. |
*(Le paragraphe sur les voûtes se contredisait avec `scripts/voutes.py` — daté du même jour —
et celui sur l'instrument avec la section qui le décrit, deux écrans plus haut. Un document
qui se contredit lui-même à cette distance n'est plus lu comme une référence.)*