Set-OPS-Public/docs/audit/affirmations.md

435 lines
42 KiB
Markdown
Raw Normal View History

# Registre des affirmations — Set-OPS
> **Phase 1 de la mise en conformité prouvable.** Ce document est un **audit**, sans
> aucun correctif de code ni de documentation. Chaque affirmation publique vérifiable
> du dépôt est tracée vers une commande de preuve reproductible, ou marquée comme
> invérifiable.
>
> Règle d'arbitrage (rappel) : *le dépôt ne doit jamais affirmer plus que ce qu'il
> prouve.* Les traitements (corriger la doc / corriger le code / chantier séparé)
> relèvent de la **Phase 3** — ils ne sont **pas** appliqués ici.
- **Date de l'audit** : 2026-07-20
- **Instance montée pendant l'audit** : `instance -> ../OPS-Technolibre` (inventaire `principal/`)
- **Outillage** : ansible-core 2.18.1, ansible-lint 25.1.3, Python 3.13.3, node présent.
- **Périmètre** : surfaces publiques uniquement — `README.md`, `AGENTS.md`,
`CLAUDE.md`, `QUICKSTART.md`, `SOLUTION.md`, `docs/`, `wiki/`, aide du `Makefile`,
libellés du GUI. Aucune VM / Proxmox / réseau touché (mission hors production).
Légende du statut : ✅ prouvée · 🟡 partielle · ❌ fausse · ⚪ invérifiable localement.
---
## 1. README.md
| ID | Affirmation (source) | Type | Commande de preuve | Statut | Écart constaté |
|---|---|---|---|---|---|
| AFF-001 | « Set-OPS est un moteur Ansible générique … à partir d'un plan déclaratif » (README:3) | fonctionnelle | `python3 scripts/instancier.py generer && python3 scripts/instancier.py comparer` | ✅ | « DIFF VIDE : le plan reproduit exactement l'inventaire ». Générateur opérationnel. |
| AFF-002 | « Set-OPS s'exploite entièrement à la main — la doc, `make` et le GUI suffisent, sans aucune IA » (README:9 ; AGENTS principe 10) | doctrinale | Suivre `QUICKSTART.md` de bout en bout ; `make verifier` | 🟡 | Le principe tient au niveau outillage, mais **deux trous cassent le parcours sans IA** : `make verifier` échoue (AFF-006) et `QUICKSTART` étape 2 renvoie à un modèle absent (AFF-020/021). Un opérateur seul bute. |
| AFF-003 | Registres = source unique de vérité : `serveurs.yml`, `applications.yml`, `bases-donnees.yml`, `domaines.yml`, `nomenclature.yml` (README:15) | structurelle | `make inventaire-verifier` (serveurs/applications/bases/domaines) | ✅ | Les 4 validateurs renvoient « Registre valide ». Fichiers présents dans `instance/plan/`. |
| AFF-004 | « `instance/inventories/production/hosts.yml` est généré depuis le plan — ne pas l'éditer à la main » (README:34) | procédurale | `python3 scripts/instancier.py comparer` (diff-vide) | ✅ | Diff vide confirmé. (Nuance de chemin : l'instance courante utilise `principal/` — voir AFF-023.) |
| AFF-005 | « Afficher l'aide : `make` » (README:86) | procédurale | `make` (défaut = cible `aide`) | ✅ | `.DEFAULT_GOAL := aide` ; l'aide s'affiche. |
| AFF-006 | « Valider le dépôt : `make verifier` » (README:91) | fonctionnelle | `make verifier` | ❌ | `make lint` (1re dépendance de `verifier`) **échoue rc=2** : ansible-lint rapporte « Failed: 33 failure(s) » (`name[play]` sur le `site.yml` généré, `risky-shell-pipe` dans `site.yml`/`valider.yml`, `name[template]`). `make verifier` ne passe donc **pas** sur un dépôt propre. |
| AFF-007 | « Inspecter … `make hote-afficher HOTE=web-frontal-01` » (README:98) | procédurale | `make hote-afficher HOTE=web-frontal-01` | 🟡 | La cible existe et fonctionne, mais l'hôte d'exemple `web-frontal-01` n'existe pas dans l'instance courante → l'exemple littéral échoue (exemple générique, pas garanti par instance). |
| AFF-008 | « Chaque groupe opérationnel doit avoir son playbook homonyme dans `playbooks/groupes/` » (README:120 ; AGENTS:316) | structurelle | `python3 scripts/inventory_host.py --inventaire instance/inventories/principal/hosts.yml verifier-playbooks --dossier-playbooks playbooks/groupes` | ✅ | rc=0. Les 30 groupes opérationnels ont leur playbook. Seuls `hotes_actifs`, `hotes_planifies`, `modeles_vm` (groupes méta/état) n'en ont pas — exclus par conception. |
| AFF-009 | « Les dépendances causales entre groupes sont dans `docs/dependances-groupes.yml` » (README:128 ; AGENTS:413) | structurelle | `python3 scripts/inventory_host.py … verifier-dependances …` | ✅ | rc=0. Aucune entrée ne référence un rôle/groupe inexistant. |
| AFF-010 | « Le runbook du DNS interne initial est dans `docs/dns-interne.md` » (README:130) | structurelle | `test -f docs/dns-interne.md` | ✅ | Fichier présent. |
| AFF-011 | « La nomenclature … est dans `docs/nomenclature-vm.md` » (README:132) | structurelle | `test -f docs/nomenclature-vm.md` | ✅ | Fichier présent. |
| AFF-012 | « Le catalogue des services … `docs/catalogue-services.md` » (README:126) | structurelle | `test -f docs/catalogue-services.md` | ✅ | Fichier présent. |
| AFF-013 | « `make creer-vm HOTE=… ` VMID/IP/VLAN/passerelle lus dans le plan » (README:138) | fonctionnelle | `python3 scripts/inventory_host.py … parametres-proxmox --hote <h>` | 🟡 | La dérivation existe (sous-commande `parametres-proxmox`), mais l'exécution réelle touche Proxmox → non testable localement (hors production). Mécanique de dérivation seule vérifiable. |
| AFF-014 | « Les déploiements de groupes ciblent automatiquement les hôtes actifs seulement » (README:171) | fonctionnelle | lecture `Makefile:261` : `--limit '$(GROUPE):&$(GROUPE_HOTES_ACTIFS)'` | ✅ | Intersection `groupe ∩ hotes_actifs` codée dans la cible `appliquer`. |
| AFF-015 | « Set-OPS est un logiciel libre … AGPLv3 … texte intégral dans `LICENSE` » (README:177-186) | structurelle | `test -f LICENSE` | ✅ | `LICENSE` présent. |
---
## 2. QUICKSTART.md
| ID | Affirmation (source) | Type | Commande de preuve | Statut | Écart constaté |
|---|---|---|---|---|---|
| AFF-020 | « Les modèles sont dans `exemples/modeles/` … Choisis : `socle`, `presence-web`, `forge`, `identite`, `observabilite`, `integral` » (QUICKSTART:25-26) | structurelle | `ls exemples/modeles/` | ❌ | Le dossier public ne contient que `socle` (+ `README.md`). Les 5 autres modèles sont dans un dépôt **privé** `Set-OPS-modeles` (correctement expliqué dans `exemples/modeles/README.md`, mais **pas** dans QUICKSTART). |
| AFF-021 | « `cp -r exemples/modeles/presence-web ../mon-instance` » (QUICKSTART:29) | procédurale | exécuter la commande | ❌ | La commande **échoue** : `exemples/modeles/presence-web` n'existe pas. C'est la commande centrale de l'étape 2 ; un opérateur suivant QUICKSTART à la lettre est bloqué immédiatement. Seule valeur valide : `socle`. |
| AFF-022 | « `instance/inventories/production/group_vars/all.yml``domaine_interne` » (QUICKSTART:36) | structurelle | `test -f exemples/modeles/socle/inventories/production/group_vars/all.yml` | ✅ | Le modèle `socle` fournit bien ce fichier sous `production/`. |
| AFF-023 | « Tous tes secrets … `instance/inventories/lab/group_vars/all/vault.yml` » (QUICKSTART:48-49) | procédurale | `ls exemples/modeles/socle/inventories/` | ❌ | Le modèle `socle` n'a **pas** d'inventaire `lab/` (uniquement `production/`). Le chemin de voûte annoncé n'existe pas dans le modèle public ; incohérent avec AFF-022 (`production/`). |
| AFF-024 | « `make instancier` (diff) puis `make instancier-appliquer FORCE=1` — 1re génération » (QUICKSTART:68-70) | fonctionnelle | `make instancier` ; cible `instancier-appliquer` (Makefile:582) | ✅ | Cibles présentes ; `FORCE` mappé sur `--force`. Diff-vide prouve le mécanisme (AFF-001). |
| AFF-025 | Étape 5 : `make preparer-modele` / `verifier-modele` / `nettoyer-modele CONFIRMER=true` (QUICKSTART:59-62) | procédurale | cibles Makefile:632-643 | 🟡 | Cibles présentes et gardes vérifiées ; l'exécution réelle exige une VM Debian 13 + Proxmox → hors production, non testable ici. |
| AFF-026 | « Valider à tout moment : `make inventaire-verifier` / `make verifier` » (QUICKSTART:98-101) | fonctionnelle | `make inventaire-verifier` ; `make verifier` | 🟡 | `make verifier` échoue (AFF-006). `make inventaire-verifier` appelle `ansible-inventory --list` qui **exige le mot de passe Vault** (`ERROR! Attempting to decrypt but no vault secrets found` sans `ANSIBLE_VAULT_PASSWORD_FILE`) — prérequis non rappelé à cet endroit. |
---
## 3. AGENTS.md
| ID | Affirmation (source) | Type | Commande de preuve | Statut | Écart constaté |
|---|---|---|---|---|---|
| AFF-030 | « RÈGLE D'OR : `…/production/hosts.yml` est un artefact GÉNÉRÉ » (AGENTS:43) | procédurale | `python3 scripts/instancier.py comparer` | ✅ | Diff vide : l'inventaire est reproductible depuis le plan. |
| AFF-031 | « `make instancier-appliquer` refuse si le diff n'est pas vide (`FORCE=1` sinon) » (AGENTS:47) | fonctionnelle | lecture `scripts/instancier.py appliquer` + Makefile:582-583 | ✅ | `appliquer` accepte `--force` (mappé `FORCE`) ; garde diff cohérente avec le comparateur. |
| AFF-032 | « VMID / IP / VLAN / passerelle sont dérivés de la `fonction` via la nomenclature » (AGENTS:49) | fonctionnelle | `python3 scripts/serveurs.py verifier` (réconcilie dérivation ↔ inventaire) | ✅ | « Registre des serveurs valide et réconcilié avec l'inventaire. » Dérivation confirmée. |
| AFF-033 | « garde-fous : … `node --check` du JS du GUI (`scripts/verifier_gui.py`, dans `make inventaire-verifier`) » (AGENTS:50) | fonctionnelle | `python3 scripts/verifier_gui.py` | ✅ | « JS du GUI : syntaxe valide (node --check). » |
| AFF-034 | « Chaque rôle qui utilise `notify` doit contenir son handler dans le rôle » (AGENTS:182) | structurelle | script de recoupement notify↔handlers sur tous les rôles | ✅ | « OK : tout `notify` pointe vers un handler existant dans le même rôle. » |
| AFF-035 | « Tout `notify` doit pointer vers un handler existant » (AGENTS:193) | structurelle | idem AFF-034 | ✅ | Aucun `notify` orphelin. |
| AFF-036 | « Playbooks … testables avec `--check` autant que possible » (AGENTS:214, 427) | fonctionnelle | `ansible-playbook … --check` sur cibles vivantes | ⚪ | Non testable hors flotte réelle (mission hors production). Recensement des rôles non-check reporté (nécessite des hôtes). |
| AFF-037 | État SSH attendu : `PasswordAuthentication no`, `AuthenticationMethods publickey` (AGENTS:538-542) | fonctionnelle | lecture `roles/ssh_baseline/defaults` + `templates/10-setops.conf.j2` | ✅ | Défauts = `no`/`no`/`yes` ; le template code `AuthenticationMethods publickey`. **Le code suit AGENTS.md.** (Contredit CLAUDE.md — voir AFF-050.) |
| AFF-038 | « Ne pas maintenir en parallèle `playbooks/socle/` ou `playbooks/durcissement/` » (AGENTS:278) | structurelle | `ls playbooks/` | ✅ | Ces dossiers n'existent pas ; socle/durcissement = groupes `serveur_debian`/`serveur_durci`. |
| AFF-039 | « Les playbooks peuvent être classés par domaine : groupes/ maintenance/ monitoring/ networking/ proxmox/ modeles_vm/ web/ database/ identity/ backup/ applications/ » (AGENTS:257-270) | structurelle | `find playbooks -maxdepth 1 -type d` | 🟡 | Seuls `groupes/`, `maintenance/`, `modeles_vm/`, `proxmox/` existent. Formulé au conditionnel (« peuvent ») → aspirationnel, pas une fausse affirmation, mais liste plus large que la réalité. |
| AFF-040 | Section « Comportement attendu de **Codex** » (AGENTS:650) | doctrinale | lecture | 🟡 | La section ne nomme que Codex alors qu'elle vaut pour tout agent (Claude inclus). Correctif de titrage prévu en Phase 2 (item 3). |
---
## 4. CLAUDE.md
| ID | Affirmation (source) | Type | Commande de preuve | Statut | Écart constaté |
|---|---|---|---|---|---|
| AFF-050 | « Pendant la construction du template : `PasswordAuthentication yes` » (CLAUDE.md § SSH) | fonctionnelle | `roles/ssh_baseline/defaults/main.yml` | ❌ | Le code déploie `PasswordAuthentication no` **dès la construction** (`ssh_baseline_password_authentication: "no"`, template `AuthenticationMethods publickey`). CLAUDE.md contredit **et** le code **et** AGENTS.md (qui fait autorité). Cause racine : duplication de doctrine. Cible Phase 2 (item 1). |
| AFF-051 | « Claude Code doit lire et respecter `AGENTS.md` avant toute modification ; en cas de contradiction, suivre `AGENTS.md` » (CLAUDE.md § Instruction principale) | doctrinale | — | ✅ | Cohérent avec AGENTS.md ; pas de commande de preuve (déclaration de gouvernance). |
| AFF-052 | CLAUDE.md redéclare la doctrine de template, SSH, pare-feu, cloud-init, handlers, actions destructives (dupliquée d'AGENTS.md) | structurelle | `diff` conceptuel CLAUDE.md ↔ AGENTS.md | 🟡 | Large duplication → source des dérives (AFF-050). Réduction à un pointeur mince prévue Phase 2 (item 2). |
---
## 5. SOLUTION.md
| ID | Affirmation (source) | Type | Commande de preuve | Statut | Écart constaté |
|---|---|---|---|---|---|
| AFF-060 | « On garde une seule structure : `inventories/ playbooks/ roles/ docs/` » (SOLUTION:11-18) | structurelle | `ls` racine | 🟡 | Réel : aussi `scripts/`, `filter_plugins/`, `exemples/`, `wiki/`, `instance/`. Description obsolète (antérieure au plan/GUI). |
| AFF-061 | Commande template : `ansible-playbook … debian13_proxmox_preparer.yml --ask-pass --ask-become-pass` (SOLUTION:24-25) | procédurale | comparaison avec la doctrine SSH clé-only | 🟡 | `--ask-pass` (mot de passe SSH) contredit l'accès **par clé dès le départ** d'AGENTS.md (AFF-037). SOLUTION.md décrit un flux périmé (mot de passe SSH) ; le flux réel = clé via cloud-init. |
| AFF-062 | « Le pare-feu `nftables` est installé et préparé, mais pas activé par défaut dans le template » (SOLUTION:60 ; AGENTS:559 ; CLAUDE.md) | fonctionnelle | `grep enabled roles/nftables_baseline/defaults/main.yml` | ✅ | `nftables_baseline_enabled: false`. `preparer-modele` inclut le rôle et affiche « nftables est installé mais désactivé par défaut ». |
---
## 6. Aide du Makefile (`make`)
| ID | Affirmation (source) | Type | Commande de preuve | Statut | Écart constaté |
|---|---|---|---|---|---|
| AFF-070 | « `make site-verifier` valide la cohérence couches/graphe » (Makefile aide:133) | fonctionnelle | `python3 scripts/orchestrer.py verifier` | ✅ | « Orchestration cohérente : 30 groupes classés, aucun cycle, aucune arête en arrière. » |
| AFF-071 | « `make flux``docs/registre-flux.md` + `instance/flux-genere/*.nft` » (Makefile aide:146) | fonctionnelle | `python3 scripts/resoudre_flux.py verifier` ; en-tête de `docs/registre-flux.md` | ✅ | « Flux cohérents : 29 rôles, 63 flux ». Le registre porte l'en-tête « GÉNÉRÉ par `scripts/resoudre_flux.py` ». |
| AFF-072 | « `make deployer-tout` — déploiement orchestré de la flotte ; garde `CONFIRMER` » (Makefile aide:138) | fonctionnelle | lecture Makefile:352-357 | 🟡 | Garde `CONFIRMER=true` vérifiée (refuse sinon, rc=2). Déploiement réel hors production → non exécuté. |
| AFF-073 | « `make wiki-publier` … refuse sans `WIKI_REMOTE` » (Makefile aide:151) | fonctionnelle | lecture Makefile:320-326 | ✅ | Garde présente : refus + message d'usage si `WIKI_REMOTE` vide. |
| AFF-074 | « `make devis-reseau` — dérivé des nomenclatures » (Makefile:309) | fonctionnelle | `python3 scripts/devis_reseau.py` | 🟡 | Dérive des instances fédérées `../*/plan/nomenclature.yml` (schéma `ip-miroir`) ; sortie dépend du voisinage de dépôts → vérifiable seulement dans un contexte fédéré. |
| AFF-075 | Cibles dépréciées `make hote-planifier / hote-ajouter / hote-groupes` renvoient vers le plan (README:118 ; Makefile:237) | procédurale | `make hote-planifier` | ✅ | Stub présent : message « Cible dépréciée … » + `exit 2`. |
---
## 7. docs/ — runbooks et cohérence des commandes
| ID | Affirmation (source) | Type | Commande de preuve | Statut | Écart constaté |
|---|---|---|---|---|---|
| AFF-080 | « `make help` — liste des cibles » (docs/config-proxmox.md:114) | procédurale | `grep -E '^help:' Makefile` | ❌ | Aucune cible `help`. La cible réelle est `aide` (défaut). Commande citée inexistante. |
| AFF-081 | « `make syntax-template` » (docs/modeles_vm/debian13-proxmox.md:77) | procédurale | `grep syntax-template Makefile` | ❌ | Aucune cible `syntax-template`. Réel : `syntaxe-modele`. Nom périmé. |
| AFF-082 | « `make syntax-template` » (docs/MISE-A-JOUR-CODEX-CLAUDE.md:146) | procédurale | idem | ❌ | Même nom périmé, second emplacement. |
| AFF-083 | Tous les docs cités par README/QUICKSTART/AGENTS existent (`plan-et-generation`, `positionnement`, `bindings-conception`, `flux-conception`, `couches-deploiement.yml`, `registre-flux.md`, `carte-set-ops`, `runbooks-exploitation`, `vm-lifecycle`, `procedure-template-…`, `dns-interne`, `nomenclature-vm`, `config-proxmox`, `catalogue-services`, `dimensionnement-ressources`, `exemples/vault.exemple.yml`, `exemples/instance.exemple.yml`) | structurelle | `test -f` sur chaque chemin | ✅ | 17/17 présents. |
| AFF-084 | « `docs/dependances-groupes.yml` — chaque groupe existant y figure, aucune entrée ne référence un rôle supprimé » (AGENTS:345,413) | structurelle | `verifier-dependances` (AFF-009) | ✅ | Validé ; graphe sans cycle, tri topologique réussi (via `orchestrer verifier`). |
| AFF-097 | Chemins d'inventaire `lab/` codés en dur pour la config/voûte **Proxmox** et le template, alors que `make config`/`cloner-vm` résolvent `lab > principal > production` (modèle `socle` = `production/`) | procédurale | `grep -rn 'inventories/lab/group_vars' docs playbooks` ; `scripts/config_proxmox.py:31` | 🟡 | **Découvert en Phase 3 (lot A).** Reste `lab/` codé en dur dans `docs/vm-lifecycle.md:171`, `docs/procedure-template-debian13-proxmox.md:741,747`, `docs/modeles_vm/debian13-proxmox.md:89`, `playbooks/proxmox/README.md:31,43,49,50`, `playbooks/proxmox/cloner_vm_debian.yml:45` (message). Non bloquant (l'outillage résout), mais trompeur pour le socle. **Différé** au lot « voûte Proxmox » (couplé à AFF-098). |
| AFF-099 | Le modèle public `socle` livre un `plan/domaines.yml` **invalide** : `exemple.internal` en `autorite: interne`, rejeté par le validateur (`auto-heberge`/`delegue`/`primaire-cache` attendus) | fonctionnelle | `SETOPS_INSTANCE=exemples/modeles/socle python3 scripts/domaines.py verifier` | ❌ | **Découvert Phase 5.** Un opérateur suivant QUICKSTART bute à la validation (étape 9 / `make inventaire-verifier`). `interne` = valeur périmée (déjà corrigée ailleurs en `auto-heberge`, cf. CHANGELOG 2026-07-07). |
| AFF-100 | `instancier.py`/`inventory_gui.py` résolvent `principal > production` mais **retombent sur `principal`** quand aucun `hosts.yml` n'existe encore ; le socle est en `production/` → 1ʳᵉ génération dans `principal/`, à côté des `group_vars` restés en `production/` (split-brain) | fonctionnelle | parcours QUICKSTART hors-ligne sur une copie du socle (`generer`→`appliquer`) | ❌ | **Découvert Phase 5.** `config_proxmox.py` détecte le répertoire existant (→ `production/`) ; `instancier`/GUI non → incohérence. QUICKSTART étapes 6-9 cassées pour le socle. |
| AFF-098 | Contradiction fonctionnelle voûte Proxmox : `make config` écrit le token dans la voûte unifiée `all/vault.yml` (et `config-proxmox.md` dit de **supprimer** `proxmox.vault.yml`), mais le clonage `cloner_vm_debian.yml` (lancé `-i localhost,`) ne charge le secret **que** depuis `proxmox.vault.yml`/`proxmox.local.yml` ou les variables d'env — **jamais** `all/vault.yml` | fonctionnelle | lecture `playbooks/proxmox/cloner_vm_debian.yml:11-12,51-64` vs `scripts/config_proxmox.py:35` | ❌ | **Découvert en Phase 3 (lot B).** Un opérateur qui suit la voûte unifiée met le token dans `all/vault.yml``make creer-vm` échoue l'assert (`proxmox_api_token_secret` vide), sauf variables d'env ou `proxmox.vault.yml` conservé. **Traitement B (code)** : faire lire `all/vault.yml` par le clonage, ou faire écrire `proxmox.vault.yml` par `make config`. À arbitrer dans le lot « voûte Proxmox ». |
---
## 8. wiki/
| ID | Affirmation (source) | Type | Commande de preuve | Statut | Écart constaté |
|---|---|---|---|---|---|
| AFF-090 | Le wiki pédagogique fournit des unités d'apprentissage par service (`wiki/*.md`) publiables dans Forgejo | structurelle | `ls wiki/*.md` | ✅ | 16 pages de contenu (+ `README.md` + `_Sidebar.md`) ; 17 publiées par `wiki-publier` (README exclu). |
| AFF-091 | Contenu conceptuel du wiki (OIDC, PKI, DNS, sauvegardes, idempotence, RBAC…) | doctrinale | — | ⚪ | Contenu pédagogique = affirmations conceptuelles, non rattachées à une preuve exécutable du dépôt. Hors périmètre de preuve automatisable. |
---
## 9. GUI (libellés)
| ID | Affirmation (source) | Type | Commande de preuve | Statut | Écart constaté |
|---|---|---|---|---|---|
| AFF-095 | « La vue **Inventaire** est en lecture seule ; vues éditables **Serveurs**, **Applications**, **Domaines** ; vues lecture seule **Réseau**, **Flux**, **Couches** » (README:41 ; AGENTS:48 ; CHANGELOG) | fonctionnelle | démarrage headless de `scripts/inventory_gui.py` + inspection des routes/onglets | 🟡 | Les routes backend et l'import des validateurs sont présents dans `scripts/inventory_gui.py` ; le rendu des onglets et le caractère « lecture seule » ne sont pas vérifiés sans lancer le serveur + un navigateur (non fait — pas de commande de preuve headless au registre). |
| AFF-096 | « GUI 100 % française » (CHANGELOG 2026-07-03) | structurelle | inspection des chaînes du GUI | ⚪ | Vérifiable seulement par revue exhaustive des libellés rendus ; non automatisé ici. |
---
## 10. Architecture réseau et fédération
> Section ajoutée le **2026-08-03**. Ces affirmations existaient dans la documentation
> depuis juillet, prouvées par le harnais, mais **sans entrée au registre** : trois
> preuves se rabattaient sur `AFF-001` (« moteur générique »), qui parle d'autre chose.
> Une preuve accrochée à la mauvaise affirmation ne prouve rien.
| ID | Affirmation (source) | Type | Commande de preuve | Statut | Écart constaté |
|---|---|---|---|---|---|
| AFF-101 | « Tout l'adressage dérive du seul seed `index` ; aucune adresse n'est stockée » (`docs/sdn-evpn.md` §2 ; wiki *Multi-instance*) | structurelle | `python3 scripts/prouver.py`**P20** | ✅ | Aucun `supernet`/`sous_reseau`/`passerelle`/`vlan` stocké dans une nomenclature. |
| AFF-102 | « Aucune paire d'instances fédérées ne partage un `index` » (`docs/multi-instances.md`) | fonctionnelle | **P21** (`scripts/instances.py --verifier`) | ✅ | 3 instances, aucune collision. Le garde-fou refuse la création en doublon. |
| AFF-103 | « L'underlay ne chevauche aucun supernet tenant et reste sous la plage VLAN tenant » (`underlay.yml.example` ; wiki) | structurelle | **P23** (`scripts/underlay.py --verifier`) | ✅ | Sauté si aucun `underlay.yml` monté (dépôt public nu). |
| AFF-104 | « La frontière déclare toujours une source d'administration ; un devis dont `nftables_admin_ssh` est vide est refusé » (`docs/frontiere-opnsense.md` §5) | fonctionnelle | **P24** (`scripts/devis_opnsense.py --verifier`) | ✅ | Garde anti-lockout : sans cet intrant, la règle SSH n'aurait aucune source et le `block` final fermerait l'accès. |
| AFF-105 | « Les modèles publics valident, underlay compris » (`exemples/modeles/socle/README.md`) | structurelle | **P17** (`scripts/modeles.py verifier`) | ✅ | Underlay facultatif ; validé pour sa cohérence **interne** seulement — un modèle est un gabarit, pas un site déployé. |
| AFF-107 | « Le filtrage est-ouest intra-tenant est dérivé du registre des flux, et redit la même politique que les nftables d'hôte » (`docs/sdn-evpn.md` §3) | structurelle | **P25** (`scripts/devis_proxmox_fw.py --verifier`) | ✅ | 34 groupes, 40 règles sur 2 tenants. Les deux barrières lisent le registre par les **mêmes fonctions** : elles ne peuvent pas diverger. |
intégrations : le rôle déclare sa politique ; le cluster passe à l'hébergeur Deux corrections de propriété, l'une dans le plan, l'autre dans les intrants. 1. Intégrations universelles (D-33/D-34, P26) Le plan portait 57 lignes d'intégration écrites à la main, dont 28 disaient oui à quelque chose de vrai pour tous les hôtes. Elles n'existaient que pour être oubliées — et elles l'avaient été : dans Chezlepro, backup-01 et infra-pki-01 n'étaient ni supervisés, ni journalisés, ni certifiés. Le rôle déclare désormais sa politique une fois, dans meta/integration.yml ; le plan ne garde que les vrais choix et refuse la recopie. Les exemptions se dérivent du service rendu (sauf_role), jamais d'un nom d'hôte : l'AC ne s'enrôle pas auprès d'elle-même, et l'exemption suit step-ca si on le déplace. Une seule fonction de résolution — integrations_de() — lue par l'inventaire, la voûte et le panneau. Sans le passage par la voûte, les secrets des intégrations universelles auraient cessé d'être exigés et P18 serait passé au vert sur une voûte incomplète. Vérifié : diff vide sur Technolibre (la politique reproduit exactement les 41 lignes retirées) ; sur Chezlepro, exactement les groupes manquants, et pas client_pki sur infra-pki-01. 2. Vue Intégrations : la matrice La fiche montrait les intégrations d'UN serveur ; le trou de Chezlepro n'a pas été trouvé par le panneau mais par le devis de pare-feu. Matrice serveurs x intégrations : colonnes de politique en lecture seule, facultatives cochables sur place, ligne de couverture n/N qui rend le motif visible sans le juger. 3. Propriété des intrants (D-35/D-36, P27) Le cluster Proxmox appartient à l'hébergeur, comme sa fabric et sa frontière. Recopié chez chaque tenant, son inventaire avait déjà divergé : deux listes de stockages contradictoires pour le même matériel. API/nœuds/stockages/ponts vont dans proxmox-hebergeur.yml, à côté d'underlay.yml, dont le chemin se dérive — l'hébergeur reste non déclaré (D-17). Restent au tenant son golden template et ses défauts de placement. Le panneau nomme désormais le propriétaire de chaque section : éditer une section « hébergeur » vaut pour tous ses tenants, et l'écran ne le disait pas. 26 preuves OK, 0 échec. --syntax-check des deux playbooks Proxmox. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 14:09:22 -04:00
| AFF-108 | « Une intégration universelle est déclarée par le rôle, pas recopiée par serveur : aucun hôte n'y échappe et le plan ne la redit pas » (`docs/decisions-architecture.md` D-33) | structurelle | **P26** (`scripts/prouver.py`) | ✅ | L'exemption se dérive du **service rendu** (`sauf_role`), jamais d'un nom d'hôte : l'AC ne s'enrôle pas auprès d'elle-même, et l'exemption suit step-ca si on le déplace. |
| AFF-109 | « Ce qui appartient à l'hébergeur (cluster, fabric, frontière) vit dans son dépôt, jamais recopié chez un tenant » (`docs/decisions-architecture.md` D-35) | structurelle | **P27** (`scripts/prouver.py`) | ✅ | Le chemin se dérive du symlink `underlay.yml` : l'hébergeur n'est toujours pas déclaré (D-17). Sautée si aucun underlay n'est monté. |
pools Proxmox : un par tenant, dérivé de l'index (D-37, P28) Onze des quatorze serveurs portent le même nom court chez Chezlepro et chez Technolibre. Vérifié un par un, ce n'est pas un problème technique : tout le reste dérive du seed et diverge (10.27.19.21 contre 10.21.19.21, VMID 117402101 contre 111402101, VLAN 1174 contre 1114, deux domaines internes), et rien n'est indexé sur le nom court — les opérations Proxmox portent toutes un vmid, les certificats un FQDN, et client_backup_repo vise backup-01.{{ domaine_interne }}. Le coût est humain : la console Proxmox affiche le nom, et deux infra-pki-01 y sont indiscernables à l'œil. Le VMID porte le tenant, encore faut-il connaître le codage. Un pool par tenant, dérivé du dossier d'instance et de l'index — déjà unique par P21, donc aucun registre de plus : Chezlepro-17, Technolibre-11. make devis-proxmox-pools rattrape la flotte existante (création du pool, puis affectation des VM actives). Les VM créées ensuite entrent d'elles-mêmes : make creer-vm dérive le pool par la même fonction et le passe à la création. Le playbook crée le pool au préalable — proxmox_kvm échoue sur un pool inconnu, et l'API ne sait pas changer le pool d'une VM existante ; c'est aussi pourquoi le rattrapage passe par les membres. P28 garde deux collisions : même nom de pool entre tenants, et surtout même VMID — une machine appartenant à deux tenants serait pire qu'une homonymie. Rien n'est renommé : les homonymes sont la preuve que la nomenclature est un vrai gabarit. Le devis ne lit pas le cluster, il dit l'état cible et non l'écart. 27 preuves OK, 0 échec. --syntax-check du playbook de clonage. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 15:15:14 -04:00
| AFF-110 | « Chaque tenant a son pool Proxmox, dérivé, sans collision de nom ni de VMID » (`docs/decisions-architecture.md` D-37) | structurelle | **P28** (`scripts/devis_proxmox_pools.py --verifier`) | ✅ | Le nom dérive du dossier d'instance et de l'`index`, déjà unique par P21 — aucun registre de plus. Le devis ne lit pas le cluster : il affirme la cohérence du **plan**, pas l'état appliqué. |
| AFF-106 | « Le panneau couvre le schéma du plan : aucun champ n'oblige à éditer le YAML » (`docs/intrants-base-gui-conception.md`) | fonctionnelle | **P19** (`scripts/couverture_gui.py`) | 🟡 | Vrai des registres du plan. **Exceptions assumées** : `reseaux` et `hotes` de l'underlay (listes de tables) restent hors du panneau ; les ports physiques et le nœud de sortie EVPN se nomment à la main. |
**Non enregistré volontairement.** Les devis eux-mêmes (`make devis-reseau`,
`make devis-opnsense`) ne font l'objet d'aucune affirmation de *justesse* : leur syntaxe
dépend d'un matériel que le dépôt ne possède pas. Six familles ont été confrontées à un
commutateur réel — deux étaient fausses — mais c'est une vérification datée, pas une preuve
rejouable. **Le dépôt n'affirme pas que ses devis s'appliquent ; il affirme qu'ils dérivent.**
## Décompte
Recompte exact par ID (numérotation à trous AFF-001 → AFF-096, **54 affirmations**).
| Statut | Nombre |
|---|---|
| ✅ prouvée | 30 |
| 🟡 partielle | 13 |
| ❌ fausse | 8 |
| ⚪ invérifiable localement | 3 |
| **Total** | **54** |
Détail :
- ✅ (30) : AFF-001, 003, 004, 005, 008, 009, 010, 011, 012, 014, 015, 022, 024, 030, 031, 032, 033, 034, 035, 037, 038, 051, 062, 070, 071, 073, 075, 083, 084, 090.
- 🟡 (13) : AFF-002, 007, 013, 025, 026, 039, 040, 052, 060, 061, 072, 074, 095.
- ❌ (8) : AFF-006, 020, 021, 023, 050, 080, 081, 082.
- ⚪ (3) : AFF-036, 091, 096.
> La Phase 4 (`make prouver`) rattachera chaque ✅ à sa section de preuve et figera le
> compte.
---
## Les dix écarts les plus graves
Classés par risque pour un opérateur qui suit la doc **à la lettre, sans IA**.
1. **AFF-021 / AFF-020 — QUICKSTART renvoie à un modèle inexistant.**
`cp -r exemples/modeles/presence-web ../mon-instance` échoue : seul `socle` est
public. **L'opérateur est bloqué dès l'étape 2** du guide de démarrage. Risque
maximal : c'est la toute première action concrète du parcours « zéro → écosystème ».
2. **AFF-006 — `make verifier` échoue sur un dépôt propre.**
`ansible-lint` rapporte 33 échecs (surtout `name[play]` sur le `site.yml` **généré**
par l'outil lui-même, + `risky-shell-pipe`). La commande de validation vantée par
README et QUICKSTART ne passe pas → l'opérateur croit son dépôt cassé, ou apprend à
ignorer l'échec (perte de confiance dans le harnais).
3. **AFF-050 — Contradiction SSH doc vs code (sécurité).**
`CLAUDE.md` prescrit `PasswordAuthentication yes` pendant la construction ; le code
et `AGENTS.md` imposent `no` + `AuthenticationMethods publickey`. Un opérateur qui
suit CLAUDE.md attend un accès mot de passe qui n'existe pas — et la doctrine de
sécurité affichée est contradictoire.
4. **AFF-023 — Chemin de voûte faux dans QUICKSTART.**
L'étape 4 place les secrets dans `inventories/lab/group_vars/all/vault.yml`, mais le
modèle public `socle` n'a pas d'inventaire `lab/` (seulement `production/`).
L'opérateur crée sa voûte au mauvais endroit → déchiffrement introuvable au déploiement.
5. **AFF-026 — `make inventaire-verifier` exige le Vault sans le dire.**
La validation « à tout moment » appelle `ansible-inventory --list` qui échoue
(`no vault secrets found`) sans `ANSIBLE_VAULT_PASSWORD_FILE`. Prérequis non rappelé
au point d'usage → échec surprenant pour qui n'a pas exporté sa clé.
6. **AFF-002 — Promesse « exploitable sans IA » entamée par 15.**
Le principe fondateur (AGENTS principe 10) est **partiellement démenti par les faits** :
le parcours documenté comporte au moins deux ruptures dures (modèle absent, `make
verifier` cassé). C'est le méta-risque : la doc ne mène pas seule au succès.
7. **AFF-080 / AFF-081 / AFF-082 — Commandes `make` périmées dans la doc.**
`make help` (→ `aide`) et `make syntax-template` (→ `syntaxe-modele`, cité 2×)
n'existent pas. L'opérateur tape une commande qui renvoie une erreur `make`.
8. **AFF-061 — SOLUTION.md décrit un flux SSH périmé.**
`--ask-pass --ask-become-pass` suppose un accès par mot de passe contraire au modèle
clé-only réel. Suivre SOLUTION.md pour construire le template mène à un flux qui ne
correspond plus au code.
9. **AFF-060 — SOLUTION.md décrit une arborescence obsolète.**
« Une seule structure : inventories/playbooks/roles/docs/ » ignore `scripts/`,
`instance/`, `filter_plugins/`, `wiki/`, `exemples/` — désoriente un nouveau lecteur
sur l'endroit où vivent le plan et le GUI.
10. **AFF-052 / AFF-040 — Doctrine dupliquée et titrage « Codex ».**
La duplication CLAUDE.md ↔ AGENTS.md est la **cause racine** de la contradiction n°3 ;
la section « Comportement attendu de Codex » sous-entend à tort qu'elle ne vise pas
Claude. Risque de gouvernance : deux sources d'autorité qui divergent avec le temps.
---
## Journal des traitements
Le tableau ci-dessus est le **snapshot d'audit Phase 1 (2026-07-20)** et n'est pas
réécrit. Les traitements appliqués ensuite sont consignés ici, datés et traçables.
### Phase 2 — 2026-07-20 (3 incohérences internes pré-cadrées)
- **AFF-050 (❌ → résolu)** — Doctrine SSH contradictoire. Vérifié que le code applique
déjà `PasswordAuthentication no` + `AuthenticationMethods publickey` dès la construction
(aligné sur `AGENTS.md`, cf. AFF-037). **Aucun correctif de code** ; la doctrine SSH a
été **retirée de `CLAUDE.md`**.
- **AFF-052 (🟡 → résolu)** — Duplication CLAUDE.md ↔ AGENTS.md. `CLAUDE.md` réduit à un
pointeur mince (autorité d'`AGENTS.md` + 5 règles absolues) ; cause racine d'AFF-050 supprimée.
- **AFF-040 (🟡 → résolu)** — Section « Comportement attendu de **Codex** » renommée
« …des agents IA » dans `AGENTS.md`, avec mention explicite qu'elle vaut pour tout agent.
Reste ouvert après Phase 2 : les 5 ❌ restants (AFF-006, 020, 021, 023, 080/081/082) et
les 🟡 relèvent de la **Phase 3 (arbitrage)**.
### Phase 3 — Lot A « doc de démarrage » — 2026-07-20
- **AFF-020/021 (❌ → résolu, A)** — `QUICKSTART.md` étape 2 : ne propose plus que le
modèle public `socle` (`cp -r exemples/modeles/socle …`) ; les modèles assemblés
renvoyés au dépôt privé `Set-OPS-modeles`, cohérent avec `exemples/modeles/README.md`.
- **AFF-023 (❌ → résolu, A + correctif d'exemple)** — Chemin de voûte corrigé
`lab/``production/` dans `QUICKSTART.md`, `exemples/vault.exemple.yml` et
`docs/config-proxmox.md`. **Piège corrigé** : le modèle `socle` livrait ses intrants en
`production/group_vars/all.yml` (forme fichier) ; ajouter `all/vault.yml` (forme dossier)
**masque silencieusement** le fichier `all.yml` (vérifié empiriquement : seul le dossier
est chargé). Le modèle a été converti en forme dossier (`all/10-intrants.yml`), alignée
sur l'instance prouvée `principal`. Validé : `ansible-inventory --host` charge bien
`domaine_interne` depuis la nouvelle disposition.
- **AFF-080/081/082 (❌ → résolu, A)** — Commandes `make` périmées corrigées :
`make help``make` (`docs/config-proxmox.md`) ; `make syntax-template`
`make syntaxe-modele` (`docs/modeles_vm/debian13-proxmox.md`,
`docs/MISE-A-JOUR-CODEX-CLAUDE.md`).
- **AFF-097 (🟡, découvert)** — `lab/` codé en dur restant dans les docs template/clone
(étapes 5+). Consigné ci-dessus ; **non traité dans ce lot** (lot séparé à prévoir).
### Phase 3 — Lot B « doc (prose) » — 2026-07-20
- **AFF-026 (🟡 → résolu, A)** — `QUICKSTART.md` § « Valider à tout moment » : note ajoutée
que `inventaire-verifier`/`verifier` chargent l'inventaire complet et exigent
`ANSIBLE_VAULT_PASSWORD_FILE` (étape 4), sinon « no vault secrets found ».
- **AFF-039 (🟡 → résolu, A)** — `AGENTS.md` : note ajoutée sous la liste des sous-dossiers
`playbooks/` — seuls `groupes/`, `maintenance/`, `modeles_vm/`, `proxmox/` sont peuplés ;
les autres sont **prospectifs**.
- **AFF-060/061 (🟡 → résolu, A)** — `SOLUTION.md` : bannière « document historique » ajoutée
(supplanté par README/QUICKSTART/`make`) ; arborescence complétée (`scripts/`, `wiki/`,
`instance/`…) ; commande de construction `--ask-pass` (mot de passe) remplacée par
`make preparer-modele` (accès par clé, aligné `AGENTS.md`).
- **AFF-002 (🟡)** — « exploitable sans IA » : **pas de reformulation** (c'est un engagement
fondateur légitime, pas une sur-affirmation). Son seul écart résiduel est AFF-006
(`make verifier` casse) → bascule ✅ une fois AFF-006 traité (lot C, B). Aucune édition.
- **AFF-007 (🟡)** — `web-frontal-01` : **assumé** comme nom d'exemple/placeholder (usage
cohérent dans tout le README) ; aucune édition.
- **AFF-097 / AFF-098** — **différés** au lot « voûte Proxmox » : AFF-098 (contradiction
fonctionnelle `all/vault.yml` ↔ clonage, traitement B) est couplé à AFF-097 (mêmes
fichiers) ; les traiter ensemble évite un double passage.
### Phase 3 — Lot « voûte Proxmox » — 2026-07-20
- **AFF-098 (❌ → résolu, B, option a)** — Le clonage lit désormais la voûte **unifiée**.
`playbooks/proxmox/cloner_vm_debian.yml` : `all/vault.yml` ajouté à la liste des sources
de secrets Proxmox (chargé via le filtre `is search('vault')`, autoritaire car en
dernier). `Makefile` (`cloner-vm`) : la détection de voûte chiffrée cherche d'abord
`group_vars/all/vault.yml` puis `proxmox.vault.yml` (déclenche `--ask-vault-pass`).
Messages de tâche/rescousse/assert rendus génériques. **Validé** : `--syntax-check` OK,
`ansible-lint` 0 échec sur le playbook, et **test fonctionnel** (mini-instance en clair) —
le token est bien chargé depuis `all/vault.yml` et l'hôte depuis `proxmox.yml`
proxmox : pools créés, jeton normalisé, reliquat de voûte supprimé Reconnaissance en lecture seule de l'API du cluster, avec le jeton de la voûte. Trois valeurs devinées étaient fausses, et deux défauts bloquants sont apparus. Corrigé d'après le cluster - stockages : truenas-dbsql manquait ; le catalogue ne garde que ceux qui portent `images` (PBS, cephFS, local et truenas iSCSI n'accueillent pas de disque de VM) ; - ponts : vmbr0 avait été omis, et l'uniformité sur les trois nœuds n'avait pas été vérifiée — un pont partiel empêche la VM de démarrer sur certains nœuds. Pools Chezlepro-17 et Technolibre-11 créés, dérivés comme le reste. Les pools Env.Tenant antérieurs (Prod.Chezlepro, Lab.KBR...) sont l'ancien monde : on n'y touche pas et on n'y verse pas la flotte générée. Diff réel : 2 pools ajoutés, 0 retiré, 1 VM sur 38 déplacée — infra-pki-01, qui n'avait aucun pool. Défaut bloquant : make creer-vm aurait échoué en 401 proxmoxer recompose `utilisateur!nom` à partir d'api_user et d'api_token_id. La voûte stocke la forme complète, que les playbooks passaient telle quelle, d'où un 401 muet — alors que le même jeton fonctionne en curl. Mesuré des deux côtés avec un module en lecture seule : forme complète = 401, forme courte = OK. Normalisation par split('!') | last, qui accepte les deux écritures. Reliquat proxmox.vault.yml supprimé Toléré « en compatibilité », il restait le seul porteur du jeton chez Technolibre — et comme *.vault.yml est gitignoré, ce jeton ne voyageait avec aucun dépôt : une voûte unique (D-19) qui ne l'était pas. Migration faite en mémoire, avec relecture et aller-retour de chiffrement vérifiés avant écriture ; fichier supprimé, listes de chargement des playbooks nettoyées, validé par un appel API réel ne chargeant que all/vault.yml. La garde qui manquait voute.py verifier ne comparait que le gabarit — il disait « complet » pendant qu'un secret vivait ailleurs. Il contrôle maintenant aussi la voûte réelle quand ANSIBLE_VAULT_PASSWORD_FILE la rend déchiffrable : noms de clés seulement, jamais de valeur, et vérification sautée sans mot de passe. Elle a trouvé un second trou dès son premier passage : la voûte réelle de Technolibre n'a ni vault_nextcloud_admin ni vault_nextcloud_oidc, que le plan exige. Non corrigé — générer ces secrets est une décision, et celui d'OIDC doit correspondre à ce que Keycloak connaîtra. 27 preuves OK. --syntax-check et ansible-lint (production) sur les playbooks. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 15:52:44 -04:00
(assert vert). **Suite, 2026-08-03** : `proxmox.vault.yml` n'est plus chargée du tout — tolérée, elle restait le seul porteur du jeton chez un tenant, dans un fichier gitignoré. Migrée vers `all/vault.yml`, supprimée, et `voute.py verifier` contrôle désormais la voûte RÉELLE quand elle est déchiffrable (ce que la seule vérification du gabarit ne pouvait pas voir).
- **AFF-097 (🟡 → résolu, A)** — `lab/` codé en dur retiré des docs Proxmox/template :
`playbooks/proxmox/README.md`, `docs/procedure-template-debian13-proxmox.md`,
`docs/vm-lifecycle.md`, `docs/modeles_vm/debian13-proxmox.md``production/` + voûte
unifiée. Plus aucune référence `inventories/lab/group_vars` dans les fichiers suivis
(hors `tmp/` gitignoré et la section *migration* de `config-proxmox.md`, intentionnelle).
### Phase 3 — Lot C « make verifier vert » — 2026-07-20
- **AFF-006 (❌ → résolu, B)** — `ansible-lint` passe de **33 échecs à 0** (profil monté
`min``production`), donc `make lint` **rc=0**. Trois causes traitées :
- **30× `name[play]`** sur le `site.yml` **généré**`scripts/orchestrer.py` (`rendre_site`)
émet désormais `- name: Couche <c> — groupe <g>` avant chaque `import_playbook` ;
`site.yml` régénéré (`make site`). Logique d'orchestration inchangée (`orchestrer verifier`
OK, 30 groupes, syntax-check OK).
- **2× `risky-shell-pipe`** (`playbooks/valider.yml`) → `set -o pipefail;` +
`executable: /bin/bash` sur les deux tâches shell à pipe.
- **1× `name[template]`** (`playbooks/proxmox/supprimer_vm_debian.yml`) → Jinja déplacé en
fin de `name` (`… (VMID {{ … }})`).
- **Validé** : `make lint` rc=0 ; `make verifier` franchit lint + test (4) + `site-verifier`
+ `flux-verifier` + `syntaxe` (35 playbooks) — **toutes vertes**. Seule `inventaire-verifier`
exige la voûte de l'opérateur (`ANSIBLE_VAULT_PASSWORD_FILE`), prérequis documenté (AFF-026).
- **AFF-002 (🟡 → ✅)** — « exploitable sans IA » : ses deux écarts résiduels sont soldés
(parcours QUICKSTART, lot A ; `make verifier`/lint, ce lot). Bascule ✅, sous le prérequis
Vault désormais rappelé (AFF-026).
### Phase 5 — Boucle documentaire — 2026-07-20
Relecture de cohérence bout-en-bout (README, QUICKSTART, docs, wiki) contre le code final,
au regard du critère du principe 10 : *un opérateur sans IA exécute chaque procédure sans
commande morte, chemin faux ni étape manquante.*
- **Sweep commandes/chemins** : toutes les commandes `make` citées existent ; jetons périmés
(`presence-web` hors contexte, `syntax-template`, `make help`, `--ask-pass`, voûte `lab/`)
soldés (les occurrences restantes ne sont que dans `CHANGELOG.md`/`docs/audit`, historiques).
- **AFF-099 (❌ → résolu, B)** — `exemples/modeles/socle/plan/domaines.yml` : `interne`
`auto-heberge`. Le modèle public valide désormais.
- **AFF-100 (❌ → résolu, B)** — `instancier.py` et `inventory_gui.py` : le repli sans
`hosts.yml` vise maintenant le **répertoire d'inventaire existant** (→ `production/` pour le
socle) avant de retomber sur `principal`. Instances existantes (avec `hosts.yml`) inchangées.
- **Cohérence GUI/doc** — `docs/intrants-communs.md` : `group_vars/all.yml`
`group_vars/all/10-intrants.yml` (le GUI écrit déjà en forme dossier). `QUICKSTART.md`
étape 8 : exemple `serveur_postgresql``serveur_powerdns` (groupe présent dans le socle).
- **Preuve bout-en-bout (hors-ligne)** : parcours QUICKSTART rejoué sur une copie du socle —
`instancier generer`→`comparer`→`appliquer --force` **écrit `production/hosts.yml`**, diff
**vide** ensuite (idempotence), et les 4 validateurs verts (`serveurs` **réconcilié**).
Non-régression vérifiée sur l'instance réelle `principal` (diff vide, cible inchangée).
## Couverture par `make prouver` (Phase 4)
Chaque affirmation ✅ est soit **rejouée** par une preuve de `make prouver`
(`scripts/prouver.py` → `docs/audit/preuve-<date>.md`), soit **structurelle/doctrinale**
(vérifiée à l'audit, rien d'exécutable à rejouer en boucle).
| Affirmation ✅ | Preuve `make prouver` |
|---|---|
| AFF-001, 004, 030, 031, 032 | **P03** (diff-vide) |
| AFF-003 | **P06** (validateurs de registres) |
| AFF-008 | **P04** (groupes ↔ playbooks) |
| AFF-009, 084 | **P05** (dépendances) |
| AFF-010, 011, 012 | **P12** (existence des runbooks) |
| AFF-015, 022, 037, 038, 062 | **P13** (invariants structurels) |
| AFF-033 | **P07** (GUI node --check) |
| AFF-034, 035 | **P10** (handlers ↔ notify) |
| AFF-070 | **P08** (orchestration) |
| AFF-071 | **P09** (flux) |
| AFF-083 | **P11** (syntaxe) + **P12** |
| AFF-097 | **P14** (pas de `lab/` codé en dur) |
| AFF-002, 006 | **P01** (lint) — + parcours démarrage corrigé |
| AFF-030 (inventaire complet) | **P16** (`ansible-inventory --list` ; voûte requise, sauté sinon) |
| AFF-026 | **P18** (gabarit de voûte complet) |
| AFF-101 | **P20** (adressage 100 % dérivé) |
| AFF-102 | **P21** (fédération : aucun index en collision) |
| AFF-103 | **P23** (underlay sans collision) |
| AFF-104 | **P24** (frontière : accès d'administration déclaré) |
| AFF-105 | **P17** (tous les modèles valident, underlay compris) |
| AFF-106 | **P19** (le GUI couvre le schéma du plan) |
| AFF-107 | **P25** (pare-feu Proxmox est-ouest) |
intégrations : le rôle déclare sa politique ; le cluster passe à l'hébergeur Deux corrections de propriété, l'une dans le plan, l'autre dans les intrants. 1. Intégrations universelles (D-33/D-34, P26) Le plan portait 57 lignes d'intégration écrites à la main, dont 28 disaient oui à quelque chose de vrai pour tous les hôtes. Elles n'existaient que pour être oubliées — et elles l'avaient été : dans Chezlepro, backup-01 et infra-pki-01 n'étaient ni supervisés, ni journalisés, ni certifiés. Le rôle déclare désormais sa politique une fois, dans meta/integration.yml ; le plan ne garde que les vrais choix et refuse la recopie. Les exemptions se dérivent du service rendu (sauf_role), jamais d'un nom d'hôte : l'AC ne s'enrôle pas auprès d'elle-même, et l'exemption suit step-ca si on le déplace. Une seule fonction de résolution — integrations_de() — lue par l'inventaire, la voûte et le panneau. Sans le passage par la voûte, les secrets des intégrations universelles auraient cessé d'être exigés et P18 serait passé au vert sur une voûte incomplète. Vérifié : diff vide sur Technolibre (la politique reproduit exactement les 41 lignes retirées) ; sur Chezlepro, exactement les groupes manquants, et pas client_pki sur infra-pki-01. 2. Vue Intégrations : la matrice La fiche montrait les intégrations d'UN serveur ; le trou de Chezlepro n'a pas été trouvé par le panneau mais par le devis de pare-feu. Matrice serveurs x intégrations : colonnes de politique en lecture seule, facultatives cochables sur place, ligne de couverture n/N qui rend le motif visible sans le juger. 3. Propriété des intrants (D-35/D-36, P27) Le cluster Proxmox appartient à l'hébergeur, comme sa fabric et sa frontière. Recopié chez chaque tenant, son inventaire avait déjà divergé : deux listes de stockages contradictoires pour le même matériel. API/nœuds/stockages/ponts vont dans proxmox-hebergeur.yml, à côté d'underlay.yml, dont le chemin se dérive — l'hébergeur reste non déclaré (D-17). Restent au tenant son golden template et ses défauts de placement. Le panneau nomme désormais le propriétaire de chaque section : éditer une section « hébergeur » vaut pour tous ses tenants, et l'écran ne le disait pas. 26 preuves OK, 0 échec. --syntax-check des deux playbooks Proxmox. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 14:09:22 -04:00
| AFF-108 | **P26** (intégrations universelles : aucun hôte laissé de côté) |
| AFF-109 | **P27** (propriété des intrants : hébergeur et tenant séparés) |
pools Proxmox : un par tenant, dérivé de l'index (D-37, P28) Onze des quatorze serveurs portent le même nom court chez Chezlepro et chez Technolibre. Vérifié un par un, ce n'est pas un problème technique : tout le reste dérive du seed et diverge (10.27.19.21 contre 10.21.19.21, VMID 117402101 contre 111402101, VLAN 1174 contre 1114, deux domaines internes), et rien n'est indexé sur le nom court — les opérations Proxmox portent toutes un vmid, les certificats un FQDN, et client_backup_repo vise backup-01.{{ domaine_interne }}. Le coût est humain : la console Proxmox affiche le nom, et deux infra-pki-01 y sont indiscernables à l'œil. Le VMID porte le tenant, encore faut-il connaître le codage. Un pool par tenant, dérivé du dossier d'instance et de l'index — déjà unique par P21, donc aucun registre de plus : Chezlepro-17, Technolibre-11. make devis-proxmox-pools rattrape la flotte existante (création du pool, puis affectation des VM actives). Les VM créées ensuite entrent d'elles-mêmes : make creer-vm dérive le pool par la même fonction et le passe à la création. Le playbook crée le pool au préalable — proxmox_kvm échoue sur un pool inconnu, et l'API ne sait pas changer le pool d'une VM existante ; c'est aussi pourquoi le rattrapage passe par les membres. P28 garde deux collisions : même nom de pool entre tenants, et surtout même VMID — une machine appartenant à deux tenants serait pire qu'une homonymie. Rien n'est renommé : les homonymes sont la preuve que la nomenclature est un vrai gabarit. Le devis ne lit pas le cluster, il dit l'état cible et non l'écart. 27 preuves OK, 0 échec. --syntax-check du playbook de clonage. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 15:15:14 -04:00
| AFF-110 | **P28** (pools Proxmox : un par tenant) |
**Structurelles/doctrinales, hors harnais récurrent** (vérifiées à l'audit) : AFF-005
(`make`=aide), AFF-014 (ciblage `groupe:&hotes_actifs`), AFF-024 (`instancier-appliquer`,
couvert indirectement par P03), AFF-051 (autorité d'`AGENTS.md`), AFF-073/075 (gardes
`make wiki-publier`/`hote-planifier`), AFF-090 (comptage wiki).
**Déclarations d'intention (⚪ — assumées, jamais présentées comme prouvées)** : AFF-036
(`--check` réel), AFF-091 (contenu wiki), AFF-096 (GUI 100 % français), AFF-007
(placeholder `web-frontal-01`). Reprises dans la section dédiée de chaque `preuve-<date>.md`.
## Limites de cet audit
- **AFF-002 est ✅ *par inspection*, pas par démonstration.** Ses écarts bloquants ont été
soldés (parcours QUICKSTART, `make verifier`) et le parcours relu de bout en bout, mais
**aucun opérateur indépendant ne l'a jamais exécuté**. ✅ signifie ici « plus aucun défaut
connu », pas « démontré ». C'est la seule affirmation du registre qu'aucune commande
locale ne peut prouver : elle exige un humain qui n'est pas l'auteur. Épreuve prévue :
[`protocole-operateur-independant.md`](protocole-operateur-independant.md) — son rapport
fera foi, y compris s'il fait redescendre AFF-002 en 🟡 ou ❌.
- **Hors production** : toute affirmation nécessitant une VM, Proxmox, le réseau ou la
flotte vivante (`make valider`, `make deployer*`, `make creer-vm`, `--check` réel, les
très nombreux « **prouvé sur VM réelles** » du `CHANGELOG.md`) est marquée ⚪ ou 🟡 —
**non rejouable localement par conception**. Le `CHANGELOG.md` n'étant pas une surface
publique listée, ses affirmations « prouvé » ne sont pas enregistrées ici ; à noter que
`AGENTS.md` (« pas encore exécuté contre des VM réelles », « ne jamais présenter un rôle
non déployé comme en production ») encadre correctement cette réserve.
- **GUI** : pas de preuve headless au registre (AFF-095/096 en 🟡/⚪).
- **Vault** : l'instance courante chiffre `group_vars/all/vault.yml` ; les commandes qui
chargent l'inventaire complet via Ansible exigent le mot de passe Vault, non fourni
pendant l'audit (les validateurs Python lisent `hosts.yml`/le plan directement et
n'en ont pas besoin — d'où leur usage ici).
- **Numérotation à trous** : les ID sautent (AFF-001…096) pour laisser de la place par
surface ; ce n'est pas une omission.