serveur_icingaweb2 installe + active businessprocess, crée le répertoire des processus (éditable UI) et sème des processus BPM en IaC (serveur_icingaweb2_bpm_processes). Format host;service éprouvé via les fixtures du module. Prouvé : processus « Supervision Chezlepro » (roll-up load/procs/swap/ ping4/ssh en ET) rend un état dans l'UI, backend IcingaDB (pas d'IDO). Rôle re-prouvé (reset → recrée le processus). Pile Icinga = moteur + Web 2 + BPM. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
42 KiB
CHANGELOG — Set-OPS
2026-07-03
Ajouté
- Rôle
serveur_icingaweb2— Icinga Web 2 (UI native) + module IcingaDB : éprouvé. App PHP (php8.4-fpm) servie par un nginx local, exposée par l'edge (icinga.lab.chezlepro.internal, auto-dérivé : vhost + cert SAN + A PowerDNS + alias plancher). Config par fichiers.ini(config/resources/authentication/roles + moduleicingadb), pas d'assistant de setup. Base IcingaDB viaresoudre_base(registre). Auth LDAP direct vers OpenLDAP (LDAPS,client_pkisursup-01) — icingaweb2 n'a pas d'OIDC natif ; SSO-par-proxy = raffinement futur. Prouvé :testmail(LDAP) se connecte (/dashboard), et le module IcingaDB affiche la supervision (hôteicinga). Déploiementfailed=0(le rôle est bon ; les frictions étaient dans le simulateur de login curl : contrôle de cookie_checkCookie, champsuid/submit_login, valeur CSRF avantname). - Module BPM (Business Process) éprouvé + codifié — pile Icinga complète.
serveur_icingaweb2installe + activeicingaweb2-module-businessprocess(serveur_icingaweb2_modules), crée le répertoire des processus (éditable via l'UI, groupeicingaweb2, setgid) et sème des processus métier en IaC (serveur_icingaweb2_bpm_processes, nom → contenu.conf). Format des feuilleshost;service(éprouvé via les fixtures du module). Prouvé : un processus « Supervision Chezlepro » (agrège load/procs/swap/ping4/ssh du hosticingaen logique ET) rend un état dans l'UI (testmailconnecté), avec le backend IcingaDB (pas d'IDO). Rôle re-prouvé (reset → recrée le processus, idempotent). BPM n'est pas remplaçable par Grafana (roll-up d'impact métier). Pile Icinga = moteur + Web 2 + BPM, complète. - Cœur Icinga éprouvé (supervision active).
serveur_icinga(cœur :icinga2+icingadb+icingadb-redis) déployé sursup-01(🔧→⭐), baseicingadbPostgreSQL via le registre. Prouvé : 3 services actifs, et le moteur supervise — IcingaDB peuplée (1 hôte, 12 services, résultats de checks persistés en base). Zéro bug de déploiement (rôle bien bâti). Icinga Web 2 + module BPM restent différés (phases dédiées : UI native + vues d'impact métier ; le BPM n'est pas remplaçable par Grafana). Confirme aussi le DRYresoudre_basesur icinga (les 3 rôles consommateurs validés). - Forgejo branché au SSO OIDC (« Se connecter avec Chezlepro ») — 2e app SSO, prouvée.
serveur_forgejo(10.0.0) éprouvé surforge-01(🔧→⭐), adossé à PostgreSQL (baseforgejoauto-provisionnée depuis le registre), exposé par l'edge (vhost + cert SAN + A PowerDNS + alias plancher, tout auto-dérivé deexpose). Source OAuth2 vers Keycloak (forgejo admin auth add-oauth, idempotent, realmchezlepro), client OIDCforgejoenregistré viaserveur_keycloak_clients. Auto-enregistrement OIDC ([oauth2_client] ENABLE_AUTO_REGISTRATION+ALLOW_ONLY_EXTERNAL_REGISTRATION: identités depuis l'annuaire seulement). Prouvé (flux authorization code headless) :testmail(LDAP) se connecte, compte auto-créé (testmail@lab.chezlepro.internal), atterrit sur le tableau de bord. 5 bugs de 1er déploiement corrigés : dépendance périméeserveur_sendmail→serveur_postfix(le vrai MTA) ;app.inidoit appartenir au usergit(Forgejo persiste des secrets générés) ; ordre admin/migrations (flush_handlers+wait_foravantadmin user create) ;HTTP_ADDR127.0.0.1→0.0.0.0(l'edge nginx est sur un autre hôte, 502 sinon) ; auto-enregistrement OIDC. - DRY : rôle utilitaire partagé
resoudre_base(résolution BD depuis le registre). Le bloc copié-collé dansserveur_keycloak,serveur_forgejoetserveur_icinga(charger le registre, filtrer par consommateur, déréférencer le secret vialookup('vars', ...), résoudre hôte/port) est extrait dansroles/resoudre_base(factsresoudre_base_entree/db_password/db_host/db_port,no_log). Les 3 rôles l'incluent (include_role) et adoptent les facts. Le secret ne quitte toujours pas le rôle (déréférencé au déploiement). Fait « sur la preuve » : re-déploiement keycloak + forgejofailed=0, idempotent,testmailtoken Keycloak HTTP 200. Ferme le reste noté de la Phase 2 des bindings (cf.docs/bindings-conception.md). - PowerDNS — A d'exposition auto-dérivés (le DNS de la Phase 3 des bindings).
serveur_powerdnsgénère désormais, dans la zone interne, un enregistrement A pour chaque FQDN d'exposition (champexposedes applications) vers l'edge qui le sert (domaines.edge) — viaexpositions_des_applications(même source que les vhosts nginx et les SANs du cert edge). Déclarerexposeproduit maintenant vhost + SAN de cert + enregistrement DNS, tout dérivé. Prouvé :dig @infra-dns-01 grafana.lab.chezlepro.internaletkeycloak.…→192.168.15.21(edge). Optionserveur_powerdns_publier_expositions(défaut true). Limite / reste : PowerDNS est autoritatif, pas récursif — pour que les nœuds utilisent ces A sans casser la résolution Internet, il faut un récursif (pdns-recursor : forward de la zone interne + récursion du reste) ou garder le plancher/etc/hosts. Ne PAS repointer naïvementclient_dnsvers l'autoritatif. hosts_statiques— alias d'exposition dans le plancher/etc/hosts(résolution client, sûre). Le plancher pose désormais, sur chaque nœud,<IP edge> <FQDN exposé>pour chaqueexpose(dérivé dedomaines.edge, même source que nginx/PowerDNS). Indépendant du DNS, aucun risque de couper la résolution (choix retenu vs pdns-recursor). Chargement du plan best-effort (statdelegate_to: localhost+become: false— les registres vivent sur le nœud de contrôle ; ignoré si le plan est absent, ex. préparation du template). Prouvé :/etc/hostsd'obs-01 régénéré aveckeycloak/grafana→ edge (ligne manuelle éliminée),getentOK, et le flux SSO Grafana fonctionne via la résolution du plancher (login: testmail). Boucle Phase 3 fermée : déclarerexpose→ vhost + SAN cert + A PowerDNS + alias plancher, tout dérivé. Bugs corrigés en chemin :serveur_loki(groupelokimanquant),statsur cible→contrôle,becomeinutile sur le contrôle.- Rôle
client_unbound— résolveur local (DNS dynamique) : éprouvé sur un nœud. Unbound par nœud (127.0.0.1) avec stub-zone vers l'autoritatif interne (PowerDNS) + récursion Internet (ou forward viaclient_unbound_transitaires). Alternative dynamique au plancher/etc/hostsstatique, sans casser Internet. Bascule de/etc/resolv.confprotégée (client_unbound_apply+client_unbound_confirm) et validée AVANT (Unbound doit résoudre interne + Internet, sinon pas de bascule → nœud jamais coupé). Prouvé sur data-sql-01 (rayon d'impact minimal) :dig @127.0.0.1 keycloak/id-sso-01.lab.chezlepro.internal→ PowerDNS,deb.debian.org→ récursion,aptOK. Rôle sûr par défaut (apply: false: installe Unbound sans toucher au resolver). Rollout flotte = opt-in par nœud. Note direction : OPNsense embarque Unbound → à terme, l'Unbound réseau peut vivre sur l'appliance de bordure (nœud public, Étape B) ; le rôle par-nœud reste portable et complémentaire (cache local). - Bindings — Phase 1 : résolveur de liens dans
instancier.py(relations service→service déclaratives). Une application déclare sesliens: [{vers, role}]dansplan/applications.yml; chaque rôle décrit les liens qu'il accepte dansmeta/liens.yml(setops_liens.accepte, commemeta/empreinte.yml).instancierré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 instancierdonne 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/porteedansbases-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 danscatalogue-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é depuisarchitecture-set-ops.md.serveur_postgresqletserveur_keycloaképrouvés sur VM réelles (🔧→⭐). PostgreSQL déployé (data-sql-01), écoute réseau + pg_hba VLAN, et provisionne la basekeycloakdepuis le registre (bases-donnees.yml) — binding app→base prouvé en réel (base + rôle créés, mot de passe =vault_bd_keycloak). Keycloak 26.0.7 déployé (id-sso-01), mode prod, connecté à PostgreSQL (87 tables du realm master écrites), token admin obtenu (auth adossée à la BD). Lacunes connues (documentéescatalogue-services.md) : fédération LDAP et edge nginx pas encore câblés. Reste : DRY du bloc de résolution BD (keycloak/forgejo/icinga).serveur_keycloak— fédération LDAP (modèle d'identité A) : automatisée et prouvée. Le rôle configure, viakcadm(idempotent), un realm applicatif (serveur_keycloak_realm, déf.chezlepro) et un provider de stockage LDAP READ_ONLY vers OpenLDAP (LDAPS,uid/entryUUID,inetOrgPerson). TLS LDAPS validé via le truststore système (truststore-paths→/etc/ssl/certs/ca-certificates.crt, racine step_ca posée par client_pki, désormais requis sur le nœud). Secrets parenvironment+no_log. Éprouvé avant codification puis prouvé par le rôle : un utilisateur LDAP (testmail) obtient un token via le realm (HTTP 200), et le redéploiement est idempotent (changed=0). Nouveau :tasks/federation-ldap.yml. Reste : edge nginx (accès HTTPS par nom), mappers d'attributs/groupes fins.- Edge nginx + exposition (Phase 3 des bindings) — prouvés avec Keycloak. Sans changement de
code : la machinerie
serveur_nginx_publier_expositionsexistait déjà (litexposedes applications +edgededomaines.yml, dériveamont = http://<IP hôte>:<port>, génère le vhost avecX-Forwarded-*). Le bac à sable déclarekeycloak.expose: [keycloak.lab.chezlepro.internal]+ le domaine internelab.chezlepro.internal(edgeserveur_nginx). Prouvé : le vhost s'auto-génère (keycloak.lab.chezlepro.internal → http://192.168.15.81:8080), et la découverte OIDC via l'edge renvoie"issuer":"https://keycloak.lab.chezlepro.internal/..."(lesX-Forwardedpassent, Keycloak se sait derrière HTTPS). Limite connue : le cert TLS de l'edge est encore le snakeoil auto-signé (avertissement navigateur). Raffinement recommandé (réutilise l'existant, pas de nouveau mécanisme) : ajouter les FQDN d'exposition auxclient_pki_sansde l'edge (client_pki demande + renouvelle déjà le cert d'hôte), puis pointerserveur_nginx_certificatsur le cert client_pki (/etc/step/certs/<edge>.crt). - Cert de l'edge : snakeoil → step_ca (HTTPS valide). Appliqué le raffinement ci-dessus :
client_pkiajouté à l'edge, sesclient_pki_sansincluent le FQDN d'exposition (keycloak.lab.chezlepro.internal), etserveur_nginx_certificat/_clepointent sur le cert client_pki. Prouvé : HTTPSHTTP 200avecssl_verify_result=0(chaîne validée contre la racine step_ca, nom correct), émetteurSet-OPS Internal CA. Sans nouveau code (client_pki + group_var). Gaps notés : (1) recharger nginx au renouvellement du cert (le cert-renewer renouvelle en place, nginx ne recharge pas seul — hook à ajouter) ; (2) auto-dériver les SANs d'exposition de l'edge depuis le plan (au lieu de les lister dans le group_var). - Grafana branché au SSO OIDC (« Se connecter avec Chezlepro ») — prouvé de bout en bout.
serveur_grafana: config OIDC viaGF_AUTH_GENERIC_OAUTH_*(client confidentielgrafana, realmchezlepro, secretvault_grafana_oidc).serveur_loki+serveur_prometheus+serveur_grafanadéployés surobs-01(🔧→⭐). Bug de rôle corrigé :serveur_lokicréait le répertoire engroup: lokialors que le paquet crée l'utilisateur ennogroupsans groupeloki→ ajout de la création du groupe. Prouvé (flux authorization code headless, via l'edge HTTPS) :testmail(user LDAP) se connecte à Grafana par le SSO —/api/userrenvoielogin: testmail, email et nom fédérés depuis LDAP. Chaîne complète LDAP → Keycloak → Grafana. Gaps notés (pour rendre 100 % déclaratif) : (1) l'enregistrement du client OIDC dans Keycloak a été fait viakcadmà la main (à codifier — rôle grafana ou liste de clients côté keycloak) ; (2) la résolutionkeycloak.…internal → edgesur obs-01 est un/etc/hostsmanuel (PowerDNS devrait porter les A d'exposition — chaînon récurrent) ; (3) mapping de rôles Grafana (tous Viewer par défaut). serveur_keycloak— enregistrement des clients OIDC codifié (gap précédent fermé). Le rôle gère une liste déclarativeserveur_keycloak_clients(clientId,redirect_uris,web_origins,secret) et enregistre chaque client confidentiel via kcadm idempotent (tasks/clients-oidc.yml, create-si-absent,no_log). Décision : côté Keycloak (les creds admin restent dans le seul rôle Keycloak, pas répandus dans chaque rôle app) ; lesecretréférence la même variable de voûte que l'app. Prouvé : clientgrafanasupprimé → rôle → recréé →testmailse connecte à Grafana (login: testmail) ; redéploiement idempotent (changed=0). Le déploiement de Grafana au SSO est désormais autonome.
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 parinstancier.pyen variables Ansible ; chaque rôle décrit les liens qu'il accepte dansmeta/liens.yml(commemeta/empreinte.yml) ; FQDN cible dérivé de la nomenclature (jamais codé en dur). Domaines publics traités comme lienexposition(écrit sur l'edge). Réconcilie l'existant (basesconsommateur,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.
LICENSEremplacé par le texte officiel intégral de l'AGPLv3 (verbatim, non modifié). Attribution + modèle libre + services/certification documentés dans leREADME(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 IaCstalwart config applyannoncé 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ôleserveur_stalwartest retiré (git en garde la trace) ;docs/courriel-conception.mdmis à 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é viasmtpd_milters(optionserveur_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/mailroot 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'attributmail), 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. Configmain.cf+ carteldap-mailboxes.cf, validée parpostfix check. Secret de bind :vault_openldap_admin. Nécessiteserveur_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 (versserveur_openldap, filtremail), stockage Maildir (user systèmevmail), 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 pardoveconfau déploiement. Sockets d'intégration Postfix conditionnels (rendus si l'utilisateurpostfixest co-localisé). Prouvé :doveadm auth test— bon mot de passe accepté, mauvais refusé, sur cert step_ca. Secret de bind :vault_openldap_admin. serveur_openldapdurci 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 paropenldap(/etc/ldap/tls) par un script + une unitépathsystemd qui re-synchronise et recharge slapd à chaque renouvellement ;olcTLS*configuré danscn=config,SLAPD_SERVICESexposeldaps://. Dégrade proprement (slapd en clair local) siclient_pkin'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.
- TLS (LDAPS + STARTTLS) : le certificat d'hôte step_ca (déposé par
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é/actifdé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
creerréussi) ou qu'on déploie passe automatiquementactifdans 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.
- Sonde de vie : le GUI teste la joignabilité SSH de chaque hôte en arrière-plan
(
Corrigé
-
nginx ne validait pas sur Debian 13 (
server_tokensen double). Debian 13 livreserver_tokens off;actif dans/etc/nginx/nginx.conf(avant : commenté). Le drop-inconf.d/99-setops.confdu rôle le redéclarait →nginx -téchouait (« directive is duplicate ») et le déploiement plantait au handler de validation. Le rôleserveur_nginxneutralise 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échiffrergroup_vars/all/vault.ymlet échoue sans mot de passe (exit 4) — alors que l'opération est structurelle, sans secret.instancierutilise désormais automatiquement le fichier conventionnel~/.config/setops-vault-pass(siANSIBLE_VAULT_PASSWORD_FILEn'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 selonvault.exemple.ymlne 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 sesvault_*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--checkn'installe pas réellement → le service (ou le fichier de zone/conf) n'existe pas encore → fauxfatal, qui bloquait le déploiement (le dry-run doit réussir pour débloquer « Déployer »). Ajout dewhen: not ansible_check_modesur ces tâches et handlers des 13 rôlesserveur_*(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 appliquersans--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 →
🖥 Pousserclone 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 →
Pousserdéploie l'hôte porteur (make deployer HOTE=<hôte>). - Base →
Pousserdéploie l'hôte du serveur de BD (crée la base). Nouveaux modescreer/pousserdansexecuter_flux+ routes/api/creeret/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 soclehosts_statiques(dansserveur_debian) : génère/etc/hostssur 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_dnsest rendu tolérant (inerte si aucun DNS interne) et sa dépendance àserveur_powerdnspasse molle. - Adressage fédéré : index d'instance. Le VMID n'est plus codé
9CSNNen dur : il prend le préfixe d'unindexdéclaré en tête deplan/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-hoc172.19.x). Code mort retiré (deriveServeurJS). Voirdocs/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 symlinkinstancevers un autre dépôt d'instance (prod ↔ bac à sable) ;make instance-couranteaffiche 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.ymlde 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 durinventories/lab/inventories/production: il vise un inventaire par instance, détecté de façon rétro-compatible (principal>production>lab) et surchargeable parSETOPS_INVENTAIRE. Les instances existantes (découpage lab/production) continuent de fonctionner à l'identique ; les nouvelles peuvent adopterinventories/principal/. Deuxième pierre de la séparation par instance (la 1re étant le drapeausetops_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 drapeausetops_productiondansgroup_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)..gitignoredurci (**/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/laben dur (config Proxmox + voûte) — vestige du modèle env qui cassait les instancesprincipal. 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éclaraitlaunch+=bindque le paquetpdns-backend-bindpose déjà. Le rôle ne déclare pluslaunch(seulementbind-config).
- Clonage/Makefile codaient
chezlepro_timezonen'était appliqué nulle part. Cet intrant de base global était défini mais aucun rôle ne s'en servait. Le rôlechrony(appliqué à tout hôte viaserveur_debian) règle désormais le fuseau horaire à partir dechezlepro_timezone(chrony_timezonepar 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#messageet#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-5changent de vue,j/kparcourent les hôtes,v/dvérifient/déploient l'hôte sélectionné,/cible le filtre,Ctrl+Ssauvegarde 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
beforeunloadsi 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 (cataloguesproxmox_noeuds/proxmox_stockages, vide = défaut global) et Intégrations une rangée de cases à cocher des rôlesclient_*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 dansgroup_vars/proxmox.yml; l'API exposeintegrations_disponibles(scan deroles/client_*). Le typeliste(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, rendudessinerArbre, 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/inventairerenvoie 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, étatmodifie). - 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 depuismake helpetQUICKSTART.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 dedomaine_interne(clé de voûte) demande une confirmation explicite ; undomaine_internevide est refusé côté serveur. La nomenclature reste en lecture seule (modifiable dans le plan). Voirdocs/intrants-communs.mdetdocs/intrants-base-gui-conception.md. - Source unique d'identité partagée.
domaine_interneetchezlepro_timezonevivent désormais dansinventories/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 écritproxmox_coeurs/proxmox_memoire/proxmox_disque_taille; le clonage passecores/memoryà Proxmox (omitsi absent → aucune régression). Override par hôte possible dans le plan (serveurs.yml). Voirdocs/dimensionnement-ressources.md.
Corrigé
make instancier-appliquer FORCE=1n'honorait pas--force. La recette Makefile lançaitinstancier.py appliquersans relayerFORCE; 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 parmake, 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.