Set-OPS-Public/CHANGELOG.md
Daniel Allaire a8bea83a91 docs: carte d'orientation (index + mécanismes) + rafraîchir la maturité
Après audit du dépôt : docs/carte-set-ops.md = point d'entrée « à lire
d'abord » (index du corpus) + catalogue des mécanismes transverses (2
directions de binding, pont de cert, résolution BD par registre,
socle-first, check-mode, voûte) avec où ils vivent. But : ne plus
re-découvrir l'existant.

Constat : la cruft était déjà inventoriée dans catalogue-services.md
(rôles-catégories inertes, échafaudages) — référencée, pas dupliquée.
catalogue-services « État d'implémentation » rafraîchi (rôles éprouvés
sur VM réelles : socle, PKI, LDAP, DNS, nginx, pile courriel).
Pointeur ajouté depuis architecture-set-ops.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 09:16:14 -04:00

390 lines
29 KiB
Markdown

# CHANGELOG — Set-OPS
## 2026-07-03
### Ajouté
- **Bindings — Phase 1 : résolveur de liens dans `instancier.py` (relations service→service
déclaratives).** Une application déclare ses `liens: [{vers, role}]` dans
`plan/applications.yml` ; chaque rôle décrit les liens qu'il accepte dans `meta/liens.yml`
(`setops_liens.accepte`, comme `meta/empreinte.yml`). `instancier` résout la cible (FQDN interne
**dérivé de la nomenclature** + `domaine_interne`), substitue les gabarits (`{cible.fqdn}`,
`{cible.hote}`, `{cible.ip}`) et **injecte les variables en host_vars du consommateur**.
Validation : rôle accepteur, cible existante, genre attendu. **Migration prouvée** : les liens
mail Postfix→Dovecot (`mailstore`) et Postfix→rspamd (`milter`) passent de group_vars codés en
dur à des liens déclaratifs — `make instancier` donne **DIFF VIDE** (mêmes variables générées),
puis les group_vars sont retirés. La topologie mail devient déclarative et portable.
Cf. `docs/bindings-conception.md`. Suite : bases (Phase 2), exposition/domaines (Phase 3), GUI (Phase 4).
- **Bindings — Phase 2 (bases) : constat + réconciliation de la note (`docs/bindings-conception.md` §5/§9).**
Inspection du code réel : le binding app→base **existe déjà****côté base** (`consommateur`/`portee`
dans `bases-donnees.yml`), résolu **dans le rôle** au déploiement (`include_vars` + filtre +
`lookup('vars', secret)`), sur 4 rôles (postgresql, forgejo, keycloak, icinga). **Délibérément
conservé** (le secret ne quitte jamais le rôle) — ne PAS dupliquer en app-side/instancier. Deux
directions assumées : app→app côté app (instancier), app→base côté base (registre). Reste (reporté
à l'épreuve de Keycloak) : factoriser le bloc de résolution copié-collé en include partagé (DRY).
- **`docs/carte-set-ops.md` — carte d'orientation (index + mécanismes transverses).** Après audit
du dépôt : point d'entrée « à lire d'abord » (index du corpus, ~22 docs), et **catalogue des
mécanismes** dispersés dans le code (les 2 directions de binding, pont de cert, résolution BD
par registre, socle-first, sûreté check-mode, voûte, dimensionnement) avec **où ils vivent**.
But : ne plus re-découvrir l'existant. Constat : la **cruft était déjà inventoriée** dans
`catalogue-services.md` (rôles-catégories inertes, échafaudages) — non dupliquée, référencée.
`catalogue-services.md` « État d'implémentation » **rafraîchi** (rôles éprouvés sur VM réelles :
socle, PKI, LDAP, DNS, nginx, pile courriel). Pointeur ajouté depuis `architecture-set-ops.md`.
## 2026-07-02
### Décidé
- **Bindings — conception des relations app/base/serveur/domaine (`docs/bindings-conception.md`).**
Les relations service→service sont aujourd'hui codées en dur, éparpillées dans des group_vars
(ex. Postfix→Dovecot/rspamd/LDAP), ce qui casse la portabilité multi-tenant. Direction retenue :
**liens déclarés côté application** (`liens: [{vers, role}]`), résolus par `instancier.py` en
variables Ansible ; chaque rôle décrit les liens qu'il accepte dans `meta/liens.yml` (comme
`meta/empreinte.yml`) ; FQDN cible **dérivé de la nomenclature** (jamais codé en dur). Domaines
publics traités comme lien `exposition` (écrit sur l'edge). Réconcilie l'existant (bases
`consommateur`, `domaines.edge`). Preuve de migration ciblée : les 3 liens mail. Implémentation
à suivre (phasée).
- **Licence : passage de CC BY-NC-SA 4.0 à AGPLv3.** Les licences Creative Commons ne sont pas
faites pour du logiciel (position de CC elle-même) et la clause **NonCommercial contredisait
le principe fondateur « tout est libre »** — en plus de bloquer les artisans/coopératives
visés. `LICENSE` remplacé par le **texte officiel intégral de l'AGPLv3** (verbatim, non
modifié). Attribution + modèle **libre + services/certification** documentés dans le `README`
(méthode d'attribution recommandée, sans toucher au texte de la licence). L'AGPLv3 protège la
souveraineté (anti-captation propriétaire en SaaS) **sans interdire l'usage commercial**.
- **Architecture d'identité/SSO (`docs/identite-sso.md`).** Modèle A : **OpenLDAP source de
vérité**, **Keycloak fédéré** (SSO web OIDC, MFA, self-service), **mail en bind LDAP
direct**. Une identité, un mot de passe, deux chemins d'auth (web→Keycloak, mail→LDAP),
tout adossé au même OpenLDAP. Sert la portabilité multi-tenant (chaque tenant = son LDAP +
son Keycloak fédéré). Pas Keycloak-source (casse-tête mail + moins portable).
- **Service courriel : pivot de Stalwart vers Postfix + Dovecot + rspamd.** La Phase 1
Stalwart (`serveur_stalwart`) avait été prototypée et déployée (v0.16.11, install +
démarrage en mode récupération). Le prototypage a révélé un projet **trop jeune/volatil
pour un pilier mail critique** : config cassée entre 0.15 et 0.16, outil IaC
`stalwart config apply` **annoncé mais non livré** dans le binaire, API REST supprimée
(JMAP), gros backlog. Pivot vers la stack **mature Postfix/Dovecot/rspamd**, en prime
**100 % configurable par fichiers** (alignée au modèle déclaratif Set-OPS). Le rôle
`serveur_stalwart` est **retiré** (git en garde la trace) ; `docs/courriel-conception.md`
mis à jour. Réévaluer Stalwart ~2028.
### Ajouté
- **`docs/pouvoirs-set-ops.md` — bilan des capacités du moteur.** Inventaire structuré
(moteur/plan, plan de contrôle GUI, socle durci, piliers d'infrastructure, services outillés,
patrons d'ingénierie), distinguant honnêtement « prouvé sur cluster réel » de « outillé ».
- **Rôle `serveur_rspamd` (rspamd 3.x) — antispam + DKIM, en milter sur Postfix.** Installé
sur le nœud edge-mta (avec Postfix), backend **Redis** local, worker proxy en **mode milter
auto-scan** (`:11332`), **signature DKIM** sortante (clé générée par le rôle de façon
idempotente ; enregistrement DNS public affiché pour l'Étape B). Config par surcharges
`/etc/rspamd/local.d/`. Postfix branché via `smtpd_milters` (option `serveur_postfix_rspamd_milter`,
`milter_default_action = accept` → tolérant si rspamd indisponible). **Prouvé** : un courriel
traversant le milter ressort **scanné** (`rspamc stat` : 1) et **signé DKIM** (`DKIM-Signature:
d=…`), puis livré et lu en IMAP. **Étape A (courriel interne) complète** : dovecot + postfix + rspamd.
- **Flux courriel interne PROUVÉ de bout en bout (Étape A).** Envoi → Postfix (`edge-mta`,
validation LDAP) → LMTP réseau → Dovecot (mail-store) → boîte Maildir → **lu en IMAP**
(auth LDAP, TLS step_ca) : `status=sent`, message lu (sujet + corps). Réglages Dovecot 2.4
qui débloquent la remise LMTP : `userdb static { static_allow_all_users = yes }` (sinon
NOTFOUND pour l'expéditeur/raw-mail-user externe), `mail_inbox_path =` vidé (le défaut mbox
`/var/mail` root refusait l'autocréation de l'INBOX), Maildir explicite (`mail_home` +
`mail_path = %{home}/Maildir`), chemin par nom d'utilisateur (home identique côté LMTP
local-part et IMAP adresse complète ; mono-domaine, multi-domaine = raffinement Étape B).
- **Rôle `serveur_postfix` (Postfix 3.x) — MTA du nœud edge-mta.** Réception `:25`, cartes
**LDAP** (validation des boîtes via l'attribut `mail`), remise **LMTP réseau** vers le nœud
mail-store Dovecot (`virtual_transport = lmtp:inet:[…]:24`), TLS via **step_ca** (pont de
cert), aucune boîte locale. Config `main.cf` + carte `ldap-mailboxes.cf`, validée par
`postfix check`. Secret de bind : `vault_openldap_admin`. Nécessite `serveur_postfix_mailstore_hote`
(FQDN du mail-store). Validé statiquement ; déploiement réel à suivre.
- **Rôle `serveur_dovecot` (Dovecot 2.4) — déployé et prouvé.** IMAP `:993`/`:143` +
LMTP, **auth/annuaire LDAP** (vers `serveur_openldap`, filtre `mail`), stockage Maildir
(user système `vmail`), **TLS via step_ca** (pont de cert + resync au renouvellement),
neutralisation de l'auth système par défaut. Config en drop-in **syntaxe Dovecot 2.4**
(`mail_driver`, `ssl_server_cert_file`, `passdb ldap`/`userdb static`, `%{user}`),
**validée par `doveconf`** au déploiement. Sockets d'intégration Postfix **conditionnels**
(rendus si l'utilisateur `postfix` est co-localisé). Prouvé : `doveadm auth test` — bon
mot de passe accepté, mauvais refusé, sur cert step_ca. Secret de bind : `vault_openldap_admin`.
- **`serveur_openldap` durci pour la prod : TLS via step_ca + organisation en intrant.**
- **TLS (LDAPS + STARTTLS)** : le certificat d'hôte step_ca (déposé par `client_pki`,
`root:root 600`) est synchronisé vers un emplacement lisible par `openldap` (`/etc/ldap/tls`)
par un script + une unité `path` systemd qui **re-synchronise et recharge slapd à chaque
renouvellement** ; `olcTLS*` configuré dans `cn=config`, `SLAPD_SERVICES` expose `ldaps://`.
Dégrade proprement (slapd en clair local) si `client_pki` n'a pas encore posé le cert.
- **Organisation** : nouvel intrant `chezlepro_organisation` (remplace le « Exemple Inc » codé).
- *Écrit en code de prod, éprouvé statiquement ; validation par déploiement réel à suivre.*
- **`docs/courriel-conception.md`** — cadrage du futur service de courriel souverain :
full self-host, suite **Stalwart** (adoptée, enveloppée par un rôle mince), topologie
MX primaire + MX secours, intégration identité (LDAP) / DNS / PKI (Let's Encrypt public
vs step_ca interne), enregistrements DNS publics, décisions ouvertes (IP/PTR, secours,
stockage) et phasage. **Conception seulement — aucun rôle livré.**
## 2026-07-01
### Ajouté
- **État RÉEL vs plan dans le GUI (sonde de vie + auto-actif).** Le badge `planifié`/
`actif` décrit l'*intention* du plan, pas l'existence de la VM — d'où la confusion
« serveur planifié mais vivant ». Deux ajouts :
- **Sonde de vie** : le GUI teste la joignabilité SSH de chaque hôte en arrière-plan
(`/api/sondes`, en parallèle) et affiche un état réel — **● vivante** / **● injoignable**
— sur les tuiles et dans le détail (« Plan : … · Réel : … »), distinct du plan.
- **Auto-actif** : un hôte qu'on **matérialise** (clone `creer` réussi) ou qu'on
**déploie** passe automatiquement `actif` dans le plan (matérialisé = actif), puis
l'inventaire est **régénéré** pour que le changement se voie partout (en-tête inclus).
- **Compteur « vivantes »** dans l'en-tête (depuis la sonde), à côté de actifs/planifiés.
### Corrigé
- **nginx ne validait pas sur Debian 13 (`server_tokens` en double).** Debian 13 livre
`server_tokens off;` **actif** dans `/etc/nginx/nginx.conf` (avant : commenté). Le
drop-in `conf.d/99-setops.conf` du rôle le redéclarait → `nginx -t` échouait
(« directive is duplicate ») et le déploiement plantait au handler de validation. Le
rôle `serveur_nginx` neutralise désormais la ligne distro (le drop-in reste l'unique
source). Trouvé en déployant nginx pour de vrai sur un hôte edge.
- **Une voûte chiffrée cassait `instancier` / « Appliquer le plan ».** `ansible-inventory
--list` (utilisé pour la comparaison sémantique du plan) tente de déchiffrer
`group_vars/all/vault.yml` et échoue sans mot de passe (`exit 4`) — alors que
l'opération est structurelle, sans secret. `instancier` utilise désormais
automatiquement le fichier conventionnel `~/.config/setops-vault-pass` (si
`ANSIBLE_VAULT_PASSWORD_FILE` n'est pas déjà défini).
- **Secrets des rôles non câblés à la voûte (échafaudage manquant).** Les rôles à
secrets déclaraient `serveur_X_password: ""` avec, en commentaire seulement, la
variable de voûte attendue (`{{ vault_X }}`) — sans mapping réel. Résultat : remplir
la voûte selon `vault.exemple.yml` ne suffisait pas, le secret restait vide et
l'assertion « secrets requis » échouait. Les 12 secrets des 8 rôles (`serveur_step_ca`,
`serveur_forgejo`, `serveur_grafana`, `serveur_keycloak`, `serveur_openldap`,
`serveur_redis`, `client_ldap`, `client_pki`) pointent désormais vers leur variable
de voûte : `serveur_X_password: "{{ vault_X | default('') }}"`. Chaque instance n'a
plus qu'à remplir ses `vault_*` dans sa voûte chiffrée ; aucun mapping par instance.
- **« Vérifier » (dry-run `--check`) échouait faussement sur un hôte frais.** Les tâches
« démarrer service » et les handlers « redémarrer / recharger / valider » des rôles
applicatifs touchent un paquet que `--check` n'installe pas réellement → le service
(ou le fichier de zone/conf) n'existe pas encore → faux `fatal`, qui **bloquait le
déploiement** (le dry-run doit réussir pour débloquer « Déployer »). Ajout de
`when: not ansible_check_mode` sur ces tâches et handlers des 13 rôles `serveur_*`
(29 gardes). En dry-run elles sont sautées ; en vrai déploiement, inchangées.
- **Le bouton ⚙ « Appliquer le plan » du GUI refusait en silence** dès que le plan
divergeait de l'inventaire (il appelait `instancier appliquer` **sans** `--force`).
Résultat : après une édition (disque, état, auto-actif), l'inventaire n'était jamais
régénéré et les compteurs restaient figés. Le clic « Appliquer » **est** l'intention
explicite → le GUI force désormais (git reste le filet).
- **Bouton « Pousser » dans le GUI — le flux devient 100 % cliquable.** Chaque objet
du détail se matérialise sur son hôte, en streaming console (avec mot de passe vault
+ confirmation renforcée en prod) :
- **Serveur** → `🖥 Pousser` clone la VM depuis le golden template (`make creer-vm`),
disponible même sur un hôte planifié — comble le trou : le GUI ne clonait pas.
- **Application** → `Pousser` déploie l'hôte porteur (`make deployer HOTE=<hôte>`).
- **Base** → `Pousser` déploie l'hôte du serveur de BD (crée la base).
Nouveaux modes `creer` / `pousser` dans `executer_flux` + routes `/api/creer` et
`/api/pousser`. Flux complet depuis l'interface : éditer → ⚙ Appliquer → 🖥 Pousser
→ Vérifier → Déployer.
- **Premier déploiement RÉEL validé de bout en bout** (cluster asgard) : flux canonique
plan-piloté clonant + durcissant + faisant tourner un PowerDNS qui résout. Voir les 3
correctifs ci-dessous.
## 2026-06-30
### Ajouté
- **Plancher de résolution `/etc/hosts` (indépendant du DNS).** Nouveau rôle de socle
`hosts_statiques` (dans `serveur_debian`) : génère `/etc/hosts` sur **chaque** VM
depuis l'inventaire (nom + FQDN interne → IP réelle). Tout l'écosystème se résout
par nom **même serveur DNS éteint**, et le bootstrap ne dépend plus du DNS. PowerDNS
devient une **commodité** (zone/externe/dynamique) ; `client_dns` est rendu tolérant
(inerte si aucun DNS interne) et sa dépendance à `serveur_powerdns` passe **molle**.
- **Adressage fédéré : index d'instance.** Le VMID n'est plus codé `9CSNN` en dur :
il prend le préfixe d'un `index` déclaré en tête de `plan/nomenclature.yml`
(`{index}{catégorie}{service}{séq}`). Convention : `supernet = 10.(10+index).0.0/16`,
`VMID = index·CSNN`. Permet à N écosystèmes de **coexister/s'interconnecter** sans
collision (Chezlepro=1 → `10.11`/`1xxxx`, Technolibre=2 → `10.12`/`2xxxx`). Sans
index → `9CSNN` (rétro-compatible ; bacs à sable, plages ad-hoc `172.19.x`).
Code mort retiré (`deriveServeur` JS). Voir `docs/multi-instances.md`.
- **Doc `docs/multi-instances.md`** : cadrage « un moteur, N écosystèmes » — l'instance
comme dépôt autonome, la bascule, l'isolation, et le socle multi-tenant.
- **Bascule d'instance (`make instance-utiliser NOM=…`).** Repointe le symlink
`instance` vers un autre dépôt d'instance (prod ↔ bac à sable) ;
`make instance-courante` affiche l'instance montée. Permet d'exploiter plusieurs
instances (séparation **par instance**) depuis un seul moteur.
- **Identité des intrants relative à l'inventaire.** Le panneau « Intrants » lit/écrit
l'identité dans `group_vars/all/10-intrants.yml` de l'inventaire monté (fichier réel
pour une instance autonome, symlink vers une source partagée sinon — l'écriture suit
le symlink). Fonctionne donc aussi bien pour une instance « par instance » que pour
l'ancien partage lab/production.
- **Inventaire d'instance neutre et configurable (`principal` / `SETOPS_INVENTAIRE`).**
Le moteur (Makefile + `inventory_gui`, `instancier`, `config_proxmox`, `serveurs`,
`applications`) ne code plus en dur `inventories/lab` / `inventories/production` :
il vise **un inventaire par instance**, détecté de façon **rétro-compatible**
(`principal` > `production` > `lab`) et surchargeable par `SETOPS_INVENTAIRE`. Les
instances existantes (découpage lab/production) continuent de fonctionner à
l'identique ; les nouvelles peuvent adopter `inventories/principal/`. Deuxième pierre
de la séparation **par instance** (la 1re étant le drapeau `setops_production`).
- **Garde-fou de prudence par instance (`setops_production`).** Le déploiement réel
est désormais possible sur **toute instance** (un bac à sable déploie sur *son*
infra lab — il est isolé **et** fonctionnel). Le drapeau `setops_production` dans
`group_vars/all/` ne **bloque** plus rien : il marque la PRODUCTION pour exiger une
**confirmation renforcée** au déploiement (bannière/badge rouge « PROD », bouton
Déployer en rouge, re-saisie du nom d'hôte) ; un bac à sable (`false`) affiche
« bac à sable » et déploie sans cette étape. Rétro-compatible (à défaut de drapeau,
ancien repère « inventaire production »). Le GUI affiche toujours quel type
d'instance est monté.
### Modifié
- **Voûte de secrets unique par environnement.** Fini les voûtes éparpillées : tous
les secrets de l'instance (token Proxmox + 17 `vault_*` pour PKI, LDAP/SSO, bases,
forge, observabilité) vivent dans **un seul fichier chiffré**,
`inventories/<env>/group_vars/all/vault.yml`. Gabarit committé
`exemples/vault.exemple.yml`. `make config` (`config_proxmox.py`) écrit/édite
désormais cette voûte (semée depuis le gabarit si absente). `.gitignore` durci
(`**/vault.yml`). Docs mises à jour (config-proxmox.md avec étapes de migration,
intrants-communs.md §H, QUICKSTART). Le GUI ne stocke toujours aucun secret.
### Corrigé
- **Trois bugs trouvés au premier déploiement réel (cluster asgard).**
- **Clonage/Makefile codaient `inventories/lab` en dur** (config Proxmox + voûte) —
vestige du modèle env qui cassait les instances `principal`. Le playbook de clonage
et le Makefile détectent maintenant l'inventaire (`lab` > `principal` > `production`).
- **Redimensionnement disque non idempotent** : quand le disque dérivé du plan est
plus petit que le golden template, Proxmox refuse (`shrinking disks is not
supported`) et le clone échouait. Le resize est désormais **grow-only** (tolère le
cas, la VM garde le disque du template — le dérivé est un minimum).
- **PowerDNS refusait de démarrer** (`multiple backends 'bind'`) : le rôle
redéclarait `launch+=bind` que le paquet `pdns-backend-bind` pose déjà. Le rôle ne
déclare plus `launch` (seulement `bind-config`).
- **`chezlepro_timezone` n'était appliqué nulle part.** Cet intrant de base global
était défini mais aucun rôle ne s'en servait. Le rôle `chrony` (appliqué à tout
hôte via `serveur_debian`) règle désormais le fuseau horaire à partir de
`chezlepro_timezone` (`chrony_timezone` par défaut, vide = ne pas toucher). Les
autres défauts globaux (nœud / stockage / pont Proxmox) étaient déjà réutilisés
comme valeurs par défaut, surchargeables par hôte.
### Modifié
- **Détail GUI : section « Groupes (dérivés) » retirée (redondante).** Depuis la fusion
en atelier maître-détail, elle ne répétait que le socle (universel), les rôles des
applications (déjà dans « Applications ici ») et les intégrations (déjà cochées). Son
seul signal unique — le prérequis bloquant — est désormais nommé dans le pied
(« Prérequis manquant : … ») ; le panneau Dépendances reste pour la vue d'ensemble.
Code mort retiré (`renduGroupe`, `detailGroupe`, `titreGroupe`, CSS `.groupe*`).
- **Nettoyage CSS/HTML du GUI** après la refonte : retrait des règles et éléments
morts (`.message`, `.chips-filtre`/`.chip-f`, `.onglet*`, `.base-ligne`,
`.bases-liste`, `.ch-grp*`/`.ch-fleche`/`.ch-roles`, `.champ-val`, divs `#message`
et `#chips`).
- **GUI refondu en atelier maître-détail unifié.** Toutes les vues suivent le même
motif : tuiles à gauche, **détail + saisie à droite**, le panneau droit reflétant
la sélection de la vue courante (fin du panneau « figé » au changement de vue).
Les vues **Applications** et **Bases** passent de tableaux pleine largeur à ce
motif ; la saisie se fait dans le panneau droit, avec **liens cliquables** entre
objets (serveur → application → base). Le panneau droit est élargi (~38 %).
- **Fusion Serveur/Hôte.** Les vues « Inventaire » (hôtes, lecture seule) et
« Serveurs » (plan) faisaient doublon : elles sont fusionnées en **une seule vue
Serveurs**. Sa tuile porte le statut de réconciliation (réconcilié / divergent /
non instancié) ; son détail réunit l'**identité éditable** (plan), les **dérivés**
(VMID/IP/VLAN), les **groupes**, les **applications et bases hébergées**, et
**Vérifier/Déployer**. La navigation clavier (`1-3`, `j/k`, `v/d`, `/`) et le
filtre opèrent désormais sur les serveurs. Un bandeau « Comment lire ce parc »
explicite le modèle serveur·hôte·groupe·application·base. Code mort retiré
(`carte`, `renduChaine`, `ONGLETS`, `champLecture`, sélection d'hôte, etc.).
### Ajouté
- **Fluidité d'exploitation du GUI** (sans dépendance, stdlib pure) :
- **Notifications empilées (toasts)** auto-effaçables au lieu d'une bannière unique
écrasée ; les erreurs restent plus longtemps, les opérations longues mettent à
jour leur propre toast.
- **Durée d'exécution** affichée en direct dans la console (Vérifier / Déployer) et
suivi vivant de « Appliquer le plan ».
- **Navigation clavier** : `1-5` changent de vue, `j/k` parcourent les hôtes,
`v`/`d` vérifient/déploient l'hôte sélectionné, `/` cible le filtre, `Ctrl+S`
sauvegarde la vue éditable courante (ignorés pendant la saisie).
- **Validation inline** des champs du plan (vue Serveurs) : nom `fonction-NN`,
mémoire, cœurs, disque — liseré rouge et blocage avant l'envoi.
- **Garde-fou anti-perte** : confirmation `beforeunload` si des éditions de plan ne
sont pas sauvegardées.
- Le bouton **Sauvegarder** de l'en-tête devient contextuel (sauve la vue éditable,
désactivé en lecture seule) — fin de l'impasse 409 ; suppression du code mort.
### Ajouté
- **Vue Serveurs : champs alimentés par les paramètres globaux.** À `+ Serveur`,
**Nœud** et **Stockage** deviennent des listes déroulantes (catalogues
`proxmox_noeuds` / `proxmox_stockages`, vide = défaut global) et **Intégrations**
une rangée de cases à cocher des rôles `client_*` disponibles (au lieu d'une saisie
texte). Les catalogues Nœuds / Stockages / Ponts sont de nouveaux intrants
(classe « Catalogue ») éditables dans le panneau « Intrants de base » et stockés
dans `group_vars/proxmox.yml` ; l'API expose `integrations_disponibles` (scan de
`roles/client_*`). Le type `liste` (chaîne virgulée → liste YAML dédoublonnée) est
ajouté au schéma des intrants.
### Supprimé
- **Vue « Chaîne » du bandeau (redondante).** Son contenu (`renduChaine`) était déjà
rendu, par hôte, dans l'onglet « Chaîne » du détail de la vue Inventaire ; la vue ne
faisait que le répéter pour tous les hôtes à la fois, sans réseau/proxmox ni boutons
d'opération. Retirée (bouton, rendu `dessinerArbre`, CSS `.arbre-*`) ; la chaîne
reste consultable hôte par hôte. Raccourcis clavier ramenés à 1-4.
### Modifié
- **Vue Inventaire (cartes) : détail converti en inspection lecture seule.** La vue
affichait « généré depuis le plan (lecture seule) » tout en exposant une surface
d'édition (+ Hôte, ✕, bascule actif/planifié, ✨ Proposer, champs et cases de
groupes éditables) qui ne pouvait pas être persistée (l'inventaire est généré ;
`/api/inventaire` renvoie 409). Ces contrôles orphelins sont retirés : nom, champs
Réseau/Proxmox et groupes en lecture seule, état affiché en badge. **Vérifier /
Déployer** et les onglets d'inspection sont conservés. L'édition reste dans les vues
**Serveurs / Applications / Bases**. Code mort supprimé (`ajouterHote`,
`supprimerHote`, `definir`, `definirNom`, `definirEtat`, `basculerGroupe`,
`proposer`, `autoProposer`, `prochainSeqLibre`, `marquerModifie`, état `modifie`).
- **Référence des paramètres de `make config`** (`docs/config-proxmox.md`).
Explique un par un les 16 paramètres Proxmox non sensibles + les secrets API
demandés par l'assistant : sens, valeur par défaut, quoi saisir, et quels champs
sont des constantes vs des défauts surchargeables par hôte. Renvois ajoutés depuis
`make help` et `QUICKSTART.md`. Comble un trou : ces invites n'étaient expliquées
nulle part de façon pérenne (impératif « exploitable sans IA »).
- **Panneau « Intrants de base » dans le GUI.** Un bouton ⚙ Intrants ouvre une fenêtre
unique pour saisir les valeurs communes à tout l'écosystème, avec une distinction
visible entre **constantes** (valeur unique, non surchargeable) et **défauts**
(valeurs proposées, surchargeables dans les instances). Les secrets ne sont **jamais**
saisis ni affichés ici (garde-fou `INTRANTS_CLES_INTERDITES` + filtrage par schéma) :
ils restent dans le Vault Ansible, édités en CLI ; le panneau les liste seulement à
titre informatif. Un changement de `domaine_interne` (clé de voûte) demande une
confirmation explicite ; un `domaine_interne` vide est refusé côté serveur. La
nomenclature reste en lecture seule (modifiable dans le plan). Voir
`docs/intrants-communs.md` et `docs/intrants-base-gui-conception.md`.
- **Source unique d'identité partagée.** `domaine_interne` et `chezlepro_timezone`
vivent désormais dans `inventories/partage/intrants-identite.yml`, référencé par
symlink depuis chaque environnement (`group_vars/all/10-intrants.yml`) — fin de la
duplication lab/production.
- **Info-bulles d'aide sur les champs du GUI.** Survoler la description d'un champ à
saisir affiche une bulle avec des instructions sommaires.
## 2026-06-28
### Ajouté
- **Document de présentation de l'écosystème Chezlepro** (`docs/ecosysteme-chezlepro.md`).
Description vulgarisée à destination client/partenaire : les piliers de l'écosystème
souverain, le modèle reproductible (plan → génération → clonage → conformité), et un
inventaire des **mesures de renforcement** réellement en place (SSH durci, fail2ban,
sysctl, nftables, AppArmor, auditd, mises à jour automatiques, gestion des secrets,
garde-fous destructifs, sécurité par l'architecture). Honnête sur le statut
« défini/validé vs déployé ».
### Ajouté
- **Dimensionnement dérivé des ressources VM.** Les cœurs/RAM/disque d'une VM sont
désormais **estimés depuis les logiciels hébergés + le socle SE**, au lieu d'hériter
des specs du golden template (CPU/RAM identiques pour toutes les VM auparavant).
Chaque rôle déclare son empreinte (`roles/<rôle>/meta/empreinte.yml`) ; le
générateur somme par hôte (marge + arrondis) et écrit `proxmox_coeurs`/
`proxmox_memoire`/`proxmox_disque_taille` ; le clonage passe `cores`/`memory` à
Proxmox (`omit` si absent → aucune régression). Override par hôte possible dans le
plan (`serveurs.yml`). Voir `docs/dimensionnement-ressources.md`.
### Corrigé
- **`make instancier-appliquer FORCE=1` n'honorait pas `--force`.** La recette Makefile lançait `instancier.py appliquer` sans relayer `FORCE` ; le message « Utilise FORCE=1 » était donc trompeur (un changement de plan intentionnel restait bloqué). La recette passe désormais `$(if $(FORCE),--force)`. Trouvé en dogfooding.
## 2026-06-24 — Première publication publique
Première mise à disposition publique de **Set-OPS**, moteur Ansible d'écosystèmes
numériques souverains sur Proxmox — offert à la communauté québécoise par
l'**Alliance Boréale**, à la Saint-Jean-Baptiste 2026.
- **Moteur générique, piloté par un plan déclaratif** : on édite le plan
(`instance/plan/*.yml`), l'inventaire Ansible se génère, les VM se clonent depuis
un golden template Debian 13, les rôles s'appliquent par groupes. Tout passe par
`make`, le GUI local et la documentation.
- **Piliers d'un écosystème souverain** : socle Debian durci, AC/PKI interne, DNS
interne, identité (LDAP + SSO), relais courriel, bases de données, observabilité,
forge.
- **Catalogue de modèles prêts à déployer** (`exemples/modeles/`) : un hébergeur
copie un modèle, le renseigne à ses couleurs, et instancie.
- **Souveraineté jusqu'au bout** : Set-OPS s'exploite entièrement à la main, sans
aucune IA.
Pour démarrer : **`QUICKSTART.md`**.