Set-OPS-Public/CHANGELOG.md
Daniel Allaire defe16d176 docs: bilan des pouvoirs de Set-OPS (docs/pouvoirs-set-ops.md)
Inventaire structuré des capacités du moteur : plan déclaratif,
plan de contrôle GUI, socle durci, piliers d'infrastructure prouvés
(PKI, identité, DNS, web, courriel), services outillés, patrons
d'ingénierie. Distingue « prouvé sur cluster réel » de « outillé ».

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 18:09:40 -04:00

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 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.
    • ApplicationPousser déploie l'hôte porteur (make deployer HOTE=<hôte>).
    • BasePousser 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.