alliance-boreale/docs/architecture/14_Structure_YAML_Registraire.md
Dan Allaire e2d5175975
Some checks are pending
CI / yaml-lint (push) Waiting to run
CI / ssot-export (push) Waiting to run
CI / tests (push) Waiting to run
CI / docs (push) Waiting to run
restructuration de la documentation
2025-10-24 10:50:25 -04:00

35 KiB

Document 14 : Structure YAML du Registraire

L'Alliance Boréale — Source de Vérité Technique

Version: 1.0
Date: 23 octobre 2025
Statut: Document technique
Adopté par : Cercle Opérationnel
Licence: CC BY-SA 4.0


PRÉAMBULE

Le Registraire est la source de vérité unique de L'Alliance Boréale.

Tout ce qui compte est dans le Registraire :

  • Membres (actifs, probation, sortis)
  • Services fédérés (DNS, email, Matrix)
  • Labels et scores
  • Décisions de gouvernance
  • Zones DNS

Format : YAML + Git

Pourquoi YAML ?

  • Lisible par humains (contrairement à JSON)
  • Éditable manuellement (pas besoin d'interface web)
  • Versionnable (Git track changes)
  • Validable (schemas, linters)
  • Parsable par machines (Ansible, scripts)

Pourquoi Git ?

  • Historique complet (qui a changé quoi, quand, pourquoi)
  • Pull requests (review avant merge)
  • Branches (test modifications avant prod)
  • Réversibilité (rollback si erreur)
  • Distribution (chaque membre peut cloner)

Principe : Une seule source de vérité, maintenue collectivement, auditable par tous.


SECTION 1 : STRUCTURE DU DÉPÔT GIT

1.1 Arborescence Complète

registraire/
├── README.md                        # Documentation générale
├── CONTRIBUTING.md                  # Guide contribution
├── LICENSE                          # CC BY-SA 4.0
│
├── schemas/                         # Schémas de validation
│   ├── member.schema.json           # JSON Schema fiche membre
│   ├── decision.schema.json         # JSON Schema décision
│   └── label.schema.json            # JSON Schema label
│
├── membres/                         # Fiches membres
│   ├── m001-chezlepro.yml           # Membre 001
│   ├── m002-technolibre.yml         # Membre 002
│   ├── m003-coop-nordique.yml       # Membre 003
│   └── ...
│
├── gouvernance/                     # Décisions & gouvernance
│   ├── decisions/
│   │   ├── 2025-001-admission-m001.yml
│   │   ├── 2025-002-admission-m002.yml
│   │   └── 2025-042-amendement-charte.yml
│   │
│   ├── cercles/
│   │   ├── strategique.yml          # Composition cercle
│   │   ├── operationnel.yml
│   │   └── ethique.yml
│   │
│   └── assemblees/
│       ├── 2025-01-ag-annuelle.yml  # PV assemblée générale
│       └── 2026-01-ag-annuelle.yml
│
├── labels/                          # Labellisation
│   ├── attributions/
│   │   ├── m001-2025-Q4.yml         # Label attribué
│   │   ├── m002-2025-Q4.yml
│   │   └── ...
│   │
│   └── audits/
│       ├── m001-2025-Q4-rapport.yml # Rapport audit (synthèse)
│       └── ...
│
├── dns/                             # Configuration DNS
│   ├── zones/
│   │   ├── boreal.ca.zone           # Zone racine
│   │   ├── chezlepro.boreal.ca.zone # Zones membres
│   │   └── technolibre.boreal.ca.zone
│   │
│   └── delegations/
│       └── delegations.yml          # Liste délégations actives
│
├── incidents/                       # Post-mortems
│   ├── 2025-10-incident-dns-p1.yml
│   └── 2025-11-incident-cert-p0.yml
│
└── banque-temps/                    # Transactions (si implémentée)
    └── transactions-2025-Q4.yml

1.2 Nomenclature des Fichiers

Règles strictes :

Type Format Exemple
Membre mXXX-[slug].yml m001-chezlepro.yml
Décision YYYY-NNN-[titre-court].yml 2025-042-amendement-charte.yml
Label mXXX-YYYY-QN.yml m001-2025-Q4.yml
Audit mXXX-YYYY-QN-rapport.yml m001-2025-Q4-rapport.yml
Incident YYYY-MM-incident-[titre]-[prio].yml 2025-10-incident-dns-p1.yml
Zone DNS [domain].zone chezlepro.boreal.ca.zone

Avantages :

  • Tri alphabétique = tri chronologique (pour décisions, incidents)
  • ID membre visible (mXXX)
  • Parsing facile (scripts)

1.3 Branches et Workflow Git

Branches :

main              → Production (fiches validées)
├── dev           → Développement (intégration continue)
└── feature/*     → Branches de travail (1 feature = 1 branch)
    ├── feature/add-member-m004
    ├── feature/update-label-m001
    └── feature/dns-delegation-m002

Workflow Pull Request :

  1. Créer branche : git checkout -b feature/add-member-m004
  2. Modifier fichier(s) : Éditer membres/m004-nouveau.yml
  3. Commit : git commit -m "feat: Add member m004 (Nouveau OBNL)"
  4. Push : git push origin feature/add-member-m004
  5. Ouvrir Pull Request sur Forge
  6. Review : 1+ membre (vérification conformité)
  7. CI/CD : Tests automatiques (yamllint, schema validation)
  8. Merge : Si tests + review → merge dans main

Protection branche main :

  • Aucun push direct (seulement via PR)
  • Review obligatoire (1+ personne)
  • CI/CD doit passer (tests)

SECTION 2 : SCHÉMA FICHE MEMBRE

2.1 Structure Complète (Commentée)

Fichier : membres/m001-chezlepro.yml

# ============================================================================
# FICHE MEMBRE — L'Alliance Boréale
# ============================================================================
# ID: m001
# Slug: chezlepro
# Nom légal: Chez le Pro Technologies Inc.
# Statut: actif
# Dernière mise à jour: 2025-10-23
# ============================================================================

# ----------------------------------------------------------------------------
# SECTION 1 : IDENTITÉ
# ----------------------------------------------------------------------------
member_id: m001
slug: chezlepro
status: active  # active | probation | suspended | exited

identity:
  legal_name: "Chez le Pro Technologies Inc."
  legal_form: corporation  # corporation | coop | npo | sole_proprietorship
  jurisdiction: QC  # Juridiction d'enregistrement
  registration_number: "1234567890"  # NEQ (Québec) ou équivalent
  
  operating_name: "Chez le Pro"  # Nom commercial (si différent)
  
  website: "https://chezle.pro"
  
  founded_date: "2018-03-15"
  joined_alliance_date: "2025-01-15"
  probation_end_date: "2025-04-15"  # null si membre actif confirmé
  
  description: |
    Chez le Pro offre des services d'hébergement web éthique et de 
    développement logiciel sur mesure pour PME québécoises. Membre 
    fondateur de L'Alliance Boréale.    

# ----------------------------------------------------------------------------
# SECTION 2 : CONTACTS
# ----------------------------------------------------------------------------
contacts:
  primary:
    name: "Jean Tremblay"
    role: "Président"
    email: "jean@chezle.pro"
    phone: "+1-514-555-0100"
    matrix: "@jean:matrix.chezlepro.boreal.ca"
  
  technical:
    name: "Marie Gagnon"
    role: "Directrice technique"
    email: "marie@chezle.pro"
    phone: "+1-514-555-0101"
    matrix: "@marie:matrix.chezlepro.boreal.ca"
  
  security:
    email: "security@chezle.pro"
    pgp_key: "0x1234567890ABCDEF"
    pgp_fingerprint: "1234 5678 90AB CDEF 1234 5678 90AB CDEF 1234 5678"
  
  legal:
    name: "Sophie Lavoie"
    role: "Conseillère juridique"
    email: "legal@chezle.pro"

# ----------------------------------------------------------------------------
# SECTION 3 : INFRASTRUCTURE
# ----------------------------------------------------------------------------
infrastructure:
  hosting:
    provider: "OVH Canada"
    datacenter_location: "Montreal, QC, Canada"
    owned: false  # true = hardware propre, false = loué
  
  network:
    ipv4_public:
      - "203.0.113.10/32"
      - "203.0.113.20/28"
    ipv6_public:
      - "2001:db8:1234::/48"
    
    ipv4_private: "10.1.0.0/16"  # Selon Doc 5
    
    asn: null  # Autonomous System Number (si applicable)
  
  dns:
    primary_ns: "ns1.chezlepro.boreal.ca"
    secondary_ns: "ns2.chezlepro.boreal.ca"
    
    ns_ips:
      ns1: "203.0.113.10"
      ns2: "203.0.113.11"
    
    dnssec_enabled: true
    dnssec_ds_records:
      - "12345 8 2 1234567890ABCDEF..."  # DS record pour parent
  
  servers:
    count: 3
    summary: |
      - 1x serveur compute (Proxmox)
      - 1x serveur storage (ZFS)
      - 1x serveur backup (dédié)      

# ----------------------------------------------------------------------------
# SECTION 4 : SERVICES PUBLICS
# ----------------------------------------------------------------------------
services:
  email:
    enabled: true
    domain: "chezle.pro"
    mx_records:
      - priority: 10
        host: "mail.chezlepro.boreal.ca"
    
    protocols:
      - smtp
      - smtps
      - imap
      - imaps
    
    security:
      dkim: true
      spf: true
      dmarc: true
      tls_min_version: "1.2"
  
  web:
    enabled: true
    urls:
      - "https://chezle.pro"
      - "https://www.chezle.pro"
    
    technologies:
      - nginx
      - php-fpm
      - postgresql
  
  cloud_storage:
    enabled: true
    platform: nextcloud
    url: "https://cloud.chezlepro.boreal.ca"
    version: "28.0.1"
  
  matrix:
    enabled: true
    homeserver_url: "https://matrix.chezlepro.boreal.ca"
    federation_enabled: true
    server_name: "chezlepro.boreal.ca"
  
  status_page:
    enabled: true
    url: "https://status.chezlepro.boreal.ca"
    platform: "upptime"  # upptime | cachet | custom
  
  monitoring:
    prometheus_endpoint: "https://prometheus.chezlepro.boreal.ca"
    grafana_url: "https://grafana.chezlepro.boreal.ca"
    public_metrics: true  # Métriques partagées avec Alliance

# ----------------------------------------------------------------------------
# SECTION 5 : STANDARDS ET CONFORMITÉ
# ----------------------------------------------------------------------------
standards:
  open_source_percentage: 85  # % de stack en logiciels libres
  
  protocols_supported:
    - smtp
    - imap
    - caldav
    - carddav
    - webdav
    - matrix
  
  certifications:
    - name: "ISO 27001"
      issued_by: "BSI Group"
      valid_until: "2026-12-31"
      certificate_url: "https://chezle.pro/certs/iso27001.pdf"
  
  compliance:
    loi_25: true  # Conformité Loi 25 (Québec)
    rgpd: true    # Conformité RGPD (EU)
    pipeda: true  # Conformité PIPEDA (Canada)

# ----------------------------------------------------------------------------
# SECTION 6 : LABEL DE PRESTIGE
# ----------------------------------------------------------------------------
label:
  current:
    level: gold  # bronze | silver | gold | platinum | null
    score_global: 82
    scores_by_domain:
      infrastructure: 85
      security: 88
      interoperability: 78
      open_source: 90
      operations: 75
      sustainability: 72
    
    issued_date: "2025-10-01"
    valid_until: "2026-10-01"
    audited_by: "m002"  # Membre auditeur
    
    report_url: "registraire/labels/audits/m001-2025-Q4-rapport.yml"
  
  history:
    - level: silver
      score_global: 68
      issued_date: "2025-04-01"
      valid_until: "2026-04-01"
    
    - level: bronze
      score_global: 52
      issued_date: "2025-01-15"
      valid_until: "2026-01-15"

# ----------------------------------------------------------------------------
# SECTION 7 : PARTICIPATION & CONTRIBUTIONS
# ----------------------------------------------------------------------------
participation:
  cercles:
    - name: "Cercle Stratégique"
      role: "membre"
      since: "2025-01-15"
    
    - name: "Cercle Opérationnel"
      role: "président"
      since: "2025-07-01"
      until: "2026-01-01"
  
  contributions:
    - type: "code"
      description: "Développement Registraire YAML (validation, CI/CD)"
      date: "2025-03-20"
      hours: 15
    
    - type: "documentation"
      description: "Rédaction runbooks DNS & DNSSEC"
      date: "2025-05-10"
      hours: 8
    
    - type: "mentoring"
      description: "Parrainage membre m003 (probation)"
      date: "2025-08-01"
      status: "en_cours"
  
  banque_temps:
    balance: 42  # Crédits (heures)
    last_updated: "2025-10-20"

# ----------------------------------------------------------------------------
# SECTION 8 : MÉTADONNÉES
# ----------------------------------------------------------------------------
metadata:
  created_at: "2025-01-15T10:30:00Z"
  updated_at: "2025-10-23T14:22:00Z"
  updated_by: "m001"  # Dernière modification par
  
  version: 2  # Numéro de version du schéma
  
  notes: |
    Membre fondateur. Infrastructure solide. Contributions régulières.
    Candidat potentiel Platine en 2026 si maintien niveau actuel.    
  
  tags:
    - fondateur
    - hebergement
    - quebec
    - infrastructure-mature

2.2 Champs Obligatoires vs Optionnels

Obligatoires (validation bloquante) :

  • member_id
  • slug
  • status
  • identity.legal_name
  • identity.legal_form
  • identity.joined_alliance_date
  • contacts.primary (name, email)
  • contacts.technical.email
  • contacts.security.email
  • infrastructure.dns.primary_ns
  • services.status_page.url

Recommandés (warning si absent) :

  • identity.website
  • infrastructure.dns.dnssec_enabled
  • services.email (si service email offert)
  • standards.open_source_percentage
  • label.current (si labellisé)

Optionnels (sans warning) :

  • infrastructure.network.asn
  • standards.certifications
  • participation.contributions
  • metadata.notes

2.3 Valeurs Enum (Contrôlées)

Statut membre (status) :

  • probation : En période probatoire (3 mois)
  • active : Membre actif confirmé
  • suspended : Suspendu temporairement
  • exited : Sorti de L'Alliance (volontaire ou radiation)

Forme légale (identity.legal_form) :

  • corporation : Entreprise incorporée
  • coop : Coopérative
  • npo : Organisme à but non lucratif
  • sole_proprietorship : Travailleur autonome

Niveau label (label.current.level) :

  • bronze
  • silver
  • gold
  • platinum
  • null : Non labellisé

Type contribution (participation.contributions.type) :

  • code : Contribution code (forge)
  • documentation : Rédaction wiki/runbooks
  • mentoring : Parrainage nouveau membre
  • audit : Audit pair-à-pair
  • governance : Participation cercle
  • infrastructure : Support technique mutuel

SECTION 3 : VALIDATION AUTOMATIQUE

3.1 YAML Lint (Syntaxe)

Outil : yamllint

Configuration : .yamllint

extends: default

rules:
  line-length:
    max: 120
    level: warning
  
  indentation:
    spaces: 2
  
  comments:
    min-spaces-from-content: 2
  
  document-start:
    present: true

Commande :

yamllint membres/*.yml

Résultat attendu :

membres/m001-chezlepro.yml
  0:0       warning  missing document start "---"  (document-start)

membres/m002-technolibre.yml
  0:0       warning  missing document start "---"  (document-start)

3.2 JSON Schema (Structure)

Schéma : schemas/member.schema.json

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://registraire.alliance-boreale.ca/schemas/member.schema.json",
  "title": "Alliance Boréale - Fiche Membre",
  "description": "Schéma de validation pour fiche membre",
  "type": "object",
  
  "required": [
    "member_id",
    "slug",
    "status",
    "identity",
    "contacts",
    "infrastructure",
    "services"
  ],
  
  "properties": {
    "member_id": {
      "type": "string",
      "pattern": "^m[0-9]{3}$",
      "description": "ID unique membre (format: mXXX)"
    },
    
    "slug": {
      "type": "string",
      "pattern": "^[a-z0-9-]{3,20}$",
      "description": "Slug textuel unique (3-20 car, minuscules)"
    },
    
    "status": {
      "type": "string",
      "enum": ["probation", "active", "suspended", "exited"]
    },
    
    "identity": {
      "type": "object",
      "required": ["legal_name", "legal_form", "joined_alliance_date"],
      "properties": {
        "legal_name": { "type": "string", "minLength": 3 },
        "legal_form": {
          "type": "string",
          "enum": ["corporation", "coop", "npo", "sole_proprietorship"]
        },
        "jurisdiction": { "type": "string", "pattern": "^[A-Z]{2}$" },
        "registration_number": { "type": "string" },
        "website": { "type": "string", "format": "uri" },
        "joined_alliance_date": { "type": "string", "format": "date" },
        "description": { "type": "string" }
      }
    },
    
    "contacts": {
      "type": "object",
      "required": ["primary", "technical", "security"],
      "properties": {
        "primary": {
          "type": "object",
          "required": ["name", "email"],
          "properties": {
            "name": { "type": "string" },
            "role": { "type": "string" },
            "email": { "type": "string", "format": "email" },
            "phone": { "type": "string" },
            "matrix": { "type": "string" }
          }
        },
        "technical": {
          "type": "object",
          "required": ["email"],
          "properties": {
            "name": { "type": "string" },
            "email": { "type": "string", "format": "email" }
          }
        },
        "security": {
          "type": "object",
          "required": ["email"],
          "properties": {
            "email": { "type": "string", "format": "email" },
            "pgp_key": { "type": "string" }
          }
        }
      }
    },
    
    "infrastructure": {
      "type": "object",
      "required": ["dns"],
      "properties": {
        "dns": {
          "type": "object",
          "required": ["primary_ns"],
          "properties": {
            "primary_ns": { "type": "string", "format": "hostname" },
            "secondary_ns": { "type": "string", "format": "hostname" },
            "dnssec_enabled": { "type": "boolean" }
          }
        }
      }
    },
    
    "services": {
      "type": "object",
      "required": ["status_page"],
      "properties": {
        "status_page": {
          "type": "object",
          "required": ["url"],
          "properties": {
            "enabled": { "type": "boolean" },
            "url": { "type": "string", "format": "uri" }
          }
        }
      }
    },
    
    "label": {
      "type": "object",
      "properties": {
        "current": {
          "type": "object",
          "required": ["level", "score_global", "issued_date", "valid_until"],
          "properties": {
            "level": {
              "type": "string",
              "enum": ["bronze", "silver", "gold", "platinum"]
            },
            "score_global": {
              "type": "integer",
              "minimum": 0,
              "maximum": 100
            },
            "scores_by_domain": {
              "type": "object",
              "properties": {
                "infrastructure": { "type": "integer", "minimum": 0, "maximum": 100 },
                "security": { "type": "integer", "minimum": 0, "maximum": 100 },
                "interoperability": { "type": "integer", "minimum": 0, "maximum": 100 },
                "open_source": { "type": "integer", "minimum": 0, "maximum": 100 },
                "operations": { "type": "integer", "minimum": 0, "maximum": 100 },
                "sustainability": { "type": "integer", "minimum": 0, "maximum": 100 }
              }
            },
            "issued_date": { "type": "string", "format": "date" },
            "valid_until": { "type": "string", "format": "date" }
          }
        }
      }
    }
  }
}

Validation :

# Outil: ajv-cli (JSON Schema validator)
ajv validate -s schemas/member.schema.json -d "membres/*.yml"

3.3 CI/CD Pipeline

Fichier : .gitlab-ci.yml (ou .github/workflows/validate.yml)

stages:
  - validate
  - test
  - deploy

# Job 1: Validation syntaxe YAML
yaml-lint:
  stage: validate
  image: python:3.11-alpine
  before_script:
    - pip install yamllint
  script:
    - yamllint membres/*.yml gouvernance/**/*.yml labels/**/*.yml
  only:
    - merge_requests
    - main

# Job 2: Validation structure (JSON Schema)
schema-validation:
  stage: validate
  image: node:18-alpine
  before_script:
    - npm install -g ajv-cli ajv-formats
  script:
    - ajv validate -s schemas/member.schema.json -d "membres/*.yml"
    - ajv validate -s schemas/decision.schema.json -d "gouvernance/decisions/*.yml"
    - ajv validate -s schemas/label.schema.json -d "labels/attributions/*.yml"
  only:
    - merge_requests
    - main

# Job 3: Vérification unicité slug/ID
uniqueness-check:
  stage: test
  image: python:3.11-alpine
  before_script:
    - pip install pyyaml
  script:
    - python scripts/check-uniqueness.py
  only:
    - merge_requests
    - main

# Job 4: Génération site web statique (optionnel)
generate-website:
  stage: deploy
  image: python:3.11-alpine
  before_script:
    - pip install pyyaml jinja2
  script:
    - python scripts/generate-website.py
    - mkdir -p public
    - cp -r website/* public/
  artifacts:
    paths:
      - public
  only:
    - main

Script scripts/check-uniqueness.py :

#!/usr/bin/env python3
"""Vérifier unicité des IDs et slugs membres."""

import sys
from pathlib import Path
import yaml

def check_uniqueness():
    members_dir = Path("membres")
    
    ids = []
    slugs = []
    
    for file in members_dir.glob("*.yml"):
        with open(file) as f:
            data = yaml.safe_load(f)
        
        member_id = data.get("member_id")
        slug = data.get("slug")
        
        if member_id in ids:
            print(f"❌ ERREUR: ID {member_id} dupliqué dans {file}")
            sys.exit(1)
        ids.append(member_id)
        
        if slug in slugs:
            print(f"❌ ERREUR: Slug {slug} dupliqué dans {file}")
            sys.exit(1)
        slugs.append(slug)
    
    print(f"✅ Validation OK: {len(ids)} membres, IDs et slugs uniques")
    return 0

if __name__ == "__main__":
    sys.exit(check_uniqueness())

SECTION 4 : PROCESSUS DE MISE À JOUR

4.1 Modification par le Membre Lui-Même

Cas d'usage : Mise à jour contact technique, ajout service, etc.

Processus :

  1. Cloner le dépôt

    git clone https://forge.boreal.ca/alliance/registraire.git
    cd registraire
    
  2. Créer branche

    git checkout -b update/m001-add-matrix-service
    
  3. Éditer fiche

    vim membres/m001-chezlepro.yml
    # Ajouter section services.matrix
    
  4. Valider localement

    yamllint membres/m001-chezlepro.yml
    ajv validate -s schemas/member.schema.json -d membres/m001-chezlepro.yml
    
  5. Commit

    git add membres/m001-chezlepro.yml
    git commit -m "feat(m001): Add Matrix federation service"
    
  6. Push et Pull Request

    git push origin update/m001-add-matrix-service
    # Ouvrir PR sur forge.boreal.ca
    
  7. Review + Merge

    • Cercle Opérationnel review (vérification cohérence)
    • CI/CD valide (tests automatiques)
    • Merge dans main

Délai typique : 1-3 jours.


4.2 Modification par Cercle Opérationnel

Cas d'usage : Changement statut, attribution label, décision gouvernance.

Processus similaire, mais :

  • Commit signé GPG (authentification)
  • Mention dans commit message : [CERCLE-OPS]
  • Review par 2+ membres du Cercle Opérationnel

Exemple :

git commit -S -m "[CERCLE-OPS] feat(m001): Promote to Gold label (Q4 2025)"

4.3 Modification Urgente (Incident)

Cas d'usage : Révocation certificat, changement IP urgent.

Processus accéléré :

  • Commit direct sur main autorisé (exception)
  • Notification Matrix #incidents immédiate
  • Justification dans commit message

Exemple :

git commit -m "fix(m001): Update DNS IP after datacenter migration (P1 incident)"
git push origin main
# Post dans Matrix: "🚨 Update urgent registraire m001 (migration datacenter)"

Post-mortem : Documentation de l'incident + amélioration du processus si nécessaire.


SECTION 5 : GÉNÉRATION DE VUES

5.1 Site Web Public

Concept : Générer site web statique à partir des YAML (lecture seule).

URL : https://registraire.alliance-boreale.ca

Pages générées :

  • / : Liste membres actifs
  • /membres/m001 : Fiche membre détaillée (infos publiques)
  • /labels : Tableau de bord labels
  • /decisions : Liste décisions publiques
  • /stats : Statistiques agrégées

Technologie : Python + Jinja2, ou Jekyll, ou Hugo.

Script scripts/generate-website.py (squelette) :

#!/usr/bin/env python3
"""Générer site web statique à partir des YAML."""

from pathlib import Path
import yaml
from jinja2 import Environment, FileSystemLoader

def generate():
    # Charger tous les membres
    members = []
    for file in Path("membres").glob("*.yml"):
        with open(file) as f:
            data = yaml.safe_load(f)
            if data["status"] in ["active", "probation"]:
                members.append(data)
    
    # Trier par joined_date
    members.sort(key=lambda m: m["identity"]["joined_alliance_date"])
    
    # Générer HTML
    env = Environment(loader=FileSystemLoader("templates"))
    template = env.get_template("index.html")
    
    html = template.render(members=members)
    
    Path("website").mkdir(exist_ok=True)
    with open("website/index.html", "w") as f:
        f.write(html)
    
    print(f"✅ Site généré: {len(members)} membres")

if __name__ == "__main__":
    generate()

Template templates/index.html :

<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <title>Registraire — L'Alliance Boréale</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <header>
        <h1>🌲 L'Alliance Boréale — Registraire Public</h1>
        <p>Fédération de services numériques éthiques</p>
    </header>
    
    <main>
        <section id="membres">
            <h2>Membres Actifs ({{ members|length }})</h2>
            
            <div class="member-grid">
            {% for member in members %}
                <article class="member-card">
                    <h3>{{ member.identity.legal_name }}</h3>
                    <p><strong>ID:</strong> {{ member.member_id }}</p>
                    <p><strong>Slug:</strong> {{ member.slug }}</p>
                    <p><strong>Statut:</strong> {{ member.status }}</p>
                    {% if member.label.current %}
                    <p><strong>Label:</strong> {{ member.label.current.level }} ({{ member.label.current.score_global }}/100)</p>
                    {% endif %}
                    <p><a href="{{ member.identity.website }}">Site web</a></p>
                </article>
            {% endfor %}
            </div>
        </section>
    </main>
    
    <footer>
        <p>Données source: <a href="https://forge.boreal.ca/alliance/registraire">Git</a></p>
        <p>Licence: CC BY-SA 4.0</p>
    </footer>
</body>
</html>

5.2 API REST (Optionnel)

Concept : Exposer données via API REST (lecture seule).

URL : https://api.registraire.alliance-boreale.ca

Endpoints :

GET /members              → Liste membres actifs
GET /members/{id}         → Fiche membre détaillée
GET /members/{id}/label   → Label actuel
GET /labels               → Tous les labels actifs
GET /decisions            → Décisions publiques
GET /stats                → Statistiques agrégées

Technologie : FastAPI (Python) ou Express (Node.js).

Réponse JSON (exemple) :

GET /members/m001

{
  "member_id": "m001",
  "slug": "chezlepro",
  "status": "active",
  "identity": {
    "legal_name": "Chez le Pro Technologies Inc.",
    "website": "https://chezle.pro"
  },
  "label": {
    "level": "gold",
    "score_global": 82,
    "issued_date": "2025-10-01",
    "valid_until": "2026-10-01"
  }
}

Avantages :

  • Intégration facilitée (scripts, apps)
  • Versioning API (v1, v2)
  • Rate limiting (prévention abus)

SECTION 6 : SÉCURITÉ ET PERMISSIONS

6.1 Accès en Lecture (Public)

Qui : Tout le monde (internet public).

Quoi :

  • Fiches membres (infos publiques)
  • Décisions publiées
  • Labels attribués
  • Zones DNS publiques

Comment : Git clone (HTTPS), site web, API.


6.2 Accès en Écriture (Restreint)

Qui :

  • Membres actifs (modification de LEUR fiche uniquement)
  • Cercle Opérationnel (modification toutes fiches + gouvernance)
  • Cercle Éthique (modification labels)

Authentification : SSH keys + GPG signing.

Permissions Git :

# Exemple .gitignore permissions (conceptuel, pas standard Git)
membres/m001-*.yml  → Éditable par m001 + Cercle Ops
membres/m002-*.yml  → Éditable par m002 + Cercle Ops
gouvernance/*       → Éditable par Cercle Stratégique + Ops
labels/*            → Éditable par Cercle Éthique uniquement
dns/*               → Éditable par Cercle Ops uniquement

Implémentation réelle : Forge (GitLab/Gitea) avec roles et protected branches.


6.3 Audit Trail (Traçabilité)

Git = Audit trail natif :

  • Chaque commit = auteur, date, message
  • Signature GPG = authentification cryptographique
  • Historique complet (git log)

Exemple :

git log --oneline --graph membres/m001-chezlepro.yml

* a3b4c5d (HEAD -> main) feat(m001): Update Matrix federation URL
* 9f8e7d6 feat(m001): Add Gold label (Q4 2025)
* 2c3d4e5 fix(m001): Correct technical contact email
* 1a2b3c4 feat: Add member m001 (Chez le Pro)

Requêtes utiles :

# Qui a modifié quoi ?
git log --all --author="jean@chezle.pro"

# Quand ce champ a-t-il changé ?
git log -p -S "mail.chezlepro.boreal.ca" membres/m001-chezlepro.yml

# Liste des commits entre deux dates
git log --since="2025-10-01" --until="2025-10-31"

CONCLUSION

Le Registraire YAML est simple mais puissant :

Lisible : N'importe qui peut lire un fichier YAML
Versionné : Git track tous les changements
Validé : Schemas + CI/CD garantissent cohérence
Auditable : Historique complet, signatures GPG
Extensible : Facile d'ajouter champs (rétrocompatible)
Distribué : Chaque membre peut cloner
Machine-readable : Scripts, Ansible, APIs

Ce n'est pas une base de données relationnelle complexe. C'est mieux : c'est Git.

La complexité ne vient pas de l'outil, mais de la rigueur des processus. Avec des conventions claires, des reviews systématiques, et des tests automatiques, le Registraire devient la colonne vertébrale informationnelle de L'Alliance.

Bienvenue dans le registraire boréal. 🌲


ANNEXES

Annexe A : Exemple Membre Complet (m002)

Fichier : membres/m002-technolibre.yml

---
member_id: m002
slug: technolibre
status: active

identity:
  legal_name: "TechnoLibre SENC"
  legal_form: coop
  jurisdiction: QC
  registration_number: "9876543210"
  website: "https://technolibre.ca"
  joined_alliance_date: "2025-02-01"
  probation_end_date: null
  description: |
    TechnoLibre est une coopérative de développeurs offrant du développement
    logiciel sur mesure et de l'hébergement souverain. Spécialisés en Odoo
    et Python/Django.    

contacts:
  primary:
    name: "Sophie Lavoie"
    role: "Présidente"
    email: "sophie@technolibre.ca"
    phone: "+1-418-555-0200"
    matrix: "@sophie:matrix.technolibre.boreal.ca"
  
  technical:
    name: "Marc Dubois"
    role: "Lead DevOps"
    email: "marc@technolibre.ca"
    matrix: "@marc:matrix.technolibre.boreal.ca"
  
  security:
    email: "security@technolibre.ca"
    pgp_key: "0xABCDEF1234567890"

infrastructure:
  hosting:
    provider: "OVH France"
    datacenter_location: "Strasbourg, France"
    owned: false
  
  network:
    ipv4_public:
      - "198.51.100.20/32"
    ipv4_private: "10.2.0.0/16"
  
  dns:
    primary_ns: "ns1.technolibre.boreal.ca"
    secondary_ns: "ns2.technolibre.boreal.ca"
    ns_ips:
      ns1: "198.51.100.20"
      ns2: "198.51.100.21"
    dnssec_enabled: false

services:
  email:
    enabled: true
    domain: "technolibre.ca"
    mx_records:
      - priority: 10
        host: "mail.technolibre.boreal.ca"
    protocols: [smtp, imap]
    security:
      dkim: true
      spf: true
      dmarc: true
  
  cloud_storage:
    enabled: true
    platform: nextcloud
    url: "https://cloud.technolibre.boreal.ca"
  
  status_page:
    enabled: true
    url: "https://status.technolibre.boreal.ca"
    platform: cachet

standards:
  open_source_percentage: 95
  protocols_supported: [smtp, imap, caldav, carddav, webdav]
  compliance:
    loi_25: true
    rgpd: true

label:
  current:
    level: silver
    score_global: 70
    issued_date: "2025-06-01"
    valid_until: "2026-06-01"
    audited_by: "m001"

participation:
  cercles:
    - name: "Cercle Stratégique"
      role: "membre"
      since: "2025-02-01"

metadata:
  created_at: "2025-02-01T09:00:00Z"
  updated_at: "2025-10-20T11:15:00Z"
  version: 2

Annexe B : Schéma Décision

Fichier : schemas/decision.schema.json (extrait)

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Décision de Gouvernance",
  "type": "object",
  "required": ["decision_id", "date", "circle", "type", "outcome"],
  "properties": {
    "decision_id": {
      "type": "string",
      "pattern": "^[0-9]{4}-[0-9]{3}$",
      "description": "Format: YYYY-NNN"
    },
    "date": { "type": "string", "format": "date" },
    "circle": {
      "type": "string",
      "enum": ["strategic", "operational", "ethics", "referents"]
    },
    "type": {
      "type": "string",
      "enum": ["admission", "policy", "budget", "label", "amendment", "other"]
    },
    "outcome": {
      "type": "string",
      "enum": ["approved", "rejected", "amended"]
    }
  }
}

Annexe C : Commandes Utiles

Recherche membre par slug :

grep -l "slug: chezlepro" membres/*.yml

Liste membres actifs :

grep -l "status: active" membres/*.yml | wc -l

Extraction infos avec yq :

# Installer: apt install yq
yq eval '.identity.legal_name' membres/m001-chezlepro.yml
# Output: Chez le Pro Technologies Inc.

yq eval '.label.current.level' membres/m001-chezlepro.yml
# Output: gold

Statistiques labels :

for file in membres/*.yml; do
  level=$(yq eval '.label.current.level // "none"' "$file")
  echo "$level"
done | sort | uniq -c

# Output:
#   5 gold
#   8 silver
#   3 bronze
#   2 none

MÉTADONNÉES

Document : 14_Structure_YAML_Registraire.md
Version : 1.0
Date de création : 23 octobre 2025
Auteur : Claude (profils #5 IaC, #12 Documentaliste)
Révision par : Cercle Opérationnel
Statut : À adopter par Cercle Stratégique
Longueur : ~6 000 mots (12 pages)
Licence : CC BY-SA 4.0

Sources utilisées :

  • 00_Glossaire_et_Definitions.md
  • 05_Architecture_Reference_Standards_Techniques.md
  • 03_Cadre_Conformite_Label_Prestige.md
  • devis_alliance_boreale_v2.md

Prochaine révision prévue : Octobre 2026 (après 1 an d'usage)


Changelog :

  • 2025-10-23 v1.0 : Création initiale Structure YAML Registraire

FIN DE LA STRUCTURE YAML

"La donnée bien structurée est la moitié du travail accompli."

🌲 L'Alliance Boréale
YAML + Git = Source de vérité vivante.