plancher : nommer ce qui est hors de l'ecosysteme
Some checks are pending
verifier / verifier (push) Waiting to run

Le plancher /etc/hosts ne savait nommer que ce que l'ecosysteme contient. Or un
ecosysteme enfant doit atteindre la forge dont il descend, et ce nom-la resout
vers l'adresse publique depuis l'overlay -- pas vers l'adresse du LAN, que la
frontiere bloque a juste titre.

hosts_statiques_externes accueille ces declarations. Le fichier etant regenere
integralement a chaque passage, une entree posee a la main ne tiendrait pas.

La poignee TCP disait "ouvert" sur l'adresse bloquee alors qu'aucune donnee ne
passait -- seule la livraison compte.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Daniel Allaire 2026-08-23 10:09:08 -04:00
parent 7d71f37528
commit 8e125b71cb
3 changed files with 110 additions and 0 deletions

View file

@ -1,5 +1,84 @@
# CHANGELOG — Set-OPS
## 2026-08-23 — Les miroirs du génome, et un nom qui ne résout pas pareil selon d'où on le demande
La copie du génome sur patient 0 était **figée au jour du poussage**. Elle ne l'est plus :
les deux dépôts publics sont désormais des **miroirs** Forgejo, resynchronisés toutes les
huit heures depuis la forge amont. L'étiquette signée `v2026.08.21` traverse le miroir
intacte — vérifié `SSH SIGNATURE` présente sur le dépôt reconstruit.
Les trois dépôts privés attendent un jeton amont en **lecture seule**. Le premier jeton
fourni pouvait ÉCRIRE : un `PATCH` sur le dépôt parent a été accepté (HTTP 200), alors que
`administration`, `organisation` et `user` rendaient bien 403 — l'instrument était donc
fiable, et le verdict aussi. Un enfant qui peut réécrire son parent inverse le sens de la
filiation ; le jeton est refusé tant qu'il n'est pas ramené à `read:repository`.
### Le piège du jour : un nom, deux réponses
`forge.alliance-boreale.ca` ne résout pas pareil selon l'endroit d'où on le demande :
```
depuis le poste d'administration -> 192.168.14.66 (LAN de l'hébergeur)
depuis l'overlay de patient 0 -> 69.70.26.51 (adresse publique)
```
Et depuis patient 0, **c'est l'adresse publique qui marche** : le tenant a le droit de
sortir sur l'internet et pas d'entrer dans le LAN `192.168.x` de son hôte — le
default-deny entre les deux mondes, qui fait exactement son travail.
La mesure, prise depuis `forge-01` :
```
ping 100 octets -> 192.168.14.66 BLOQUÉ (même 100 octets : pas un problème de MTU)
ping / https -> internet passe, toutes tailles
curl https://69.70.26.51/api/v1/version -> {"version":"8.0.3"} la vraie forge amont
la forge locale de patient 0 -> {"version":"16.0.2"} bien distincte
8 essais sur 8 -> HTTP 200 en ~6,7 ms (retour en épingle local, stable)
```
Et une fois de plus, **la poignée TCP a menti** : `192.168.14.66:443` répondait « ouvert »
alors qu'aucune donnée ne passait. Seule la livraison compte.
### `hosts_statiques_externes` — nommer ce qui est hors de l'écosystème
Le plancher `/etc/hosts` ne savait nommer que ce que l'écosystème contient. Or un
écosystème doit atteindre des noms du dehors, à commencer par la forge dont il descend.
Poser l'entrée à la main ne tiendrait pas : le fichier est **regénéré intégralement** à
chaque passage du rôle. La déclaration vit donc au plan :
```yaml
hosts_statiques_externes:
- ip: "69.70.26.51"
noms: ["forge.alliance-boreale.ca"]
pourquoi: "forge amont du génome — l'adresse interne est bloquée par la frontière"
```
On épingle l'adresse dont on a **prouvé** qu'elle livre. Si un enregistrement à horizon
partagé rendait un jour l'adresse interne, le miroir s'arrêterait sans bruit — et une
copie du génome qui vieillit en silence est précisément le défaut que ce dépôt combat.
### Ce que la conversion a coûté, et pourquoi elle est gardée
Forgejo ne convertit pas un dépôt ordinaire en miroir : il faut le détruire et le
recréer. Trois gardes encadrent donc l'opération, et deux ont mordu pour de bon :
- **l'amont contient-il déjà le local ?** La première version exigeait l'égalité et a
refusé la conversion pour un simple commit de retard. Le bon critère est l'inclusion —
`merge-base --is-ancestor`, qui distingue « en retard » (sans risque) de « en avance »
(destruction de travail).
- **un répertoire résiduel est-il vraiment vide ?** Une migration avortée avait laissé
une coquille de `set-ops-public.git` — 0 référence — qui bloquait la suivante avec
« Files already exist ». On ne la retire qu'après avoir compté les références.
Leçon d'outillage, aussi : `no_log: true` avait masqué l'erreur `409` **et** l'avait
rendue indéchiffrable. La tâche qui porte le mot de passe reste muette, mais un `debug`
séparé rend maintenant le statut et le message.
### À suivre
Les hôtes de patient 0 résolvent par Quad9 et **n'interrogent pas leur propre serveur
`infra-dns-01`**. L'écosystème fait tourner un résolveur que personne n'utilise.
## 2026-08-23 — Patient 0 porte le génome : la boucle est fermée
Les cinq dépôts qui fabriquent la lignée vivent désormais sur la forge de patient 0 —

View file

@ -9,3 +9,25 @@ hosts_statiques_domaine: "{{ domaine_interne | default('') }}"
# du DNS. Best-effort : ignoré si le plan n'est pas disponible (ex. préparation du template).
hosts_statiques_publier_expositions: true
hosts_statiques_expositions: []
# --- Hôtes EXTERNES à l'écosystème -------------------------------------------
#
# Le plancher ne savait nommer que ce que l'écosystème contient. Or un écosystème a
# besoin d'atteindre des noms du dehors — à commencer par la forge dont il descend.
#
# LE PIÈGE, MESURÉ LE 2026-08-23. `forge.alliance-boreale.ca` résout vers 192.168.14.66
# depuis le poste d'administration, et vers 69.70.26.51 — l'adresse PUBLIQUE — depuis
# l'overlay de patient 0, qui ne sait pas l'atteindre de l'intérieur (pas de retour en
# épingle). Le clone initial du miroir avait pourtant réussi : la résolution n'est pas
# stable, elle dépend du résolveur interrogé. Un miroir qui se synchronise toutes les
# huit heures serait tombé dessus tôt ou tard, et le silence aurait duré.
#
# Poser l'entrée à la main dans /etc/hosts ne tiendrait pas : ce fichier est REGÉNÉRÉ
# intégralement à chaque passage du rôle. La déclaration doit donc vivre au plan.
#
# Format :
# hosts_statiques_externes:
# - ip: "192.168.14.66"
# noms: ["forge.alliance-boreale.ca"]
# pourquoi: "forge amont — l'adresse publique est injoignable de l'intérieur"
hosts_statiques_externes: []

View file

@ -26,3 +26,12 @@ ff02::2 ip6-allrouters
{% endif %}
{% endfor %}
{% endif %}
{% if hosts_statiques_externes %}
# --- Hôtes EXTERNES à l'écosystème (déclarés au plan) ---
# Un nom qui existe hors de l'écosystème peut résoudre vers une adresse PUBLIQUE que
# l'écosystème ne sait pas atteindre depuis l'intérieur. Ces entrées le corrigent.
{% for ext in hosts_statiques_externes %}
{{ "%-15s" | format(ext.ip) }} {{ ext.noms | join(' ') }}{{ (' # ' ~ ext.pourquoi) if ext.pourquoi is defined else '' }}
{% endfor %}
{% endif %}