Rôle antispam sur edge-mta : Redis local, worker proxy en mode milter auto-scan (:11332), signature DKIM sortante (clé générée idempotente, enregistrement DNS affiché pour l'Étape B). Config par local.d/. Postfix branché via smtpd_milters (option serveur_postfix_rspamd_milter, milter_default_action=accept = tolérant si rspamd down). Prouvé : courriel traversant le milter → scanné (rspamc stat: 1) + signé DKIM (DKIM-Signature d=...), livré, lu en IMAP. Étape A (courriel interne) complète : dovecot + postfix + rspamd. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
25 KiB
CHANGELOG — Set-OPS
2026-07-02
Décidé
- 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é
- 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.