site : le runner travaille, et le genome remonte chez lui
`serveur_ops` et `serveur_ops_site` sont deployes sur `site-ops-01` : le moteur,
les plans des trois tenants, les collections hors ligne, et la voute de
l'underlay deposee CHIFFREE. Le site a son runner.
DEUX FORMES DE RUNNER, ET UNE SEULE ETAIT PREVUE.
`serveur_ops` exigeait un depot `role: instance` et un `serveur_ops_instance` —
la forme d'un runner de TENANT, qui pilote un ecosysteme, monte son plan par le
lien `instance`, detient sa voute.
Un runner de SITE ne pilote rien : il MATERIALISE le terrain que d'autres
occuperont, et son inventaire est dynamique. Son plan declarait donc ses depots
de tenants en `role: tenant`, a dessein et avec ses raisons ecrites — et le role
le refusait au nom d'une exigence qui ne le concernait pas. Il exige desormais
que le runner pilote QUELQUE CHOSE, sans prescrire quoi.
Et il posait le lien quand meme : `serveur_ops_instance: ""` donnait
`instance -> /opt/setops/`, un repertoire qui n'est l'ecosysteme de personne. Un
lien qui existe et ne designe rien est pire qu'un lien absent : tout ce qui le
suit croit avoir trouve une instance.
LE CERTIFICAT COUVRE LES NOMS DU SERVICE.
`client_pki` ne mettait dans ses SAN que le FQDN, le nom court et l'IP. Le
clonage du genome a bute dessus : « certificate subject name
(site-forge-01.genese.internal) does not match target hostname
'forge.genese.internal' ». Le nom declare `expose:` etait publie partout —
plancher, zone DNS — et couvert nulle part.
Les expositions que cet hote SERT REELLEMENT entrent maintenant dans le
certificat. Un certificat qui revendiquerait le nom d'un service rendu ailleurs
serait une usurpation, pas une commodite.
`make genome-pousser` — LE RUNNER POUSSE, LE POSTE NE ROUTE PAS.
La forge du genome vit DANS le site, sur un reseau que le poste de l'exploitant
ne route pas. On ne perce pas un chemin pour lui : on lui retire le role. Le
poste emballe les commits dans un `git bundle` — un fichier, verifiable, qui ne
demande aucun reseau — et le runner verifie, avance EN AVANCE RAPIDE SEULEMENT,
puis pousse avec SA cle, autorisee en ecriture sur ces depots-la seulement.
Ce qui est pousse se DERIVE de `serveur_ops_depots`. `DEPOT=<nom>` en cible un
seul.
Trois pieges rencontres, tous inscrits dans le code : l'outil ne peut pas etre
supposé present sur le runner (il ne recevrait cette version qu'APRES la poussee
qu'elle sert a faire — le controleur le porte) ; la branche n'est pas toujours
`main` (deux depots vivent sur `master`, et le plan le declarait deja) ; le
dossier des depots freres contient un espace, d'ou `argv` et non `cmd`.
Le juge n'est pas le journal du playbook mais LA FORGE : on demande a son API ce
qu'elle porte, et on refuse si ca differe de ce que porte le runner. Six depots
verifies.
Pourquoi ca comptait : la forge du site etait restee quatre commits derriere le
poste, sans que rien ne le signale — dont le correctif qui desarme le pare-feu
Proxmox. Un ecosysteme qui se reproduit depuis une forge en retard reproduit ses
defauts.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 08:52:37 -04:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
"""Porter des commits jusqu'a une forge qu'on ne route pas — par colis, pas par reseau.
|
|
|
|
|
|
|
|
|
|
POURQUOI CE DETOUR, ET POURQUOI IL EST BON.
|
|
|
|
|
|
|
|
|
|
La forge du genome vit DANS le site, sur un reseau que le poste de l'exploitant ne route
|
|
|
|
|
pas — et c'est voulu : le poste n'a de patte que sur l'administration, la frontiere police
|
|
|
|
|
le reste. Il ne peut donc pas pousser lui-meme.
|
|
|
|
|
|
|
|
|
|
On ne perce pas un chemin pour lui. Le RUNNER existe exactement pour ca : il est dans le
|
|
|
|
|
site, il porte le genome, il a une identite. On lui PORTE les commits dans un `git bundle`
|
|
|
|
|
— un fichier, verifiable, qui ne demande aucun reseau — et c'est LUI qui pousse.
|
|
|
|
|
|
|
|
|
|
Mesure du 2026-08-26 : la forge du site etait restee quatre commits derriere le poste, sans
|
|
|
|
|
que rien ne le signale. Le runner clonait donc un moteur perime — dont, ce jour-la, le
|
|
|
|
|
correctif qui desarme le pare-feu Proxmox. Un ecosysteme qui se reproduit depuis une forge
|
|
|
|
|
en retard reproduit ses defauts.
|
|
|
|
|
|
|
|
|
|
DEUX GESTES, DEUX COTES :
|
|
|
|
|
|
|
|
|
|
genome_colis.py emballer <depot> <colis>
|
|
|
|
|
Sur le CONTROLEUR. Fabrique le colis a partir de `main`.
|
|
|
|
|
|
|
|
|
|
genome_colis.py recevoir <depot> <colis> <pushurl> [--cle <fichier>]
|
|
|
|
|
Sur le RUNNER. Verifie le colis, avance `main` EN AVANCE RAPIDE SEULEMENT,
|
|
|
|
|
puis pousse. Rend du JSON : {avant, apres, pousse}.
|
|
|
|
|
|
genome : la forge du SITE fait autorite (D-81), et `make genome-etat` le verifie
Decision de l'exploitant : la forge du SITE fait autorite pour le genome. Toute
autre copie — y compris celle d'ou le moteur a ete pousse jusqu'ici — est un
MIROIR.
Un ecosysteme se reproduit depuis la forge de son site : c'est de la qu'il clone
son moteur, ses plans, ses modeles. Si l'autorite est ailleurs, cette forge
devient un cache qu'on croit a jour — et le 2026-08-26 elle etait quatre commits
en arriere sans que rien ne le signale, dont le correctif qui desarme le pare-feu
Proxmox.
UNE AUTORITE QU'ON NE VERIFIE PAS EST UNE AUTORITE QU'ON SUPPOSE.
`make genome-etat` confronte, depot par depot, ce que le poste porte a ce que la
forge porte. Il REFUSE en cas d'ecart plutot que de le signaler : un ecart connu
et tolere redevient un ecart oublie, et la commande qui le corrige tient en trois
mots. Il dit aussi quand la copie locale n'est pas propre — des commits pas
encore faits sont une autre forme de retard.
Mesure au passage, et traitee plutot qu'ignoree : le premier contact avec la
forge echoue une fois sur six — poignee TLS expiree, puis cinq reponses de suite.
Ce n'est pas le chemin, qui est prouve ; c'est l'acceptation TLS apres un temps
d'inactivite. Les deux cibles reessaient.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 09:17:24 -04:00
|
|
|
genome_colis.py etat <depot>
|
|
|
|
|
Des deux cotes. Ce que cette copie porte, pour le confronter a la forge.
|
|
|
|
|
|
site : le runner travaille, et le genome remonte chez lui
`serveur_ops` et `serveur_ops_site` sont deployes sur `site-ops-01` : le moteur,
les plans des trois tenants, les collections hors ligne, et la voute de
l'underlay deposee CHIFFREE. Le site a son runner.
DEUX FORMES DE RUNNER, ET UNE SEULE ETAIT PREVUE.
`serveur_ops` exigeait un depot `role: instance` et un `serveur_ops_instance` —
la forme d'un runner de TENANT, qui pilote un ecosysteme, monte son plan par le
lien `instance`, detient sa voute.
Un runner de SITE ne pilote rien : il MATERIALISE le terrain que d'autres
occuperont, et son inventaire est dynamique. Son plan declarait donc ses depots
de tenants en `role: tenant`, a dessein et avec ses raisons ecrites — et le role
le refusait au nom d'une exigence qui ne le concernait pas. Il exige desormais
que le runner pilote QUELQUE CHOSE, sans prescrire quoi.
Et il posait le lien quand meme : `serveur_ops_instance: ""` donnait
`instance -> /opt/setops/`, un repertoire qui n'est l'ecosysteme de personne. Un
lien qui existe et ne designe rien est pire qu'un lien absent : tout ce qui le
suit croit avoir trouve une instance.
LE CERTIFICAT COUVRE LES NOMS DU SERVICE.
`client_pki` ne mettait dans ses SAN que le FQDN, le nom court et l'IP. Le
clonage du genome a bute dessus : « certificate subject name
(site-forge-01.genese.internal) does not match target hostname
'forge.genese.internal' ». Le nom declare `expose:` etait publie partout —
plancher, zone DNS — et couvert nulle part.
Les expositions que cet hote SERT REELLEMENT entrent maintenant dans le
certificat. Un certificat qui revendiquerait le nom d'un service rendu ailleurs
serait une usurpation, pas une commodite.
`make genome-pousser` — LE RUNNER POUSSE, LE POSTE NE ROUTE PAS.
La forge du genome vit DANS le site, sur un reseau que le poste de l'exploitant
ne route pas. On ne perce pas un chemin pour lui : on lui retire le role. Le
poste emballe les commits dans un `git bundle` — un fichier, verifiable, qui ne
demande aucun reseau — et le runner verifie, avance EN AVANCE RAPIDE SEULEMENT,
puis pousse avec SA cle, autorisee en ecriture sur ces depots-la seulement.
Ce qui est pousse se DERIVE de `serveur_ops_depots`. `DEPOT=<nom>` en cible un
seul.
Trois pieges rencontres, tous inscrits dans le code : l'outil ne peut pas etre
supposé present sur le runner (il ne recevrait cette version qu'APRES la poussee
qu'elle sert a faire — le controleur le porte) ; la branche n'est pas toujours
`main` (deux depots vivent sur `master`, et le plan le declarait deja) ; le
dossier des depots freres contient un espace, d'ou `argv` et non `cmd`.
Le juge n'est pas le journal du playbook mais LA FORGE : on demande a son API ce
qu'elle porte, et on refuse si ca differe de ce que porte le runner. Six depots
verifies.
Pourquoi ca comptait : la forge du site etait restee quatre commits derriere le
poste, sans que rien ne le signale — dont le correctif qui desarme le pare-feu
Proxmox. Un ecosysteme qui se reproduit depuis une forge en retard reproduit ses
defauts.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 08:52:37 -04:00
|
|
|
`--ff-only` est le garde-fou. Si les deux histoires ont diverge, on s'arrete plutot que de
|
|
|
|
|
fabriquer une fusion que personne n'a decidee — et le message de git nomme alors exactement
|
|
|
|
|
ce qu'il faut reconcilier.
|
|
|
|
|
|
|
|
|
|
CE SCRIPT VIT DANS LE MOTEUR, donc le runner en a une copie des qu'il l'a clone : on ne
|
|
|
|
|
depose rien de special sur la machine pour qu'elle sache recevoir.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
|
import json
|
|
|
|
|
import subprocess
|
|
|
|
|
import sys
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _git(depot: Path, *args: str, env: dict | None = None) -> str:
|
|
|
|
|
"""Un git, dans un depot, qui parle clair quand il echoue."""
|
|
|
|
|
r = subprocess.run(["git", "-C", str(depot), *args],
|
|
|
|
|
capture_output=True, text=True, env=env)
|
|
|
|
|
if r.returncode != 0:
|
|
|
|
|
sortie = (r.stderr or r.stdout or "").strip()
|
|
|
|
|
raise SystemExit(f"git {' '.join(args)} a echoue dans {depot} :\n {sortie}")
|
|
|
|
|
return (r.stdout or "").strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def emballer(depot: Path, colis: Path, branche: str) -> int:
|
|
|
|
|
if not (depot / ".git").exists():
|
|
|
|
|
raise SystemExit(f"{depot} n'est pas un depot git — rien a emballer.")
|
|
|
|
|
colis.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
_git(depot, "bundle", "create", str(colis), branche)
|
|
|
|
|
print(json.dumps({"depot": str(depot), "colis": str(colis),
|
|
|
|
|
"tete": _git(depot, "log", "-1", "--format=%H")}))
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
|
genome : la forge du SITE fait autorite (D-81), et `make genome-etat` le verifie
Decision de l'exploitant : la forge du SITE fait autorite pour le genome. Toute
autre copie — y compris celle d'ou le moteur a ete pousse jusqu'ici — est un
MIROIR.
Un ecosysteme se reproduit depuis la forge de son site : c'est de la qu'il clone
son moteur, ses plans, ses modeles. Si l'autorite est ailleurs, cette forge
devient un cache qu'on croit a jour — et le 2026-08-26 elle etait quatre commits
en arriere sans que rien ne le signale, dont le correctif qui desarme le pare-feu
Proxmox.
UNE AUTORITE QU'ON NE VERIFIE PAS EST UNE AUTORITE QU'ON SUPPOSE.
`make genome-etat` confronte, depot par depot, ce que le poste porte a ce que la
forge porte. Il REFUSE en cas d'ecart plutot que de le signaler : un ecart connu
et tolere redevient un ecart oublie, et la commande qui le corrige tient en trois
mots. Il dit aussi quand la copie locale n'est pas propre — des commits pas
encore faits sont une autre forme de retard.
Mesure au passage, et traitee plutot qu'ignoree : le premier contact avec la
forge echoue une fois sur six — poignee TLS expiree, puis cinq reponses de suite.
Ce n'est pas le chemin, qui est prouve ; c'est l'acceptation TLS apres un temps
d'inactivite. Les deux cibles reessaient.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 09:17:24 -04:00
|
|
|
def etat(depot: Path, branche: str) -> int:
|
|
|
|
|
"""Ce que cette copie porte — pour le confronter a ce que la forge porte.
|
|
|
|
|
|
|
|
|
|
LA DERIVE NE SE VOIT PAS TOUTE SEULE. Le 2026-08-26, la forge du site etait restee
|
|
|
|
|
quatre commits derriere le poste sans que rien ne le signale ; le runner clonait donc
|
|
|
|
|
un moteur perime, dont le correctif qui desarme le pare-feu Proxmox. On rend donc
|
|
|
|
|
l'ecart LISIBLE, plutot que d'esperer s'en souvenir.
|
|
|
|
|
"""
|
|
|
|
|
if not (depot / ".git").exists():
|
|
|
|
|
raise SystemExit(f"{depot} n'est pas un depot git.")
|
|
|
|
|
print(json.dumps({
|
|
|
|
|
"tete": _git(depot, "log", "-1", "--format=%H", branche),
|
|
|
|
|
"courte": _git(depot, "log", "-1", "--format=%h", branche),
|
|
|
|
|
"sujet": _git(depot, "log", "-1", "--format=%s", branche)[:80],
|
|
|
|
|
"propre": _git(depot, "status", "--porcelain") == "",
|
|
|
|
|
}))
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
|
site : le runner travaille, et le genome remonte chez lui
`serveur_ops` et `serveur_ops_site` sont deployes sur `site-ops-01` : le moteur,
les plans des trois tenants, les collections hors ligne, et la voute de
l'underlay deposee CHIFFREE. Le site a son runner.
DEUX FORMES DE RUNNER, ET UNE SEULE ETAIT PREVUE.
`serveur_ops` exigeait un depot `role: instance` et un `serveur_ops_instance` —
la forme d'un runner de TENANT, qui pilote un ecosysteme, monte son plan par le
lien `instance`, detient sa voute.
Un runner de SITE ne pilote rien : il MATERIALISE le terrain que d'autres
occuperont, et son inventaire est dynamique. Son plan declarait donc ses depots
de tenants en `role: tenant`, a dessein et avec ses raisons ecrites — et le role
le refusait au nom d'une exigence qui ne le concernait pas. Il exige desormais
que le runner pilote QUELQUE CHOSE, sans prescrire quoi.
Et il posait le lien quand meme : `serveur_ops_instance: ""` donnait
`instance -> /opt/setops/`, un repertoire qui n'est l'ecosysteme de personne. Un
lien qui existe et ne designe rien est pire qu'un lien absent : tout ce qui le
suit croit avoir trouve une instance.
LE CERTIFICAT COUVRE LES NOMS DU SERVICE.
`client_pki` ne mettait dans ses SAN que le FQDN, le nom court et l'IP. Le
clonage du genome a bute dessus : « certificate subject name
(site-forge-01.genese.internal) does not match target hostname
'forge.genese.internal' ». Le nom declare `expose:` etait publie partout —
plancher, zone DNS — et couvert nulle part.
Les expositions que cet hote SERT REELLEMENT entrent maintenant dans le
certificat. Un certificat qui revendiquerait le nom d'un service rendu ailleurs
serait une usurpation, pas une commodite.
`make genome-pousser` — LE RUNNER POUSSE, LE POSTE NE ROUTE PAS.
La forge du genome vit DANS le site, sur un reseau que le poste de l'exploitant
ne route pas. On ne perce pas un chemin pour lui : on lui retire le role. Le
poste emballe les commits dans un `git bundle` — un fichier, verifiable, qui ne
demande aucun reseau — et le runner verifie, avance EN AVANCE RAPIDE SEULEMENT,
puis pousse avec SA cle, autorisee en ecriture sur ces depots-la seulement.
Ce qui est pousse se DERIVE de `serveur_ops_depots`. `DEPOT=<nom>` en cible un
seul.
Trois pieges rencontres, tous inscrits dans le code : l'outil ne peut pas etre
supposé present sur le runner (il ne recevrait cette version qu'APRES la poussee
qu'elle sert a faire — le controleur le porte) ; la branche n'est pas toujours
`main` (deux depots vivent sur `master`, et le plan le declarait deja) ; le
dossier des depots freres contient un espace, d'ou `argv` et non `cmd`.
Le juge n'est pas le journal du playbook mais LA FORGE : on demande a son API ce
qu'elle porte, et on refuse si ca differe de ce que porte le runner. Six depots
verifies.
Pourquoi ca comptait : la forge du site etait restee quatre commits derriere le
poste, sans que rien ne le signale — dont le correctif qui desarme le pare-feu
Proxmox. Un ecosysteme qui se reproduit depuis une forge en retard reproduit ses
defauts.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 08:52:37 -04:00
|
|
|
def recevoir(depot: Path, colis: Path, pushurl: str, cle: str | None,
|
|
|
|
|
branche: str) -> int:
|
|
|
|
|
if not (depot / ".git").exists():
|
|
|
|
|
raise SystemExit(f"{depot} n'est pas un depot git — rien a recevoir.")
|
|
|
|
|
if not colis.is_file():
|
|
|
|
|
raise SystemExit(f"{colis} est introuvable : le colis n'est pas arrive.")
|
|
|
|
|
|
|
|
|
|
# UN COLIS DOIT ETRE VERIFIABLE AVANT D'ETRE CRU. `git bundle verify` exige d'etre
|
|
|
|
|
# lance DEPUIS un depot — il verifie que les prerequis du colis sont deja dans cette
|
|
|
|
|
# histoire-ci. Lance ailleurs : « la verification d'un colis requiert un depot ».
|
|
|
|
|
_git(depot, "bundle", "verify", str(colis))
|
|
|
|
|
|
|
|
|
|
avant = _git(depot, "log", "-1", "--format=%H")
|
|
|
|
|
_git(depot, "fetch", str(colis),
|
|
|
|
|
f"refs/heads/{branche}:refs/remotes/colis/{branche}")
|
|
|
|
|
_git(depot, "merge", "--ff-only", f"refs/remotes/colis/{branche}")
|
|
|
|
|
apres = _git(depot, "log", "-1", "--format=%H")
|
|
|
|
|
|
|
|
|
|
env = None
|
|
|
|
|
if cle:
|
|
|
|
|
import os
|
|
|
|
|
env = dict(os.environ)
|
|
|
|
|
env["GIT_SSH_COMMAND"] = (
|
|
|
|
|
f"ssh -o StrictHostKeyChecking=accept-new -o BatchMode=yes -i {cle}")
|
|
|
|
|
|
|
|
|
|
# `-c remote.origin.pushurl=…` plutot qu'un remote ecrit : la LECTURE reste anonyme
|
|
|
|
|
# (doctrine de `serveur_ops` — aucun justificatif a detenir pour se reconstruire),
|
|
|
|
|
# seule l'ECRITURE s'authentifie, et seulement le temps de cette commande.
|
|
|
|
|
r = subprocess.run(
|
|
|
|
|
["git", "-C", str(depot), "-c", f"remote.origin.pushurl={pushurl}",
|
|
|
|
|
"push", "origin", branche],
|
|
|
|
|
capture_output=True, text=True, env=env)
|
|
|
|
|
sortie = ((r.stdout or "") + (r.stderr or "")).strip()
|
|
|
|
|
if r.returncode != 0:
|
|
|
|
|
raise SystemExit(f"la poussee a echoue :\n {sortie}")
|
|
|
|
|
|
|
|
|
|
print(json.dumps({"avant": avant, "apres": apres,
|
|
|
|
|
"pousse": f"{branche} -> {branche}" in sortie, "sortie": sortie[:400]}))
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main(argv: list[str]) -> int:
|
|
|
|
|
p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
|
|
|
sous = p.add_subparsers(dest="geste", required=True)
|
|
|
|
|
|
|
|
|
|
e = sous.add_parser("emballer", help="fabriquer un colis depuis `main` (controleur)")
|
|
|
|
|
e.add_argument("depot")
|
|
|
|
|
e.add_argument("colis")
|
|
|
|
|
e.add_argument("--branche", default="main")
|
|
|
|
|
|
|
|
|
|
r = sous.add_parser("recevoir", help="verifier, avancer, pousser (runner)")
|
|
|
|
|
r.add_argument("depot")
|
|
|
|
|
r.add_argument("colis")
|
|
|
|
|
r.add_argument("pushurl")
|
|
|
|
|
r.add_argument("--cle", default=None, help="cle SSH du runner")
|
|
|
|
|
r.add_argument("--branche", default="main")
|
|
|
|
|
|
genome : la forge du SITE fait autorite (D-81), et `make genome-etat` le verifie
Decision de l'exploitant : la forge du SITE fait autorite pour le genome. Toute
autre copie — y compris celle d'ou le moteur a ete pousse jusqu'ici — est un
MIROIR.
Un ecosysteme se reproduit depuis la forge de son site : c'est de la qu'il clone
son moteur, ses plans, ses modeles. Si l'autorite est ailleurs, cette forge
devient un cache qu'on croit a jour — et le 2026-08-26 elle etait quatre commits
en arriere sans que rien ne le signale, dont le correctif qui desarme le pare-feu
Proxmox.
UNE AUTORITE QU'ON NE VERIFIE PAS EST UNE AUTORITE QU'ON SUPPOSE.
`make genome-etat` confronte, depot par depot, ce que le poste porte a ce que la
forge porte. Il REFUSE en cas d'ecart plutot que de le signaler : un ecart connu
et tolere redevient un ecart oublie, et la commande qui le corrige tient en trois
mots. Il dit aussi quand la copie locale n'est pas propre — des commits pas
encore faits sont une autre forme de retard.
Mesure au passage, et traitee plutot qu'ignoree : le premier contact avec la
forge echoue une fois sur six — poignee TLS expiree, puis cinq reponses de suite.
Ce n'est pas le chemin, qui est prouve ; c'est l'acceptation TLS apres un temps
d'inactivite. Les deux cibles reessaient.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 09:17:24 -04:00
|
|
|
d = sous.add_parser("etat", help="ce que cette copie porte")
|
|
|
|
|
d.add_argument("depot")
|
|
|
|
|
d.add_argument("--branche", default="main")
|
|
|
|
|
|
site : le runner travaille, et le genome remonte chez lui
`serveur_ops` et `serveur_ops_site` sont deployes sur `site-ops-01` : le moteur,
les plans des trois tenants, les collections hors ligne, et la voute de
l'underlay deposee CHIFFREE. Le site a son runner.
DEUX FORMES DE RUNNER, ET UNE SEULE ETAIT PREVUE.
`serveur_ops` exigeait un depot `role: instance` et un `serveur_ops_instance` —
la forme d'un runner de TENANT, qui pilote un ecosysteme, monte son plan par le
lien `instance`, detient sa voute.
Un runner de SITE ne pilote rien : il MATERIALISE le terrain que d'autres
occuperont, et son inventaire est dynamique. Son plan declarait donc ses depots
de tenants en `role: tenant`, a dessein et avec ses raisons ecrites — et le role
le refusait au nom d'une exigence qui ne le concernait pas. Il exige desormais
que le runner pilote QUELQUE CHOSE, sans prescrire quoi.
Et il posait le lien quand meme : `serveur_ops_instance: ""` donnait
`instance -> /opt/setops/`, un repertoire qui n'est l'ecosysteme de personne. Un
lien qui existe et ne designe rien est pire qu'un lien absent : tout ce qui le
suit croit avoir trouve une instance.
LE CERTIFICAT COUVRE LES NOMS DU SERVICE.
`client_pki` ne mettait dans ses SAN que le FQDN, le nom court et l'IP. Le
clonage du genome a bute dessus : « certificate subject name
(site-forge-01.genese.internal) does not match target hostname
'forge.genese.internal' ». Le nom declare `expose:` etait publie partout —
plancher, zone DNS — et couvert nulle part.
Les expositions que cet hote SERT REELLEMENT entrent maintenant dans le
certificat. Un certificat qui revendiquerait le nom d'un service rendu ailleurs
serait une usurpation, pas une commodite.
`make genome-pousser` — LE RUNNER POUSSE, LE POSTE NE ROUTE PAS.
La forge du genome vit DANS le site, sur un reseau que le poste de l'exploitant
ne route pas. On ne perce pas un chemin pour lui : on lui retire le role. Le
poste emballe les commits dans un `git bundle` — un fichier, verifiable, qui ne
demande aucun reseau — et le runner verifie, avance EN AVANCE RAPIDE SEULEMENT,
puis pousse avec SA cle, autorisee en ecriture sur ces depots-la seulement.
Ce qui est pousse se DERIVE de `serveur_ops_depots`. `DEPOT=<nom>` en cible un
seul.
Trois pieges rencontres, tous inscrits dans le code : l'outil ne peut pas etre
supposé present sur le runner (il ne recevrait cette version qu'APRES la poussee
qu'elle sert a faire — le controleur le porte) ; la branche n'est pas toujours
`main` (deux depots vivent sur `master`, et le plan le declarait deja) ; le
dossier des depots freres contient un espace, d'ou `argv` et non `cmd`.
Le juge n'est pas le journal du playbook mais LA FORGE : on demande a son API ce
qu'elle porte, et on refuse si ca differe de ce que porte le runner. Six depots
verifies.
Pourquoi ca comptait : la forge du site etait restee quatre commits derriere le
poste, sans que rien ne le signale — dont le correctif qui desarme le pare-feu
Proxmox. Un ecosysteme qui se reproduit depuis une forge en retard reproduit ses
defauts.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 08:52:37 -04:00
|
|
|
a = p.parse_args(argv)
|
genome : la forge du SITE fait autorite (D-81), et `make genome-etat` le verifie
Decision de l'exploitant : la forge du SITE fait autorite pour le genome. Toute
autre copie — y compris celle d'ou le moteur a ete pousse jusqu'ici — est un
MIROIR.
Un ecosysteme se reproduit depuis la forge de son site : c'est de la qu'il clone
son moteur, ses plans, ses modeles. Si l'autorite est ailleurs, cette forge
devient un cache qu'on croit a jour — et le 2026-08-26 elle etait quatre commits
en arriere sans que rien ne le signale, dont le correctif qui desarme le pare-feu
Proxmox.
UNE AUTORITE QU'ON NE VERIFIE PAS EST UNE AUTORITE QU'ON SUPPOSE.
`make genome-etat` confronte, depot par depot, ce que le poste porte a ce que la
forge porte. Il REFUSE en cas d'ecart plutot que de le signaler : un ecart connu
et tolere redevient un ecart oublie, et la commande qui le corrige tient en trois
mots. Il dit aussi quand la copie locale n'est pas propre — des commits pas
encore faits sont une autre forme de retard.
Mesure au passage, et traitee plutot qu'ignoree : le premier contact avec la
forge echoue une fois sur six — poignee TLS expiree, puis cinq reponses de suite.
Ce n'est pas le chemin, qui est prouve ; c'est l'acceptation TLS apres un temps
d'inactivite. Les deux cibles reessaient.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 09:17:24 -04:00
|
|
|
if a.geste == "etat":
|
|
|
|
|
return etat(Path(a.depot), a.branche)
|
site : le runner travaille, et le genome remonte chez lui
`serveur_ops` et `serveur_ops_site` sont deployes sur `site-ops-01` : le moteur,
les plans des trois tenants, les collections hors ligne, et la voute de
l'underlay deposee CHIFFREE. Le site a son runner.
DEUX FORMES DE RUNNER, ET UNE SEULE ETAIT PREVUE.
`serveur_ops` exigeait un depot `role: instance` et un `serveur_ops_instance` —
la forme d'un runner de TENANT, qui pilote un ecosysteme, monte son plan par le
lien `instance`, detient sa voute.
Un runner de SITE ne pilote rien : il MATERIALISE le terrain que d'autres
occuperont, et son inventaire est dynamique. Son plan declarait donc ses depots
de tenants en `role: tenant`, a dessein et avec ses raisons ecrites — et le role
le refusait au nom d'une exigence qui ne le concernait pas. Il exige desormais
que le runner pilote QUELQUE CHOSE, sans prescrire quoi.
Et il posait le lien quand meme : `serveur_ops_instance: ""` donnait
`instance -> /opt/setops/`, un repertoire qui n'est l'ecosysteme de personne. Un
lien qui existe et ne designe rien est pire qu'un lien absent : tout ce qui le
suit croit avoir trouve une instance.
LE CERTIFICAT COUVRE LES NOMS DU SERVICE.
`client_pki` ne mettait dans ses SAN que le FQDN, le nom court et l'IP. Le
clonage du genome a bute dessus : « certificate subject name
(site-forge-01.genese.internal) does not match target hostname
'forge.genese.internal' ». Le nom declare `expose:` etait publie partout —
plancher, zone DNS — et couvert nulle part.
Les expositions que cet hote SERT REELLEMENT entrent maintenant dans le
certificat. Un certificat qui revendiquerait le nom d'un service rendu ailleurs
serait une usurpation, pas une commodite.
`make genome-pousser` — LE RUNNER POUSSE, LE POSTE NE ROUTE PAS.
La forge du genome vit DANS le site, sur un reseau que le poste de l'exploitant
ne route pas. On ne perce pas un chemin pour lui : on lui retire le role. Le
poste emballe les commits dans un `git bundle` — un fichier, verifiable, qui ne
demande aucun reseau — et le runner verifie, avance EN AVANCE RAPIDE SEULEMENT,
puis pousse avec SA cle, autorisee en ecriture sur ces depots-la seulement.
Ce qui est pousse se DERIVE de `serveur_ops_depots`. `DEPOT=<nom>` en cible un
seul.
Trois pieges rencontres, tous inscrits dans le code : l'outil ne peut pas etre
supposé present sur le runner (il ne recevrait cette version qu'APRES la poussee
qu'elle sert a faire — le controleur le porte) ; la branche n'est pas toujours
`main` (deux depots vivent sur `master`, et le plan le declarait deja) ; le
dossier des depots freres contient un espace, d'ou `argv` et non `cmd`.
Le juge n'est pas le journal du playbook mais LA FORGE : on demande a son API ce
qu'elle porte, et on refuse si ca differe de ce que porte le runner. Six depots
verifies.
Pourquoi ca comptait : la forge du site etait restee quatre commits derriere le
poste, sans que rien ne le signale — dont le correctif qui desarme le pare-feu
Proxmox. Un ecosysteme qui se reproduit depuis une forge en retard reproduit ses
defauts.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 08:52:37 -04:00
|
|
|
if a.geste == "emballer":
|
|
|
|
|
return emballer(Path(a.depot), Path(a.colis), a.branche)
|
|
|
|
|
return recevoir(Path(a.depot), Path(a.colis), a.pushurl, a.cle, a.branche)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
raise SystemExit(main(sys.argv[1:]))
|