diff --git a/.github/workflows/check-pr.yml b/.github/workflows/check-pr.yml new file mode 100644 index 0000000..a6c944e --- /dev/null +++ b/.github/workflows/check-pr.yml @@ -0,0 +1,82 @@ +name: Vérification des pull requests (build, accessibilité et PDF) + +# Mêmes étapes de construction que le déploiement, sans déploiement ni secret : +# le site et les PDF du portail (livre blanc, statuts, règlement intérieur, charte) sont validés +# avant fusion, et les PDF joints en artefact pour relecture. + +on: + pull_request: + branches: ["main"] + +concurrency: + group: "check-pr-${{ github.event.pull_request.number }}" + cancel-in-progress: true + +permissions: + contents: read + +jobs: + check: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: pip + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt -r requirements-pdf.txt + sudo apt-get update + sudo apt-get install -y libpango-1.0-0 libpangoft2-1.0-0 poppler-utils + + - name: Build documentation + run: mkdocs build --strict + + # Accessibilité des pages HTML (WCAG 2 AA, moteur axe) : Chrome du runner, sans téléchargement + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: "22" + + - name: Check accessibility (pa11y, axe) + env: + PUPPETEER_SKIP_DOWNLOAD: "true" + CHROME: /usr/bin/google-chrome + run: | + npm install --no-save --no-audit --no-fund pa11y@9 + node scripts/check_a11y.js site + + - name: Export PDF + run: python scripts/export_pdf.py + + - name: Cache veraPDF + uses: actions/cache@v4 + with: + path: ~/.cache/verapdf + key: verapdf-1.30.2 + + - name: Validate PDF/UA-1 + run: bash scripts/check_pdf_ua.sh + + # Polices embarquées : Inter pour le texte ; toute autre police signale un glyphe absent d'Inter + - name: List embedded fonts + run: | + for pdf in $(find site -name 'otspi-*.pdf' | sort); do + echo "== $pdf" + pdffonts "$pdf" + done + + - name: Upload PDF + uses: actions/upload-artifact@v4 + with: + name: portail-pdf + path: site/**/otspi-*.pdf + retention-days: 14 diff --git a/.gitignore b/.gitignore index 5501d95..a0c6888 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ __pycache__/ *.py[cod] .venv/ .DS_Store +node_modules/ diff --git a/docs/adhesion/bulletin-adhesion.md b/docs/adhesion/bulletin-adhesion.md index 99d263a..b3a6156 100644 --- a/docs/adhesion/bulletin-adhesion.md +++ b/docs/adhesion/bulletin-adhesion.md @@ -11,7 +11,7 @@ Les demandes d'adhésion et dossiers complétés sont à transmettre à l'adress --- -## 1. Collèges et Conditions d'Adhésion +## 1. Collèges et conditions d'adhésion Conformément aux Articles 5, 5 bis et 5 quater des Statuts : @@ -34,10 +34,10 @@ Conformément aux Articles 5, 5 bis et 5 quater des Statuts : --- -## 2. Formulaire pour Personne Physique +## 2. Formulaire pour personne physique ```markdown -### Demande d'adhésion — Personne Physique +### Demande d'adhésion — personne physique - **Nom légal** : - **Prénom(s)** : @@ -75,10 +75,10 @@ Signature : --- -## 3. Formulaire pour Personne Morale +## 3. Formulaire pour personne morale ```markdown -### Demande d'adhésion — Personne Morale +### Demande d'adhésion — personne morale - **Raison sociale de l'organisation** : - **Forme juridique** (ex. Association, Fondation, SA, SAS, Établissement Public) : diff --git a/docs/administratif/declaration-prefecture.md b/docs/administratif/declaration-prefecture.md index 23c4359..b4ffae0 100644 --- a/docs/administratif/declaration-prefecture.md +++ b/docs/administratif/declaration-prefecture.md @@ -1,4 +1,4 @@ -# Guide des Démarches Administratives et Juridiques +# Guide des démarches administratives et juridiques **Création et Immatriculation de l'Association « OTSPI » (Loi 1901)** *« Open Trusted Service Provider Initiative »* @@ -9,7 +9,7 @@ Ce document récapitule la procédure légale et administrative pour enregistrer --- -## Étape 1 : Rédaction et Signature des Documents Constitutifs +## Étape 1 : rédaction et signature des documents constitutifs Avant de procéder à la déclaration, réunir les documents suivants paraphés et signés : 1. **Les Statuts** : [statuts-association.md](../statuts/statuts-association.md) complétés avec l'adresse du siège social dans la Métropole de Lyon, datés du jour de l'assemblée générale constitutive et signés par les dirigeants fondateurs (Président(e) et Trésorier(e)). @@ -18,7 +18,7 @@ Avant de procéder à la déclaration, réunir les documents suivants paraphés --- -## Étape 2 : Déclaration en Préfecture (Télédéclaration e-création) +## Étape 2 : déclaration en préfecture (télédéclaration e-création) La déclaration se réalise de manière dématérialisée sur le portail officiel de l'administration française : @@ -34,7 +34,7 @@ La déclaration se réalise de manière dématérialisée sur le portail officie --- -## Étape 3 : Récépissé de Déclaration et Numéro RNA +## Étape 3 : récépissé de déclaration et numéro RNA - Dans un délai généralement compris entre **24 heures et 5 jours ouvrés**, la Préfecture du Rhône / Métropole de Lyon valide le dossier. - L'administration délivre un **Récépissé de Déclaration de Création**. @@ -42,7 +42,7 @@ La déclaration se réalise de manière dématérialisée sur le portail officie --- -## Étape 4 : Publication au Journal Officiel (JOAFE) +## Étape 4 : publication au Journal officiel (JOAFE) - La publication au **Journal Officiel des Associations et Fondations d'Entreprise (JOAFE)** est automatique et gratuite. - La préfecture transmet directement l'avis de publication à la Direction de l'Information Légale et Administrative (DILA). @@ -52,7 +52,7 @@ La déclaration se réalise de manière dématérialisée sur le portail officie --- -## Étape 5 : Obtention des Numéros SIREN et SIRET (INSEE) +## Étape 5 : obtention des numéros SIREN et SIRET (INSEE) Pour ouvrir un compte bancaire associatif, souscrire aux polices de garantie financière et d'assurance responsabilité civile professionnelle, employer du personnel ou recevoir des financements et mécénats, l'association doit disposer d'un numéro SIRET. @@ -66,7 +66,7 @@ Pour ouvrir un compte bancaire associatif, souscrire aux polices de garantie fin --- -## Étape 6 : Ouverture du Compte Bancaire et Fonds de Réserve +## Étape 6 : ouverture du compte bancaire et fonds de réserve Une fois les identifiants officiels obtenus : - Ouverture du compte courant d'exploitation auprès d'un établissement bancaire ; diff --git a/docs/assets/fonts/inter/OFL.txt b/docs/assets/fonts/inter/OFL.txt new file mode 100644 index 0000000..40589da --- /dev/null +++ b/docs/assets/fonts/inter/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2016 The Inter Project Authors (https://github.com/rsms/inter) Inter-Italic[opsz,wght].ttf: Copyright 2016 The Inter Project Authors (https://github.com/rsms/inter) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/docs/assets/fonts/inter/inter-latin-300-normal.woff2 b/docs/assets/fonts/inter/inter-latin-300-normal.woff2 new file mode 100644 index 0000000..ece952c Binary files /dev/null and b/docs/assets/fonts/inter/inter-latin-300-normal.woff2 differ diff --git a/docs/assets/fonts/inter/inter-latin-400-italic.woff2 b/docs/assets/fonts/inter/inter-latin-400-italic.woff2 new file mode 100644 index 0000000..9e98286 Binary files /dev/null and b/docs/assets/fonts/inter/inter-latin-400-italic.woff2 differ diff --git a/docs/assets/fonts/inter/inter-latin-400-normal.woff2 b/docs/assets/fonts/inter/inter-latin-400-normal.woff2 new file mode 100644 index 0000000..f15b025 Binary files /dev/null and b/docs/assets/fonts/inter/inter-latin-400-normal.woff2 differ diff --git a/docs/assets/fonts/inter/inter-latin-600-normal.woff2 b/docs/assets/fonts/inter/inter-latin-600-normal.woff2 new file mode 100644 index 0000000..d189794 Binary files /dev/null and b/docs/assets/fonts/inter/inter-latin-600-normal.woff2 differ diff --git a/docs/assets/fonts/inter/inter-latin-700-italic.woff2 b/docs/assets/fonts/inter/inter-latin-700-italic.woff2 new file mode 100644 index 0000000..48b6e5b Binary files /dev/null and b/docs/assets/fonts/inter/inter-latin-700-italic.woff2 differ diff --git a/docs/assets/fonts/inter/inter-latin-700-normal.woff2 b/docs/assets/fonts/inter/inter-latin-700-normal.woff2 new file mode 100644 index 0000000..a68fb10 Binary files /dev/null and b/docs/assets/fonts/inter/inter-latin-700-normal.woff2 differ diff --git a/docs/assets/fonts/inter/inter-latin-800-normal.woff2 b/docs/assets/fonts/inter/inter-latin-800-normal.woff2 new file mode 100644 index 0000000..74a16d4 Binary files /dev/null and b/docs/assets/fonts/inter/inter-latin-800-normal.woff2 differ diff --git a/docs/assets/logo-vertical-dark.svg b/docs/assets/logo-vertical-dark.svg index 93009c3..ceab211 100644 --- a/docs/assets/logo-vertical-dark.svg +++ b/docs/assets/logo-vertical-dark.svg @@ -1,11 +1,5 @@ - - - - - - + fill-rule="evenodd" /> + + @@ -29,10 +25,10 @@ stroke-width="14.40" stroke-linecap="round" stroke-linejoin="round" /> - + OTSPI - + OPEN TRUSTED SERVICE PROVIDER INITIATIVE diff --git a/docs/assets/og/og-portail.png b/docs/assets/og/og-portail.png new file mode 100644 index 0000000..73b250f Binary files /dev/null and b/docs/assets/og/og-portail.png differ diff --git a/docs/cadrage/cp-cps-cadre.md b/docs/cadrage/cp-cps-cadre.md index a4d28ed..e5b2ce8 100644 --- a/docs/cadrage/cp-cps-cadre.md +++ b/docs/cadrage/cp-cps-cadre.md @@ -1,4 +1,4 @@ -# Cadre Général des Politiques de Certification et Pratiques (CP/CPS) +# Cadre général des politiques de certification et pratiques (CP/CPS) ## Conforme à la RFC 3647, ETSI EN 319 401, ETSI EN 319 411-1 / 411-2 et WebTrust **Association « Open Trusted Service Provider Initiative » (OTSPI)** @@ -25,7 +25,7 @@ Le présent document définit les Politiques de Certification (*Certificate Poli --- -## 2. Répertoires de Publication et Référentiels +## 2. Répertoires de publication et référentiels ### 2.1. Répertoires publics OTSPI maintient un répertoire accessible publiquement, de façon continue (24h/24, 7j/7), neutre et gratuite, contenant : @@ -39,7 +39,7 @@ La disponibilité des services de publication de révocation (CRL/OCSP) fait l'o --- -## 3. Identification et Authentification (I&A) +## 3. Identification et authentification (I&A) ### 3.1. Enregistrement initial - **Officiers d'Autorité et Gardiens de clés** : Identification civile formelle en présence physique ou par PVID qualifié, vérification du casier judiciaire, serment déontologique (Article 5 du Règlement Intérieur). @@ -50,7 +50,7 @@ Tous les accès administratifs et d'émission requièrent des clés matérielles --- -## 4. Exigences Opérationnelles du Cycle de Vie des Certificats +## 4. Exigences opérationnelles du cycle de vie des certificats ### 4.1. Demande et émission de certificats Toute demande de certificat suit un processus automatisé ou validé par un Officier d'Autorité selon le profil d'usage. L'émission est signée exclusivement au sein d'un module matériel de sécurité (HSM) qualifié. @@ -62,7 +62,7 @@ Toute demande de certificat suit un processus automatisé ou validé par un Offi --- -## 5. Contrôles de Sécurité Physique, Environnementale et Procédurale +## 5. Contrôles de sécurité physique, environnementale et procédurale ### 5.1. Sécurité physique des sites d'hébergement Les composants serveurs et modules HSM sont hébergés au sein de datacenters certifiés ISO/IEC 27001 et qualifiés SecNumCloud (ou équivalent européen souverain), situés exclusivement sur le territoire de l'Union Européenne : @@ -75,7 +75,7 @@ Toute opération sur les HSM ou sur les clés racines (génération, sauvegarde, --- -## 6. Contrôles Techniques de Sécurité et Cycle de Vie des Clés +## 6. Contrôles techniques de sécurité et cycle de vie des clés ### 6.1. Modules matériels de sécurité (HSM) - Les clés privées racines et intermédiaires sont générées et stockées exclusivement au sein de modules HSM certifiés **Common Criteria EAL 4+ (profil de protection EN 419 221-5)** ou **FIPS 140-2 / 140-3 Niveau 3**. @@ -94,7 +94,7 @@ Conformément aux référentiels ETSI TS 119 312 et aux recommandations de l'ANS --- -## 7. Profils de Certificats, de CRL et d'Horodatage +## 7. Profils de certificats, de CRL et d'horodatage 1. **Profils X.509 v3** : Conformes aux normes IETF RFC 5280 et profils ETSI EN 319 412 (parties 1 à 5). 2. **Profils d'horodatage qualifié** : Conformes à la RFC 3161 et ETSI EN 319 421 / 422. @@ -102,7 +102,7 @@ Conformément aux référentiels ETSI TS 119 312 et aux recommandations de l'ANS --- -## 8. Audit de Conformité et Évaluation +## 8. Audit de conformité et évaluation 1. **Audits internes périodiques** : Réalisés sous la responsabilité de l'Auditeur Interne indépendant nommé en coordination avec le CPC. 2. **Audits externes d'accréditation** : Conduits au moins tous les deux (2) ans (avec audits de surveillance annuels) par un organisme d'évaluation de la conformité (*Conformity Assessment Body - CAB*) accrédité selon la norme ISO/IEC 17065 et ETSI EN 319 403 / 403-1. @@ -110,7 +110,7 @@ Conformément aux référentiels ETSI TS 119 312 et aux recommandations de l'ANS --- -## 9. Dispositions Légales, Responsabilités et Droit Applicable +## 9. Dispositions légales, responsabilités et droit applicable 1. **Inaliénabilité des clés et séquestre** : Les clés privées sont insaisissables et sous séquestre technique exclusif (Article 8 ter des Statuts). 2. **Garanties financières et RC Pro** : Couverture par le fonds de réserve opérationnelle (Article 12 bis des Statuts) et police d'assurance responsabilité civile professionnelle souscrite par l'association. diff --git a/docs/cadrage/index.md b/docs/cadrage/index.md index f03d2d2..8ef9f25 100644 --- a/docs/cadrage/index.md +++ b/docs/cadrage/index.md @@ -1,4 +1,4 @@ -# Documents de Cadrage Stratégique et Socle de Conformité Initial (TSP / PKI) +# Documents de cadrage stratégique et socle de conformité initial (TSP / PKI) **Association « Open Trusted Service Provider Initiative » (OTSPI)** @@ -6,7 +6,7 @@ Ce dossier rassemble l'ensemble des documents de cadrage stratégique, réglemen --- -## 🏛️ Le Socle de Conformité Initial (Corpus d'Audit) +## Le socle de conformité initial (corpus d'audit) 1. **[Cadre Général des Politiques de Certification et Pratiques (CP/CPS)](cp-cps-cadre.md)** : - Document maître conforme à la **RFC 3647**, à l'**ETSI EN 319 401**, à l'**ETSI EN 319 411-1 / 411-2** et aux exigences **WebTrust / CA/Browser Forum** ; @@ -28,7 +28,7 @@ Ce dossier rassemble l'ensemble des documents de cadrage stratégique, réglemen --- -## 🧭 Documents Complémentaires de Cadrage +## Documents complémentaires de cadrage - **[Livre blanc](../livre-blanc/index.md)** : vision et modèle d'intérêt général à but non lucratif inspiré de l'ISRG / Let's Encrypt appliqué aux services qualifiés eIDAS. - **[Manifeste pour une identité numérique libre et ouverte](https://www.otspi.org/manifeste.html)** : dix principes ouverts à la signature des personnes et des organisations. @@ -37,7 +37,7 @@ Ce dossier rassemble l'ensemble des documents de cadrage stratégique, réglemen --- -## 🔒 Principes d'Auditabilité et Sécurité +## Principes d'auditabilité et de sécurité - Tous les documents de cadrage normatifs sont publics et consultables en libre accès dans le cadre de la redevabilité publique intégrale ; - Les seules exceptions concernent les secrets cryptographiques matériels (clés sous séquestre HSM) et les embargos temporaires de sécurité sur les vulnérabilités non corrigées (*Coordinated Vulnerability Disclosure*). diff --git a/docs/cadrage/pssi.md b/docs/cadrage/pssi.md index d332ab3..a5d17d4 100644 --- a/docs/cadrage/pssi.md +++ b/docs/cadrage/pssi.md @@ -1,4 +1,4 @@ -# Politique de Sécurité des Systèmes d'Information (PSSI) +# Politique de sécurité des systèmes d'information (PSSI) ## Conforme aux normes ISO/IEC 27001 et ETSI EN 319 401 **Association « Open Trusted Service Provider Initiative » (OTSPI)** @@ -6,7 +6,7 @@ --- -## 1. Objectifs, Périmètre et Engagement de la Direction +## 1. Objectifs, périmètre et engagement de la direction ### 1.1. Objectifs de la PSSI La présente Politique de Sécurité des Systèmes d'Information (PSSI) a pour objet de garantir : @@ -19,16 +19,16 @@ La PSSI s'applique à : - L'ensemble des systèmes d'information, réseaux, serveurs, conteneurs et modules matériels HSM ; - Tous les administrateurs, membres du Bureau, membres du CPC, Officiers d'Autorité, Gardiens de clés et salariés ou prestataires intervenant sur l'infrastructure. -### 1.3. Engagement de la Direction Opérationnelle +### 1.3. Engagement de la direction opérationnelle Le Bureau de l'association (*Executive Management* au sens de l'ETSI EN 319 401) s'engage à allouer les ressources humaines, matérielles et financières nécessaires à l'application et au contrôle continu de cette politique. --- -## 2. Organisation de la Sécurité et Rôles Clés +## 2. Organisation de la sécurité et rôles clés Conformément à la séparation stricte des fonctions imposée par l'ETSI et WebTrust : -### 2.1. Le Responsable de la Sécurité des Systèmes d'Information (RSSI / CISO) +### 2.1. Le responsable de la sécurité des systèmes d'information (RSSI / CISO) - **Nomination et indépendance** : Nommé par le Comité des Politiques de Confiance (CPC) pour un mandat d'un (1) an renouvelable (Article 8 bis.3 des Statuts). - **Missions** : * Piloter le Système de Management de la Sécurité de l'Information (SMSI) ; @@ -36,21 +36,21 @@ Conformément à la séparation stricte des fonctions imposée par l'ETSI et Web * Superviser la gestion des incidents et coordonner la divulgation des vulnérabilités (CVD) ; * Disposer d'une ligne de reporting direct auprès du Bureau et du CPC, en totale indépendance des équipes d'exploitation courante. -### 2.2. L'Auditeur Interne Indépendant +### 2.2. L'auditeur interne indépendant - Désigné en concertation entre le CPC et le Conseil d'Administration. - Ne peut exercer aucune fonction au sein du Bureau, ni aucune tâche opérationnelle d'administration système ou d'Officier d'Autorité. - Réalise des audits de conformité semestriels et supervise l'audit contradictoire des scellés des Gardiens de clés. -### 2.3. Les Équipes d'Exploitation et d'Ingénierie +### 2.3. Les équipes d'exploitation et d'ingénierie - Chargées de l'administration système, du déploiement des briques logicielles libres (TSA, PKI, validateurs) et de la maintenance opérationnelle. - Opèrent sous le principe du moindre privilège (*Least Privilege*) et ne possèdent aucun accès direct aux clés privées racines en clair. -### 2.4. Les Officiers d'Autorité et Gardiens de Clés +### 2.4. Les officiers d'autorité et gardiens de clés - Rôles de confiance soumis à habilitation préalable par le CPC, assermentation éthique et vérification d'identité (Articles 5 et 7 du Règlement Intérieur). --- -## 3. Gestion des Actifs et Contrôles d'Accès +## 3. Gestion des actifs et contrôles d'accès ### 3.1. Classification des informations Les actifs d'information sont classifiés en 4 niveaux : @@ -66,7 +66,7 @@ Les actifs d'information sont classifiés en 4 niveaux : --- -## 4. Sécurité Physique et Opérationnelle +## 4. Sécurité physique et opérationnelle ### 4.1. Hébergement des infrastructures - Les modules matériels de sécurité (HSM) et serveurs d'autorité sont implantés dans des centres de données hautement sécurisés situés dans l'Union Européenne, qualifiés SecNumCloud ou certifiés ISO 27001. @@ -79,7 +79,7 @@ Les actifs d'information sont classifiés en 4 niveaux : --- -## 5. Gestion des Incidents de Sécurité et Continuité d'Activité +## 5. Gestion des incidents de sécurité et continuité d'activité ### 5.1. Détection et journalisation (*Logging & Monitoring*) - Journalisation centralisée et horodatée des événements de sécurité sur un serveur de logs dédié en écriture seule (WORM / *Write Once Read Many*) ; @@ -97,6 +97,6 @@ Les actifs d'information sont classifiés en 4 niveaux : --- -## 6. Révision et Amélioration Continue +## 6. Révision et amélioration continue La présente PSSI fait l'objet d'une revue annuelle obligatoire par le RSSI, le Bureau et le CPC, ainsi qu'à la suite de tout incident majeur ou évolution réglementaire substantielle. diff --git a/docs/cadrage/termination-plan.md b/docs/cadrage/termination-plan.md index 4bb39bd..ddff6b5 100644 --- a/docs/cadrage/termination-plan.md +++ b/docs/cadrage/termination-plan.md @@ -1,12 +1,12 @@ -# Plan de Fin d'Activité et de Cessation des Services de Confiance -## (*Termination Plan* conforme à l'article 24.2.e du Règlement eIDAS et à l'ETSI EN 319 401 § 7.12) +# Plan de fin d'activité et de cessation des services de confiance +## (*Termination Plan* conforme à l'article 24.2.e du règlement eIDAS et à l'ETSI EN 319 401 § 7.12) **Association « Open Trusted Service Provider Initiative » (OTSPI)** *Approuvé par le Bureau et le Comité des Politiques de Confiance (CPC).* --- -## 1. Contexte, Objet et Cadre Légal +## 1. Contexte, objet et cadre légal Conformément à l'**article 24 paragraphe 2 point e) du Règlement (UE) n° 910/2014 (eIDAS)** et à la clause 7.12 de la norme **ETSI EN 319 401**, tout prestataire de services de confiance qualifié doit maintenir un plan à jour garantissant la continuité ou la cessation ordonnée de ses activités en cas de liquidation, de faillite ou d'arrêt programmé de service. @@ -18,7 +18,7 @@ Le présent document formalise le **Plan de Fin d'Activité (*Termination Plan*) --- -## 2. Déclenchement du Plan de Fin d'Activité +## 2. Déclenchement du plan de fin d'activité Le plan de fin d'activité est déclenché dans les cas suivants : - Décision volontaire de cessation de service votée par l'Assemblée Générale Extraordinaire (Article 10 et 11 des Statuts) ; @@ -27,7 +27,7 @@ Le plan de fin d'activité est déclenché dans les cas suivants : --- -## 3. Procédure Opérationnelle de Notification et Délais +## 3. Procédure opérationnelle de notification et délais Dès la décision ou le constat de cessation d'activité : @@ -42,7 +42,7 @@ Dès la décision ou le constat de cessation d'activité : --- -## 4. Révocation des Autorités et Révocation des Certificats +## 4. Révocation des autorités et révocation des certificats 1. **Arrêt immédiat des nouvelles émissions** : Dès la date d'effet de la fin de service, aucune nouvelle clé ou certificat ne peut être émis. 2. **Génération d'une CRL finale complète** : @@ -54,7 +54,7 @@ Dès la décision ou le constat de cessation d'activité : --- -## 5. Maintien des Annuaires de Révocation et Validateurs (CRL / OCSP) +## 5. Maintien des annuaires de révocation et validateurs (CRL / OCSP) Même après l'arrêt d'émission, l'accès public aux données d'état de révocation doit être assuré pour toute la durée de validité résiduelle des certificats émis : 1. **Périmètre temporel** : Les listes de révocation finales (CRL) et validateurs d'état restent consultables publiquement pendant au moins **dix (10) ans** suivant la cessation de l'activité. @@ -63,7 +63,7 @@ Même après l'arrêt d'émission, l'accès public aux données d'état de révo --- -## 6. Archivage Probatoire à Long Terme des Journaux d'Audit (10 à 30 ans) +## 6. Archivage probatoire à long terme des journaux d'audit (10 à 30 ans) Conformément à l'ETSI EN 319 401 et aux exigences réglementaires de conservation de la preuve électronique : 1. **Données à archiver** : @@ -77,7 +77,7 @@ Conformément à l'ETSI EN 319 401 et aux exigences réglementaires de conservat --- -## 7. Convention de Séquestre Financier et Fonds de Garantie (Article 12 bis des Statuts) +## 7. Convention de séquestre financier et fonds de garantie (article 12 bis des statuts) Pour garantir que la fin d'activité ne sera pas compromise par une défaillance financière : 1. **Sanctuarisation du Fonds de Réserve Opérationnelle** : diff --git a/docs/gouvernance/charte-ethique.md b/docs/gouvernance/charte-ethique.md index 08596b0..01d2c79 100644 --- a/docs/gouvernance/charte-ethique.md +++ b/docs/gouvernance/charte-ethique.md @@ -1,11 +1,15 @@ -# Charte d'Éthique, de Déontologie et de Transparence Publique +--- +pdf: otspi-charte-ethique.pdf +--- + +# Charte d'éthique, de déontologie et de transparence publique **Association « Open Trusted Service Provider Initiative » (OTSPI)** *Organisation d'Intérêt Général et à Gestion Désintéressée* --- -## 1. Vision et Raison d'Être : La Confiance Numérique comme Bien Commun +## 1. Vision et raison d'être : la confiance numérique comme bien commun L'association **« Open Trusted Service Provider Initiative » (OTSPI)** a pour but d'intérêt général de lever les barrières économiques, techniques, administratives et éducatives à la sécurité, à la confidentialité et à la confiance numérique dans les communications électroniques mondiales. @@ -15,32 +19,32 @@ L'association est régie par les principes d'**intérêt général**, de **gesti --- -## 2. Principes Directeurs d'Ordre Public Interne +## 2. Principes directeurs d'ordre public interne -### A. Redevabilité Publique Intégrale (*Full Public Accountability*) +### A. Redevabilité publique intégrale (*Full Public Accountability*) - **Transparence des processus** : Toutes les réunions statutaires, délibérations du Conseil d'Administration, résolutions normatives, documents de cadrage, feuilles de route (*roadmaps*), budgets prévisionnels et comptes financiers sont publiés en libre accès public (*open access*). - **Code source et spécifications ouvertes** : Tous les logiciels d'infrastructure sont sous licence libre ou ouverte. Les rapports d'évaluation d'audit de conformité (normes ETSI EN 319 401, EN 319 411 / 421 / 422, WebTrust, eIDAS) sont rendus publics. - **Les deux seules réserves protégées** : 1. **Protection stricte de la vie privée et conformité RGPD** : Anonymisation ou pseudonymisation préalable des données personnelles des membres, votants, donateurs et contributeurs avant publication (Règlement UE 2016/679). 2. **Sécurité opérationnelle et divulgation coordonnée de vulnérabilités (*Coordinated Vulnerability Disclosure*)** : Confidentialité temporaire sous embargo des rapports de vulnérabilités le temps de la correction et du déploiement, et secret technique absolu des clés privées et secrets matériels protégés au sein des modules HSM qualifiés. -### B. Gestion Strictement Désintéressée et Inaliénabilité +### B. Gestion strictement désintéressée et inaliénabilité - **Bénévolat intégral des mandats** : Conformément à l'Article 12 des Statuts, les fonctions d'administrateurs au Conseil d'Administration et de membres du Bureau sont exercées à titre strictement bénévole. Aucun membre ne perçoit de rémunération, dividende ou avantage matériel direct ou indirect. - **Séparation étanche avec les salariés** : Les éventuels salariés ou prestataires techniques de l'association ne peuvent siéger ni au Bureau ni au Conseil d'Administration. - **Inaliénabilité perpétuelle (Article 13 des Statuts)** : L'association s'interdit perpétuellement toute transformation lucrative. Ses dépôts logiciels libres, marques et infrastructures sont inaliénables. En cas de dissolution, l'actif net subsistant après exécution complète du plan de terminaison d'activité (*Termination Plan*) est obligatoirement dévolu à un organisme d'intérêt général ou d'utilité publique poursuivant des missions analogues. -### C. Ségrégation des Fonctions et Intégrité Normative +### C. Ségrégation des fonctions et intégrité normative - **Indépendance de l'autorité normative (Article 8 bis des Statuts)** : Le Comité des Politiques de Confiance (CPC / PMA) assure la garantie normative et cryptographique en toute indépendance de la gestion administrative ou des pressions commerciales. - **Incompatibilité de gouvernance** : L'appartenance au Bureau exécutif est strictement incompatible avec les fonctions de membre du CPC ou d'auditeur interne. - **Prévention des conflits d'intérêts** : Tout administrateur, membre du CPC ou Officier d'Autorité déclare publiquement ses liens d'intérêt et s'abstient de prendre part au vote en cas de délibération susceptible de créer un conflit d'intérêts. -### D. Neutralité, Égalité d'Accès et Souveraineté +### D. Neutralité, égalité d'accès et souveraineté - **Universalité et non-discrimination (Article 12 quater des Statuts)** : L'accès aux services de confiance et briques logicielles édités par l'association est universel, neutre et non discriminatoire, dans le respect strict des sanctions légales et embargos applicables. - **Indépendance technologique** : L'association veille à la neutralité et à l'interopérabilité de ses infrastructures, face aux risques d'enfermement propriétaire (*vendor lock-in*) et de dépendance vis-à-vis de juridictions extraterritoriales intrusives. - **Minimisation des données (*Privacy by Design*)** : L'architecture privilégie systématiquement la protection de la vie privée, le traitement d'empreintes cryptographiques (*hashes*) et le refus absolu de tout profilage ou exploitation commerciale des métadonnées des utilisateurs. --- -## 3. Déclaration Publique d'Engagement +## 3. Déclaration publique d'engagement Tout administrateur, membre du Bureau, membre du CPC et Officier d'Autorité souscrit formellement à cette charte dès sa prise de fonction et s'engage à œuvrer au service exclusif de l'intérêt général et de la confiance numérique collective. diff --git a/docs/gouvernance/comite-technique.md b/docs/gouvernance/comite-technique.md index 94cffdb..8e07efe 100644 --- a/docs/gouvernance/comite-technique.md +++ b/docs/gouvernance/comite-technique.md @@ -1,10 +1,10 @@ -# Organisation Technique et Comité des Politiques de Confiance (CPC) +# Organisation technique et Comité des politiques de confiance (CPC) **Association « Open Trusted Service Provider Initiative » (OTSPI)** --- -## 1. Architecture de la Gouvernance Technique +## 1. Architecture de la gouvernance technique La gouvernance technique de l'association repose sur une séparation stricte des rôles entre l'autorité normative cryptographique et les comités d'ingénierie logicielle et opérationnelle : @@ -32,7 +32,7 @@ La gouvernance technique de l'association repose sur une séparation stricte des --- -## 2. Le Comité des Politiques de Confiance (CPC / PMA) +## 2. Le Comité des politiques de confiance (CPC / PMA) Conformément à l'**Article 8 bis des Statuts**, le CPC est l'organe collégial indépendant faisant office d'**Autorité de Gestion des Politiques (*Policy Management Authority - PMA*)** : - **Garantie d'indépendance** : Strictement dissocié de la direction exécutive (incompatibilité absolue avec le Bureau) ; @@ -45,7 +45,7 @@ Conformément à l'**Article 8 bis des Statuts**, le CPC est l'organe collégial --- -## 3. Le Comité de Pilotage Technique (TSC — Technical Steering Committee) +## 3. Le Comité de pilotage technique (TSC — Technical Steering Committee) Le **TSC** rassemble les ingénieurs, mainteneurs de code et experts en systèmes distribués assurant le développement et l'exploitation quotidienne des briques logicielles libres : - **Ingénierie logicielle** : Développement des composants d'horodatage qualifié (TSA), PKI, protocoles d'émission et de validation (RFC 3161, ACME, REST/JSON, CMP, OCSP) ; @@ -58,7 +58,7 @@ Le **TSC** rassemble les ingénieurs, mainteneurs de code et experts en système --- -## 4. Politique de Sécurité et Signalement de Vulnérabilités +## 4. Politique de sécurité et signalement de vulnérabilités La sécurité de l'infrastructure de confiance est assurée sous la coordination directe du RSSI et du CPC : - **Signalement confidentiel** : @@ -70,7 +70,7 @@ La sécurité de l'infrastructure de confiance est assurée sous la coordination --- -## 5. Environnements et Qualification +## 5. Environnements et qualification Les services opèrent sur deux environnements strictement cloisonnés : 1. **Environnement Bac à Sable (Staging / Test)** : diff --git a/docs/index.md b/docs/index.md index 7f54fdb..18242f1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,106 +1,95 @@ --- title: "OTSPI — Open Trusted Service Provider Initiative" description: "Portail officiel de gouvernance, statuts, conformité et documentation d'intérêt général pour la confiance numérique européenne." +template: home.html hide: - navigation - toc --- -
+ -:material-shield-check: Association d'intérêt général (loi 1901) en cours de constitution +
-# Open Trusted Service Provider Initiative +
+:material-bank-outline:{ .otspi-feature__icon } -

-Construire, opérer et pérenniser des infrastructures de confiance numérique ouvertes, souveraines et universellement accessibles, conformes aux standards eIDAS 2.0, ETSI et WebTrust sous gouvernance strictement désintéressée. -

+

100 % intérêt général

-
-[:material-book-open-page-variant: Consulter le projet de statuts](statuts/statuts-association.md){ .md-button .md-button--primary } -[:material-draw-pen: Signer le Manifeste](https://www.otspi.org/manifeste.html){ .md-button } -[:material-github: Dépôt GitHub](https://github.com/otspi/organisation){ .md-button target="_blank" } +Gestion désintéressée, bénévolat strict des dirigeants, inaliénabilité des logiciels libres et des marques.
-
+
+:material-certificate-outline:{ .otspi-feature__icon } -
+

eIDAS 2.0 et ETSI

-
-🏛️ -
100% Intérêt Général
-

Gestion désintéressée, bénévolat strict des dirigeants, inaliénabilité des logiciels libres et des marques.

+Services de confiance qualifiés : horodatage, signature, scellement et archivage probatoire sous audit CAB.
-
-🇪🇺 -
eIDAS 2.0 & ETSI
-

Services de confiance qualifiés : horodatage, signature, scellement et archivage probatoire sous audit CAB.

-
+
+:material-scale-balance:{ .otspi-feature__icon } -
-⚖️ -
Séparation des Devoirs
-

Indépendance absolue du Comité des Politiques de Confiance (CPC) vis-à-vis du Bureau exécutif.

-
+

Séparation des devoirs

-
-🔍 -
Redevabilité Intégrale
-

Transparence totale des PV, budgets prévisionnels, rapports d'audit et politiques de certification (CP/CPS).

+Indépendance absolue du Comité des Politiques de Confiance (CPC) vis-à-vis du Bureau exécutif.
+
+:material-magnify-scan:{ .otspi-feature__icon } + +

Redevabilité intégrale

+ +Transparence totale des PV, budgets prévisionnels, rapports d'audit et politiques de certification (CP/CPS).
---- +
-## 📚 Explorer la Documentation et les Textes Fondateurs +## Explorer la documentation et les textes fondateurs -
+
-- :material-scale-balance:{ .lg .middle } __Statuts & Gouvernance__ +- :material-scale-balance:{ .lg .middle } __Statuts et gouvernance__ --- Le projet de statuts constitutifs, soumis au vote de l'assemblée générale constitutive, fondé sur l'intérêt général (CGI art. 200 & 238 bis), la gestion désintéressée et la séparation stricte des devoirs. [:octicons-arrow-right-24: Consulter le projet de statuts](statuts/statuts-association.md) - [:octicons-arrow-right-24: Charte d'Éthique & Déontologie](gouvernance/charte-ethique.md) - [:octicons-arrow-right-24: Organisation Technique & CPC](gouvernance/comite-technique.md) + [:octicons-arrow-right-24: Charte d'éthique et de déontologie](gouvernance/charte-ethique.md) + [:octicons-arrow-right-24: Organisation technique et CPC](gouvernance/comite-technique.md) -- :material-book-open-page-variant:{ .lg .middle } __Règlement Intérieur__ +- :material-book-open-page-variant:{ .lg .middle } __Règlement intérieur__ --- Fonctionnement opérationnel : cursus d'habilitation des Officiers d'Autorité, clés matérielles FIPS/ANSSI et règles de fonctionnement de l'association. - [:octicons-arrow-right-24: Règlement Intérieur](reglement-interieur/reglement-interieur.md) + [:octicons-arrow-right-24: Règlement intérieur](reglement-interieur/reglement-interieur.md) -- :material-certificate:{ .lg .middle } __Socle de Conformité (TSP / PKI)__ +- :material-certificate:{ .lg .middle } __Socle de conformité (TSP / PKI)__ --- Le référentiel d'audit initial conforme aux normes eIDAS, ETSI et WebTrust : Cadre CP/CPS (RFC 3647), Politique de Sécurité (PSSI) et Fin d'Activité. - [:octicons-arrow-right-24: Cadre Général CP/CPS](cadrage/cp-cps-cadre.md) - [:octicons-arrow-right-24: Politique de Sécurité (PSSI ISO 27001)](cadrage/pssi.md) - [:octicons-arrow-right-24: Plan de Fin d'Activité (Termination Plan)](cadrage/termination-plan.md) + [:octicons-arrow-right-24: Cadre général CP/CPS](cadrage/cp-cps-cadre.md) + [:octicons-arrow-right-24: Politique de sécurité (PSSI ISO 27001)](cadrage/pssi.md) + [:octicons-arrow-right-24: Plan de fin d'activité (Termination Plan)](cadrage/termination-plan.md) -- :material-file-document-outline:{ .lg .middle } __Démarches Légales & Fiscales__ +- :material-file-document-outline:{ .lg .middle } __Démarches légales et fiscales__ --- Dossier juridique complet : Procès-Verbal de l'AG Constitutive, demande formelle de rescrit fiscal mécénat DGFIP et guide d'immatriculation. - [:octicons-arrow-right-24: Procès-Verbal Constitutif (Modèle)](administratif/pv-ag-constitutive-modele.md) - [:octicons-arrow-right-24: Dossier de Rescrit Fiscal DGFIP](administratif/rescrit-fiscal-mecenat.md) - [:octicons-arrow-right-24: Guide Préfecture (RNA, SIRET)](administratif/declaration-prefecture.md) + [:octicons-arrow-right-24: Procès-verbal constitutif (modèle)](administratif/pv-ag-constitutive-modele.md) + [:octicons-arrow-right-24: Dossier de rescrit fiscal DGFIP](administratif/rescrit-fiscal-mecenat.md) + [:octicons-arrow-right-24: Guide préfecture (RNA, SIRET)](administratif/declaration-prefecture.md)
---- - -## 🏛️ La Confiance Numérique comme Bien Commun +## La confiance numérique comme bien commun L'association **« Open Trusted Service Provider Initiative » (OTSPI)**, en cours de constitution, a pour but d'intérêt général de lever les barrières économiques, techniques, administratives et éducatives à la sécurité, à la confidentialité et à la confiance numérique dans les communications électroniques mondiales. @@ -110,57 +99,32 @@ L'association **« Open Trusted Service Provider Initiative » (OTSPI)**, en cou * **Gestion strictement désintéressée** : Bénévolat strict des dirigeants, inaliénabilité des dépôts logiciels libres et des marques, absence de distribution d'actifs et réinvestissement intégral des excédents dans la mission d'intérêt général. * **Garantie de continuité opérationnelle** : Un fonds de réserve sanctuarisé garantit l'exécution intégrale du plan de fin d'activité (*Termination Plan*, maintien des listes de révocation CRL/OCSP pendant au moins 10 ans). ---- - -## ⚖️ Architecture de Gouvernance et Ségrégation des Fonctions +## Architecture de gouvernance et ségrégation des fonctions La gouvernance d'OTSPI applique une séparation stricte des devoirs conformément aux normes ETSI EN 319 401 et WebTrust : -```mermaid -flowchart TD - AG["Assemblée Générale (Titulaires & Sympathisants)"] --> CA["Conseil d'Administration (2 à 9 membres)"] - AG -.->|"Confirmation des nominations"| CPC["Comité des Politiques de Confiance (CPC / PMA)"] - CA --> Bureau["Bureau (Direction Opérationnelle - Executive Management)
Président, Trésorier, Secrétaire Général"] - - subgraph Normatif["Autorité Normative Cryptographique (Indépendante)"] - CPC -->|"Avis conforme CP/CPS & Cérémonies"| TSC["Comité Technique & Ingénierie (TSC)"] - CPC -->|"Désignation (1 an)"| RSSI["RSSI / CISO"] - CPC -->|"Habilitation & Contrôle"| OA["Officiers d'Autorité & Key Custodians"] - end - - subgraph Operationnel["Direction Opérationnelle & Exploitation"] - Bureau -->|"Mise en œuvre PSSI & Moyens"| TSC - Bureau -->|"Relations de gouvernance"| ANSSI["Autorités de Contrôle & Auditeurs (CAB)"] - end - - classDef blue fill:#2563eb,stroke:#1d4ed8,color:#fff; - classDef green fill:#059669,stroke:#047857,color:#fff; - classDef orange fill:#d97706,stroke:#b45309,color:#fff; - class AG,CA,Bureau blue; - class CPC,RSSI,OA green; - class TSC,ANSSI orange; -``` + +
+Organisation de la gouvernance d'OTSPIL'assemblée générale élit le conseil d'administration, qui désigne le bureau (direction opérationnelle) ; le bureau assure les relations avec les autorités de contrôle et les auditeurs. L'assemblée générale confirme aussi les nominations au comité des politiques de confiance (CPC), autorité normative indépendante du bureau, qui désigne le RSSI pour un an, habilite et contrôle les officiers d'autorité et rend un avis conforme sur les travaux du comité technique ; le bureau fournit à ce comité ses moyens.DIRECTION OPÉRATIONNELLEAUTORITÉ NORMATIVE INDÉPENDANTEconfirmation des nominationsAssemblée généraletitulaires et sympathisantsConseil d'administration2 à 9 membresBureauprésidence · trésorerie · secrétariatAutorités de contrôleet auditeurs (CAB)Comité des politiques de confianceCPC / PMAComité technique (TSC)avis CPC · moyens du BureauRSSI / CISOdésigné par le CPC, 1 anOfficiers d'autoritéet gardiens de clés +
+ 1. **Direction Opérationnelle (*Executive Management*)** : Assurée par le Bureau issu du Conseil d'Administration. Il porte la responsabilité juridique et financière, alloue les ressources de sécurité et assure les relations de gouvernance. 2. **Comité des Politiques de Confiance (CPC / PMA)** : Organe collégial indépendant garant de la rigueur cryptographique et normative. Il approuve les CP/CPS, valide les cérémonies de clés, supervise l'habilitation des Officiers d'Autorité et désigne le RSSI. **L'appartenance au Bureau est strictement incompatible avec le CPC.** 3. **Officiers d'Autorité & Gardiens de Clés (*Key Custodians*)** : Opérateurs habilités assurant sous contrôle à quatre yeux (*Dual Control*) les cérémonies de clés et disposant d'un pouvoir autonome de **révocation d'urgence** sans délai. ---- - -## 🔍 Redevabilité Publique Intégrale (*Public Accountability*) +## Redevabilité publique intégrale (*Public Accountability*) OTSPI applique un principe de **transparence radicale et d'auditabilité publique** : + - Ordres du jour, débats et comptes-rendus publics des réunions (CA, Bureau, Assemblées Générales, comités techniques) ; - Budgets prévisionnels, comptes annuels certifiés, rapports moraux et rapports d'évaluation d'audit (ETSI / eIDAS / WebTrust) ; - Dépôt public de l'ensemble des sources et des documents de gouvernance sur [GitHub : `otspi/organisation`](https://github.com/otspi/organisation). -> [!NOTE] -> **Réserves de protection légitimes :** -> Conformément à notre projet de statuts, les seules exceptions à la publication intégrale concernent la **protection des données personnelles (RGPD)** de nos membres et votants (données nominatives caviardées), les **secrets cryptographiques matériels** (clés protégées sous HSM) et l'**embargo temporaire de sécurité** lors du traitement coordonné de vulnérabilités critiques (*Coordinated Vulnerability Disclosure — CVD*). - ---- +!!! note "Réserves de protection légitimes" + Conformément à notre projet de statuts, les seules exceptions à la publication intégrale concernent la **protection des données personnelles (RGPD)** de nos membres et votants (données nominatives caviardées), les **secrets cryptographiques matériels** (clés protégées sous HSM) et l'**embargo temporaire de sécurité** lors du traitement coordonné de vulnérabilités critiques (*Coordinated Vulnerability Disclosure — CVD*). -## 📬 Contact & Adresses Officielles +## Contact et adresses officielles - **Contact unique (Informations, Partenariats, Sécurité)** : `contact@otspi.org` *(Pour le signalement de vulnérabilités / CVD : indiquer `[Sécurité]` en objet ou utiliser les [GitHub Private Security Advisories](https://github.com/otspi/organisation/security/advisories))* diff --git a/docs/javascripts/a11y.js b/docs/javascripts/a11y.js index 564f130..af7d089 100644 --- a/docs/javascripts/a11y.js +++ b/docs/javascripts/a11y.js @@ -19,3 +19,15 @@ document.addEventListener("DOMContentLoaded", function () { } }); }); + +// Rend accessibles au clavier les blocs de code qui défilent horizontalement (règle axe +// scrollable-region-focusable) : ils deviennent focalisables et portent le nom « Code ». +document.addEventListener("DOMContentLoaded", function () { + document.querySelectorAll("pre > code").forEach(function (code) { + if (code.scrollWidth > code.clientWidth && !code.hasAttribute("tabindex")) { + code.setAttribute("tabindex", "0"); + code.setAttribute("role", "region"); + code.setAttribute("aria-label", "Code"); + } + }); +}); diff --git a/docs/livre-blanc/index.md b/docs/livre-blanc/index.md index a4cfc38..13b32da 100644 --- a/docs/livre-blanc/index.md +++ b/docs/livre-blanc/index.md @@ -7,7 +7,7 @@ description: "Livre blanc de l'Open Trusted Service Provider Initiative : défai
-![OTSPI](../assets/logo-vertical.svg){ .wp-cover-logo } +![OTSPI](../assets/logo-vertical-dark.svg){ .wp-cover-logo }

Livre blanc

Une infrastructure de services de confiance qualifiés d'utilité publique pour eIDAS 2.0

@@ -25,6 +25,8 @@ description: "Livre blanc de l'Open Trusted Service Provider Initiative : défai

La version PDF est balisée et validée au format PDF/UA-1 par l'outil de contrôle veraPDF ; cette validation automatique ne remplace pas un test avec un lecteur d'écran. La présente page (HTML) reste la version accessible de référence.

+
+ | | | |---|---| | **Émetteur** | Initiative « Open Trusted Service Provider Initiative » (OTSPI), association loi 1901 en cours de constitution | @@ -34,6 +36,8 @@ description: "Livre blanc de l'Open Trusted Service Provider Initiative : défai | **Licence** | Creative Commons Attribution 4.0 International (CC-BY-4.0) | | **Contact** | [contact@otspi.org](mailto:contact@otspi.org) | +
+ !!! note "Nature du document" Le présent livre blanc expose une intention et une architecture cible. Il ne constitue ni une Politique d'Horodatage, ni une Déclaration des Pratiques de Certification (DPC), ni un engagement contractuel de service. Les références à des produits ou fournisseurs sont données à titre indicatif ; leur sélection définitive relèvera de procédures de mise en concurrence et de l'approbation du Comité des Politiques de Confiance (CPC). @@ -376,8 +380,9 @@ Tout éditeur de solution de signature, commercial ou libre, peut s'appuyer sur ### 4.1. Hiérarchie de certification -
+
Hiérarchie de certification qualifiée d'OTSPIL'AC racine OTSPI, hors ligne et sous quorum, certifie l'AC intermédiaire d'horodatage, qui certifie les unités d'horodatage TSU-1 (site A) et TSU-2 (site B). Elle certifiera aussi de futures AC qualifiées de cachet, de signature et d'attestations.AC racine OTSPIhors ligne · air-gap · quorum M-de-NAC intermédiaire Horodatagehors ligne ou en ligne restreinteAC qualifiées futurescachet · signature · attestationsUnité d'horodatage TSU-1HSM en ligne — site AUnité d'horodatage TSU-2HSM en ligne — site B +
Figure 1 — Hiérarchie de certification qualifiée d'OTSPI
@@ -385,11 +390,12 @@ Tout éditeur de solution de signature, commercial ou libre, peut s'appuyer sur - Chaque **unité d'horodatage (TSU)** dispose d'une clé propre, exclusivement réservée à la signature de jetons d'horodatage, générée et conservée dans un module cryptographique certifié. - La période d'utilisation des clés TSU est inférieure à la durée de validité de leur certificat, conformément à l'ETSI EN 319 421, afin de garantir la vérifiabilité des jetons émis en fin de période. - Les futures **AC qualifiées** de cachet, de signature et d'attestations (cf. § 6.1, phase 5) seront rattachées à cette même racine qualifiée, chacune sous une AC intermédiaire dédiée à un seul usage. -- Le futur service de certificats TLS (cf. § 2.4) repose sur **deux racines distinctes** de la racine qualifiée d'horodatage : une **racine WebTrust**, destinée aux magasins de confiance des systèmes d'exploitation et des navigateurs, et une **racine QWAC**, inscrite sur la liste de confiance européenne. Elles appliquent les mêmes principes de gouvernance (air-gap, quorum, cérémonies) et ne partagent aucune clé avec les AC d'horodatage, de cachet ou de signature. +- Le futur service de certificats TLS (cf. § 2.4) repose sur **deux racines distinctes** de la racine qualifiée d'horodatage ([figure 2](#figure-2)) : une **racine WebTrust**, destinée aux magasins de confiance des systèmes d'exploitation et des navigateurs, et une **racine QWAC**, inscrite sur la liste de confiance européenne. Elles appliquent les mêmes principes de gouvernance (air-gap, quorum, cérémonies) et ne partagent aucune clé avec les AC d'horodatage, de cachet ou de signature. -
+
Hiérarchies de certification TLS d'OTSPIDeux racines distinctes : une racine WebTrust pour les magasins des navigateurs, une racine QWAC inscrite sur la liste de confiance européenne. La racine WebTrust certifie une sous-AC DV, qui émet des certificats serveur DV par ACME instantané. Une sous-AC hybride OV / QWAC, à clé unique, est certifiée par la racine WebTrust et, par signature croisée, par la racine QWAC ; elle émet des certificats OV / QWAC.signature croiséeRacine WebTrustmagasins OS et navigateursRacine QWACliste de confiance européenneSous-AC DVWebTrust uniquement · HSM standardSous-AC hybride OV / QWACune clé · deux certificats d'ACCertificats serveur DVusage Web · ACME instantanéCertificats serveur OV / QWACDSP2, eIDAS · ACME avec liaison de compte +
Figure 2 — Hiérarchies de certification TLS d'OTSPI
diff --git a/docs/reglement-interieur/reglement-interieur.md b/docs/reglement-interieur/reglement-interieur.md index 25037fa..4178994 100644 --- a/docs/reglement-interieur/reglement-interieur.md +++ b/docs/reglement-interieur/reglement-interieur.md @@ -1,3 +1,7 @@ +--- +pdf: otspi-reglement-interieur.pdf +--- + # Règlement Intérieur de l'Association OTSPI ## « Open Trusted Service Provider Initiative » @@ -9,6 +13,7 @@ ## Préambule Le présent Règlement Intérieur opérationnalise les principes généraux fixés dans les Statuts de l'Association **« Open Trusted Service Provider Initiative » (OTSPI)**. Il définit les règles précises relatives : + - À la qualification des membres, à la vérification d'identité civile et à la gestion des équipements cryptographiques matériels ; - Au cursus d'habilitation, au serment éthique et au régime des Officiers d'Autorité et des Gardiens de clés (*Key Custodians*) ; - À la ségrégation stricte des devoirs entre la direction opérationnelle (Bureau) et l'autorité normative (CPC / PMA) ; @@ -21,6 +26,7 @@ Le présent Règlement Intérieur opérationnalise les principes généraux fix ### Article 1 — Collèges et catégories de membres Conformément à l'Article 5 des Statuts, l'association comprend : + 1. **Membres sympathisants** : Personnes physiques ou morales soutenant les buts de l'association, à jour de cotisation. Ils disposent d'une voix consultative et exercent la faculté de refus collective prévue aux Articles 9 et 11 des Statuts. 2. **Membres titulaires** : Personnes physiques ou personnes morales dûment représentées participant de manière substantielle et active à la gouvernance, à la conformité ou aux opérations critiques. Ils disposent d'une voix délibérative pleine et entière. 3. **Membres bienfaiteurs** : Personnes morales apportant une contribution annuelle significative supérieure à cinq mille euros (5 000 € / an) sous forme de dons financiers, de mécénat matériel (serveurs, HSMs) ou de prise en charge locative. Ce statut honorifique n'ouvre aucun droit de vote (ni délibératif ni consultatif) afin de garantir la stricte neutralité et l'indépendance de l'infrastructure. @@ -28,6 +34,7 @@ Conformément à l'Article 5 des Statuts, l'association comprend : ### Article 2 — Vérification formelle de l'identité civile des membres titulaires Conformément à l'Article 5 quater des Statuts, aucune voix délibérative ne peut être exercée sous statut pseudonyme ou anonyme : + 1. **Procédure de contrôle d'identité préalable** : - Tout candidat personne physique au statut titulaire (ou tout représentant physique désigné par une personne morale membre titulaire) doit présenter une pièce d'identité officielle en cours de validité (carte nationale d'identité, passeport ou titre de séjour émis par un État souverain). - La vérification est effectuée soit en présentiel par un membre du Bureau ou un Officier d'Autorité, soit par un moyen de vérification d'identité à distance conforme au référentiel d'exigences PVID (Prestataire de Vérification d'Identité à Distance) de l'ANSSI ou équivalent eIDAS de niveau substantiel ou élevé. @@ -58,6 +65,7 @@ Conformément à l'Article 5 quater des Statuts, aucune voix délibérative ne p ### Article 5 — Cursus d'évaluation et habilitation des Officiers d'Autorité L'Officier d'Autorité exerce un rôle de confiance critique au sens des normes ETSI EN 319 401 et WebTrust. + 1. **Critères d'éligibilité** : - Être membre titulaire ou contributeur de l'association depuis au moins six (6) mois (sauf durant la phase d'amorçage de 24 mois visée à l'Article 5 bis.3 des Statuts) ; - Avoir une identité civile vérifiée et un casier judiciaire vierge (bulletin n°3 ou équivalent international de moins de 3 mois) ; @@ -102,6 +110,7 @@ L'Officier d'Autorité exerce un rôle de confiance critique au sens des normes ### Article 8 — Étanchéité de gouvernance et incompatibilités Conformément à l'Article 8 bis des Statuts : + 1. **Incompatibilité absolue** : Les fonctions de membre du Bureau (Président, Trésorier, Secrétaire Général) sont strictement incompatibles avec celles de membre du Comité des Politiques de Confiance (CPC) et avec celles d'Auditeur Interne indépendant. 2. **Rôle des administrateurs du CA** : Les membres du Conseil d'Administration n'exerçant pas de fonction exécutive au Bureau peuvent siéger au CPC, sous réserve de s'abstenir lors de tout vote du Conseil d'Administration portant sur la ratification ou le contrôle des décisions du CPC. 3. **Désignation du RSSI (CISO)** : Le RSSI est nommé par le CPC pour un mandat d'un (1) an renouvelable. Il rapporte techniquement au CPC et fonctionnellement au Bureau. @@ -135,6 +144,7 @@ Conformément à l'Article 8 bis des Statuts : ### Article 11 — Plafonds d'engagement de dépenses autonomes du Bureau Afin de concilier réactivité opérationnelle et contrôle budgétaire de l'Assemblée et du Conseil d'Administration : + 1. **Dépenses courantes autonomes du Bureau** : Le Président et le Trésorier peuvent engager conjointement les dépenses d'exploitation courante (abonnements d'infrastructure d'hébergement, télécoms, petit matériel, frais de mission justifiés) dans la limite d'un plafond de **cinq mille euros (5 000 €) hors taxes par opération**, dans le cadre du budget annuel voté. 2. **Autorisation préalable obligatoire du Conseil d'Administration** : Requiert une délibération préalable du Conseil d'Administration : - Tout investissement matériel unitaire ou contrat de prestation excédant **cinq mille euros (5 000 €) HT** ; diff --git a/docs/reunions/index.md b/docs/reunions/index.md index 341658d..a087ed5 100644 --- a/docs/reunions/index.md +++ b/docs/reunions/index.md @@ -1,4 +1,4 @@ -# Réunions et Comptes-Rendus Publics (*Public Accountability*) +# Réunions et comptes-rendus publics (*Public Accountability*) **Association « Open Trusted Service Provider Initiative » (OTSPI)** @@ -8,7 +8,7 @@ Toutes les réunions formelles de gouvernance font l'objet d'un ordre du jour pr --- -## 🏛️ Typologie des Réunions +## Typologie des réunions 1. **Assemblées Générales (AG)** : - Assemblée Générale Ordinaire (AGO) : Approbation des comptes, rapport moral, budget, renouvellement du CA, confirmation des membres du CPC. @@ -22,7 +22,7 @@ Toutes les réunions formelles de gouvernance font l'objet d'un ordre du jour pr --- -## 🛡️ Règles de Publication et Protection RGPD / Sécurité +## Règles de publication et protection RGPD / sécurité Avant toute publication publique d'un compte-rendu : @@ -35,7 +35,7 @@ Avant toute publication publique d'un compte-rendu : --- -## 📝 Modèle Type de Compte-Rendu Public +## Modèle type de compte-rendu public ```markdown # Compte-Rendu : [Conseil d'Administration / CPC / AG / TSC] du [AAAA-MM-JJ] @@ -47,20 +47,20 @@ Avant toute publication publique d'un compte-rendu : - **Participants** : [Liste des membres avec respect de la vie privée RGPD] - **Quorum constaté** : [Pourcentage et conformité statutaire] -### 1. Ordre du Jour +### 1. Ordre du jour 1. Point de situation opérationnelle et technique 2. Examen de conformité réglementaire et audits 3. Résolutions soumises au vote -### 2. Synthèse des Débats +### 2. Synthèse des débats [Résumé factuel et transparent des échanges] -### 3. Décisions et Votes +### 3. Décisions et votes - **Résolution 1** : [Description de la décision] - *Vote des titulaires* : Pour (X) / Contre (Y) / Abstention (Z) - *Faculté de refus des sympathisants* : Opposition exprimée (Oui/Non, suffrages) - *Résultat* : Adopté / Rejeté -### 4. Prochaines Échéances +### 4. Prochaines échéances - [Actions planifiées et responsables désignés] ``` diff --git a/docs/statuts/statuts-association.md b/docs/statuts/statuts-association.md index c26b6a9..7a4d2b8 100644 --- a/docs/statuts/statuts-association.md +++ b/docs/statuts/statuts-association.md @@ -2,6 +2,7 @@ title: "STATUTS DE L'ASSOCIATION OTSPI" author: "Membres Fondateurs" date: "Projet — non encore adopté" +pdf: otspi-projet-de-statuts.pdf --- # STATUTS DE L'ASSOCIATION @@ -26,6 +27,7 @@ L’association est régie par les principes d'une **gestion désintéressée** L’association a pour vocation de promouvoir, concevoir, opérer et pérenniser des infrastructures critiques de confiance numérique ouvertes, souveraines, transparentes et universellement accessibles. À ce titre, l’association a notamment pour missions, directes ou indirectes, en France et à l'international : + 1. **L'opération et la fourniture de services de confiance :** concevoir, déployer, opérer et maintenir des infrastructures et services de confiance numérique (notamment la gestion des identités, les infrastructures à clés publiques, l'émission de certificats, l'horodatage électronique qualifié, le scellement, la signature numérique, l'archivage probatoire et la préservation ou validation de preuves cryptographiques) ; 2. **L'obtention et le maintien d'accréditations, de qualifications et de certifications :** instruire et pérenniser toute démarche d'évaluation, d'audit technique ou de qualification auprès des autorités publiques compétentes, des organismes de normalisation, des consortiums de l'industrie et des programmes de confiance des éditeurs de plateformes ; 3. **La conception et la diffusion de technologies ouvertes :** concevoir, maintenir et publier sous licences libres ou ouvertes des spécifications, protocoles, briques logicielles et architectures matérielles vérifiables concourant à la transparence des chaînes de confiance ; @@ -49,6 +51,7 @@ La durée de l'association est illimitée. #### Article 5 — Catégories de membres L'association se compose de : + * **Membres sympathisants :** personnes physiques ou morales adhérant aux buts de l'association, à jour de leur cotisation annuelle. Ils participent aux travaux et aux Assemblées Générales avec voix consultative, et disposent de la faculté de refus collective prévue aux présents statuts ; * **Membres titulaires :** personnes physiques ou personnes morales dûment représentées, participant de façon substantielle et active à la gouvernance, à la conformité ou aux opérations critiques de l'association. Ils disposent d'une voix délibérative pleine et entière sur l'ensemble des scrutins ; * **Membres bienfaiteurs :** personnes morales apportant un soutien financier, matériel ou de mise à disposition de locaux/infrastructures significatif (défini par le Règlement Intérieur, supérieur à 5 000 € par an). Cette qualité honorifique n'ouvre aucun droit de vote au sein des Assemblées Générales, préservant l'indépendance de l'association ; @@ -91,6 +94,7 @@ Les fondateurs initiaux, signataires des statuts constitutifs, disposent de plei #### Article 6 — Perte de la qualité de membre La qualité de membre de l'association se perd par : + 1. **La démission :** adressée par écrit au Conseil d'Administration, prenant effet à sa réception ; 2. **Le non-paiement de la cotisation :** * **Régime de droit commun :** Le défaut de paiement de la cotisation annuelle à l'échéance fixée par le Règlement Intérieur, demeuré infructueux trente (30) jours calendaires après mise en demeure par voie électronique, entraîne la **radiation automatique d'office** de l'association. Pour un membre titulaire n'exerçant aucun mandat statutaire ni rôle de confiance actif, le défaut de régularisation emporte la perte immédiate de la qualité de membre titulaire et de ses droits de vote délibératifs. @@ -172,6 +176,7 @@ L’Assemblée Générale comprend tous les membres de l'association. Elle se r #### Article 10 — Domaine réservé de l'Assemblée Générale Relèvent de la compétence exclusive de l'Assemblée Générale, sans délégation possible : + 1. L’élection et la révocation des administrateurs, ainsi que la fixation du nombre de sièges ; 2. L’approbation annuelle des comptes financiers, l'affectation du résultat et le vote du budget prévisionnel ; 3. L’autorisation préalable de tout emprunt ou engagement financier pluriannuel excédant les seuils fixés par le Règlement Intérieur ; @@ -185,6 +190,7 @@ Relèvent de la compétence exclusive de l'Assemblée Générale, sans délégat #### Article 11 — Assemblée Générale Extraordinaire (Régime ordinaire) L'Assemblée Générale Extraordinaire est seule compétente pour modifier les présents statuts ou prononcer la dissolution de l'association, sous réserve des dispositions intangibles prévues aux Articles 8 ter et 13. + 1. **Quorum :** Participation d'au moins **soixante-quinze pour cent (75 %)** des membres titulaires. 2. **Unanimité des votants :** Aucune modification ne peut être adoptée sans recueillir le vote favorable de **cent pour cent (100 %) des membres titulaires participant au scrutin**. 3. **Contrôle global :** Même approuvée à l'unanimité des titulaires votants, la révision est rejetée si la majorité simple des suffrages exprimés par l'ensemble des membres (titulaires et sympathisants réunis prenant part au scrutin) émet un vote d'opposition. @@ -207,6 +213,7 @@ L'Assemblée Générale Extraordinaire est seule compétente pour modifier les p #### Article 11 ter — Régime de la Reconnaissance d'Utilité Publique (RUP) Dans l'hypothèse où l'association viendrait à être reconnue d'utilité publique par décret en Conseil d'État : + * Les clauses et règles de tutelle prévues par les statuts types approuvés par le Conseil d'État se substituent de plein droit aux stipulations des Articles 11 et 11 bis pour les révisions statutaires ; * Le contrôle de légalité, l'approbation préalable par décret ministériel et l'avis conforme du Conseil d'État constituent dès lors le garant institutionnel de l'inaliénabilité des missions d'intérêt général de l'association. @@ -250,6 +257,7 @@ Par dérogation légale impérative, l'association applique les restrictions tec #### Article 13 bis — Continuité d’infrastructure et association successeur Par dérogation aux règles ordinaires de liquidation, l’Assemblée Générale Extraordinaire statuant selon les règles de l'Article 11 peut décider du transfert de tout ou partie des infrastructures, dépôts logiciels, marques et qualifications vers une nouvelle personne morale sans but lucratif (*association successeur*), à la condition expresse et préalable que celle-ci : + * Soit constituée sous forme non lucrative (loi 1901 ou équivalent européen) ; * Reprenne textuellement dans ses statuts les mêmes clauses d'inaliénabilité, d'interdiction de transformation lucrative et d'engagement envers les licences libres ; * S'engage formellement à assurer la continuité intégrale des obligations souscrites auprès des autorités de contrôle et des programmes de confiance internationaux. diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css index 817e9a0..3ecfb54 100644 --- a/docs/stylesheets/extra.css +++ b/docs/stylesheets/extra.css @@ -1,228 +1,419 @@ /* ========================================================================== - OTSPI Custom Styling — Modern, Trustworthy & Accessible + OTSPI — charte du portail + Bleu nuit du logo (#0b1120), bleu institutionnel, bleu ciel et jaune d'accent. ========================================================================== */ :root { + --otspi-navy: #0b1120; + --otspi-navy-2: #111a33; --otspi-primary: #1e40af; --otspi-primary-light: #3b82f6; - --otspi-accent: #0ea5e9; - --otspi-success: #10b981; - --otspi-warning: #f59e0b; + --otspi-sky: #38bdf8; + --otspi-gold: #fbbf24; --otspi-card-bg: #ffffff; - --otspi-card-border: rgba(226, 232, 240, 0.8); - --otspi-card-shadow: 0 4px 20px -2px rgba(0, 0, 0, 0.05); - --otspi-card-hover-shadow: 0 12px 28px -4px rgba(30, 64, 175, 0.12); + --otspi-card-border: #e2e8f0; + --otspi-card-shadow: 0 1px 2px rgba(15, 23, 42, 0.04), 0 4px 16px -6px rgba(15, 23, 42, 0.08); + --otspi-card-hover-shadow: 0 10px 28px -8px rgba(30, 64, 175, 0.22); +} + +/* Palette personnalisée de Material (primary: custom, accent: custom) */ +:root > * { + --md-primary-fg-color: #1e40af; + --md-primary-fg-color--light: #3b82f6; + --md-primary-fg-color--dark: #0b1120; + --md-primary-bg-color: #ffffff; + --md-primary-bg-color--light: rgba(255, 255, 255, 0.72); + --md-accent-fg-color: #0369a1; + --md-accent-fg-color--transparent: rgba(3, 105, 161, 0.1); + --md-typeset-a-color: #1d4ed8; + /* Gris de coloration syntaxique et texte secondaire des schémas : le gris clair de Material + (#0000008a) reste sous 4,5:1 sur le fond des blocs de code et des boîtes teintées */ + --md-code-hl-generic-color: #4b5563; + --md-code-hl-comment-color: #4b5563; + --otspi-muted: #4b5563; } [data-md-color-scheme="slate"] { - --otspi-card-bg: #1e293b; - --otspi-card-border: rgba(51, 65, 85, 0.7); - --otspi-card-shadow: 0 4px 20px -2px rgba(0, 0, 0, 0.25); - --otspi-card-hover-shadow: 0 12px 28px -4px rgba(14, 165, 233, 0.15); + --md-hue: 225; + --otspi-card-bg: #161f36; + --otspi-card-border: rgba(148, 163, 184, 0.18); + --otspi-card-shadow: 0 4px 20px -6px rgba(0, 0, 0, 0.4); + --otspi-card-hover-shadow: 0 12px 28px -6px rgba(56, 189, 248, 0.18); +} + +[data-md-color-scheme="slate"] > * { + --otspi-muted: #b4bcc8; + --md-accent-fg-color: #38bdf8; + --md-accent-fg-color--transparent: rgba(56, 189, 248, 0.12); +} + +/* -------------------------------------------------------------------------- + En-tête et onglets : bleu nuit du logo + -------------------------------------------------------------------------- */ +.md-header, +.md-tabs { + background-color: var(--otspi-navy); +} + +.md-header { + box-shadow: none; + border-bottom: 1px solid rgba(148, 163, 184, 0.14); +} + +.md-header--shadow { + box-shadow: 0 2px 12px rgba(2, 6, 23, 0.35); +} + +.md-header__button.md-logo { + display: flex !important; + align-items: center; + margin-right: 0.5rem; +} + +.md-header__button.md-logo img, +.md-header__button.md-logo svg { + height: 1.6rem !important; + width: auto !important; +} + +.md-tabs__link { + font-weight: 500; + opacity: 0.78; +} + +.md-tabs__item--active .md-tabs__link, +.md-tabs__link:hover { + opacity: 1; +} + +.md-tabs__item--active .md-tabs__link { + color: #ffffff; + box-shadow: inset 0 -2px 0 var(--otspi-sky); +} + +.md-search__form { + background-color: rgba(148, 163, 184, 0.14); + border-radius: 8px; +} + +/* Pied de page aux couleurs de l'en-tête */ +:root > * { + --md-footer-bg-color: #0b1120; + --md-footer-bg-color--dark: #070b16; } /* -------------------------------------------------------------------------- - Hero Section + Titres : hiérarchie nette, sans le gris clair et la graisse légère de Material -------------------------------------------------------------------------- */ +.md-typeset h1 { + color: var(--md-default-fg-color); + font-weight: 800; + line-height: 1.2; + letter-spacing: -0.02em; +} + +.md-typeset h2 { + font-weight: 700; + letter-spacing: -0.01em; +} + +.md-typeset h3 { + font-weight: 700; +} + +/* -------------------------------------------------------------------------- + Accueil — bandeau pleine largeur (overrides/home.html) + -------------------------------------------------------------------------- */ +/* Fond uni : le logo, qui porte son propre fond #0b1120, s'y fond sans cadre visible */ .otspi-hero { - text-align: center; - padding: clamp(2rem, 5vw, 3.5rem) 1rem clamp(1.5rem, 4vw, 2.5rem); - margin-bottom: 1.5rem; - position: relative; + background: var(--otspi-navy); + color: #e2e8f0; } -.otspi-badge { - display: inline-flex; +[data-md-color-scheme="slate"] .otspi-hero { + border-bottom: 1px solid rgba(148, 163, 184, 0.16); +} + +.otspi-hero__inner { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 2rem; align-items: center; - gap: 0.5rem; - padding: 0.35rem 0.9rem; - border-radius: 9999px; - font-size: clamp(0.72rem, 2vw, 0.82rem); - font-weight: 600; - letter-spacing: 0.025em; - background-color: rgba(59, 130, 246, 0.1); - color: var(--otspi-primary-light); - border: 1px solid rgba(59, 130, 246, 0.25); - margin-bottom: 1.25rem; + padding: clamp(2.2rem, 6vw, 4rem) 0.8rem clamp(2.4rem, 6vw, 4.2rem); +} + +.otspi-hero__kicker { + margin: 0 0 1rem; + color: var(--otspi-sky); + font-size: 0.68rem; + font-weight: 700; + letter-spacing: 0.16em; text-transform: uppercase; - max-width: 100%; } -.otspi-hero h1 { - font-size: clamp(1.65rem, 5vw + 0.3rem, 2.5rem) !important; - font-weight: 800 !important; - line-height: 1.25 !important; - margin-bottom: 1rem !important; - letter-spacing: -0.02em; +.otspi-hero__title { + margin: 0; + color: #ffffff; + font-size: clamp(1.7rem, 4.2vw, 2.7rem); + font-weight: 800; + line-height: 1.12; + letter-spacing: -0.025em; } -.otspi-hero .subtitle { - font-size: clamp(0.95rem, 2vw + 0.2rem, 1.15rem); - line-height: 1.6; - max-width: 48rem; - margin: 0 auto 1.5rem; - color: var(--md-default-fg-color--light); +.otspi-hero__title::after { + content: ""; + display: block; + width: 3.2rem; + height: 0.22rem; + margin-top: 1.1rem; + border-radius: 1px; + background: var(--otspi-gold); +} + +.otspi-hero__lead { + max-width: 38rem; + margin: 1.2rem 0 0; + color: #cbd5e1; + font-size: clamp(0.85rem, 1.6vw, 0.98rem); + line-height: 1.65; } -.otspi-hero-actions { +.otspi-hero__lead strong { + color: #ffffff; +} + +.otspi-hero__actions { display: flex; - justify-content: center; - gap: 0.85rem; flex-wrap: wrap; - margin-top: 1.25rem; + gap: 0.7rem; + margin-top: 1.8rem; } -@media screen and (max-width: 600px) { - .otspi-hero-actions { - flex-direction: column; - align-items: stretch; - max-width: 300px; - margin: 1.25rem auto 0; +/* Hors de .md-typeset, Material ne donne aucun style de bouton : on les pose ici */ +.otspi-hero .md-button { + display: inline-block; + margin: 0; + border: 0.1rem solid rgba(226, 232, 240, 0.45); + color: #ffffff; + font-size: 0.72rem; + line-height: 1.4; + text-decoration: none; +} + +.otspi-hero .md-button:hover, +.otspi-hero .md-button:focus-visible { + border-color: #ffffff; + background-color: rgba(255, 255, 255, 0.08); + color: #ffffff; +} + +.otspi-hero .md-button--primary { + border-color: var(--otspi-sky) !important; + background-color: var(--otspi-sky) !important; + color: var(--otspi-navy) !important; + box-shadow: none !important; +} + +.otspi-hero .md-button--primary:hover, +.otspi-hero .md-button--primary:focus-visible { + border-color: #7dd3fc !important; + background-color: #7dd3fc !important; +} + +.otspi-hero__logo { + width: 13rem; + height: auto; +} + +@media screen and (max-width: 59.9844em) { + .otspi-hero__inner { + grid-template-columns: minmax(0, 1fr); } - .otspi-hero-actions .md-button { - width: 100%; + + .otspi-hero__logo { + display: none; + } +} + +@media screen and (max-width: 29.9844em) { + .otspi-hero__actions .md-button { + flex: 1 1 100%; text-align: center; - justify-content: center; } } /* -------------------------------------------------------------------------- - Key Stats / Value Pillars + Accueil — quatre engagements -------------------------------------------------------------------------- */ -.otspi-stats-grid { +.otspi-features { display: grid; - grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); - gap: 1.25rem; - margin: 2.5rem 0 3.5rem; + grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr)); + gap: 1rem; + margin: 0.4rem 0 2.6rem; } -.otspi-stat-card { - background: var(--otspi-card-bg); +.md-typeset .otspi-feature { + padding: 1.2rem 1.2rem 1rem; border: 1px solid var(--otspi-card-border); border-radius: 12px; - padding: 1.5rem 1.25rem; - text-align: center; + background: var(--otspi-card-bg); box-shadow: var(--otspi-card-shadow); - transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease; + font-size: 0.72rem; + line-height: 1.55; } -.otspi-stat-card:hover { - transform: translateY(-3px); - border-color: var(--otspi-primary-light); - box-shadow: var(--otspi-card-hover-shadow); +.md-typeset .otspi-feature p { + margin: 0; + color: var(--md-default-fg-color--light); } -.otspi-stat-icon { - font-size: 1.75rem; - margin-bottom: 0.5rem; - display: inline-block; +.md-typeset .otspi-feature .otspi-feature__icon { + display: inline-flex; + width: 2.2rem; + height: 2.2rem; + margin-bottom: 0.8rem; + padding: 0.45rem; + border-radius: 10px; + background: var(--md-accent-fg-color--transparent); + color: var(--otspi-primary); } -.otspi-stat-title { - font-size: 1.05rem; - font-weight: 700; - margin-bottom: 0.35rem; - color: var(--md-default-fg-color); +.md-typeset .otspi-feature .otspi-feature__icon svg { + width: 100%; + height: 100%; + max-height: none; } -.otspi-stat-desc { +.md-typeset .otspi-feature p.otspi-feature__title { + margin: 0 0 0.35rem; + color: var(--md-default-fg-color); font-size: 0.85rem; - line-height: 1.4; - color: var(--md-default-fg-color--light); + font-weight: 700; +} + +[data-md-color-scheme="slate"] .md-typeset .otspi-feature .otspi-feature__icon { + color: var(--otspi-sky); +} + +/* Titres de section de l'accueil */ +.md-content:has(.otspi-features) .md-typeset h2 { + margin-top: 2.4rem; + padding-top: 1.6rem; + border-top: 1px solid var(--md-default-fg-color--lightest); +} + +.md-content:has(.otspi-features) .md-typeset .otspi-features + h2 { + border-top: none; + padding-top: 0; +} + +/* Cartes de documentation : deux colonnes au plus, pas de carte orpheline */ +.md-typeset .grid.cards.otspi-docs { + grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr)); +} + +/* -------------------------------------------------------------------------- + Page d'erreur 404 (overrides/404.html) + -------------------------------------------------------------------------- */ +.md-typeset .otspi-404 { + max-width: 34rem; + margin: 1rem 0 3rem; +} + +.md-typeset .otspi-404__code { margin: 0; + color: var(--otspi-primary); + font-size: 3.2rem; + font-weight: 800; + line-height: 1; + letter-spacing: -0.04em; +} + +[data-md-color-scheme="slate"] .md-typeset .otspi-404__code { + color: var(--otspi-sky); +} + +.md-typeset .otspi-404 h1 { + margin: 0.6rem 0 0.8rem; +} + +.md-typeset .otspi-404__en { + color: var(--md-default-fg-color--light); +} + +.md-typeset .otspi-404__links { + margin-top: 1.4rem; + padding: 1rem 1.2rem; + border: 1px solid var(--otspi-card-border); + border-radius: 12px; + margin-left: 0; + background: var(--otspi-card-bg); + list-style: none; +} + +.md-typeset .otspi-404__links li { + margin: 0.35em 0; } /* -------------------------------------------------------------------------- - Material for MkDocs Cards Customization + Cartes Material -------------------------------------------------------------------------- */ .md-typeset .grid.cards > ul > li { background: var(--otspi-card-bg); border: 1px solid var(--otspi-card-border); - border-radius: 14px; - padding: 1.75rem; + border-radius: 12px; + padding: 1.4rem 1.5rem; box-shadow: var(--otspi-card-shadow); - transition: transform 0.25s cubic-bezier(0.16, 1, 0.3, 1), - box-shadow 0.25s cubic-bezier(0.16, 1, 0.3, 1), - border-color 0.25s ease; - position: relative; + transition: box-shadow 0.2s ease, border-color 0.2s ease; } .md-typeset .grid.cards > ul > li:hover { - transform: translateY(-4px); box-shadow: var(--otspi-card-hover-shadow); border-color: var(--otspi-primary-light); } .md-typeset .grid.cards > ul > li > hr { - margin: 1rem 0; - opacity: 0.2; + margin: 0.8rem 0; + opacity: 0.4; +} + +.md-typeset .grid.cards .lg { + color: var(--otspi-primary); +} + +[data-md-color-scheme="slate"] .md-typeset .grid.cards .lg { + color: var(--otspi-sky); } /* -------------------------------------------------------------------------- - Custom Buttons + Boutons -------------------------------------------------------------------------- */ .md-button { border-radius: 8px !important; font-weight: 600 !important; letter-spacing: 0.01em; - padding: 0.65em 1.5em !important; - transition: all 0.2s ease !important; + padding: 0.6em 1.3em !important; + transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease !important; } .md-button--primary { background-color: var(--otspi-primary) !important; border-color: var(--otspi-primary) !important; - box-shadow: 0 4px 14px rgba(30, 64, 175, 0.25) !important; } .md-button--primary:hover { - background-color: var(--otspi-primary-light) !important; - border-color: var(--otspi-primary-light) !important; - transform: translateY(-1px); - box-shadow: 0 6px 20px rgba(30, 64, 175, 0.35) !important; + background-color: #1e3a8a !important; + border-color: #1e3a8a !important; } /* -------------------------------------------------------------------------- - Mermaid Diagrams & Admonitions + Encadrés : Material les compose plus petit que le corps du texte -------------------------------------------------------------------------- */ -.mermaid { - background: var(--otspi-card-bg); - border: 1px solid var(--otspi-card-border); - border-radius: 12px; - padding: 1.5rem 1rem; - box-shadow: var(--otspi-card-shadow); - margin: 1.5rem 0 !important; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - max-width: 100%; -} - -.md-typeset .admonition { +.md-typeset .admonition, +.md-typeset details { border-radius: 10px; - box-shadow: var(--otspi-card-shadow); -} - -/* -------------------------------------------------------------------------- - Navigation & Header Refinements - -------------------------------------------------------------------------- */ -.md-header { - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 2px rgba(0, 0, 0, 0.1); -} - -.md-header__button.md-logo { - display: flex !important; - align-items: center; - margin-right: 0.5rem; -} - -.md-header__button.md-logo img, -.md-header__button.md-logo svg { - height: 1.6rem !important; - width: auto !important; -} - -.md-tabs { - border-bottom: 1px solid rgba(255, 255, 255, 0.1); + box-shadow: none; + font-size: 0.72rem; } /* -------------------------------------------------------------------------- @@ -262,6 +453,88 @@ } } +/* -------------------------------------------------------------------------- + Livre blanc — en-tête, métadonnées et hiérarchie des titres + (la page de garde .wp-cover, masquée à l'écran, identifie la page du livre blanc) + -------------------------------------------------------------------------- */ +.md-typeset .wp-cover ~ h1 { + margin-bottom: 0.4em; + color: var(--otspi-primary); + font-size: 0.8rem; + font-weight: 700; + letter-spacing: 0.16em; + text-transform: uppercase; +} + +.md-typeset .wp-cover ~ h1 + h2 { + margin-top: 0; + padding-left: 0.8rem; + border-left: 4px solid var(--otspi-primary); + color: var(--md-default-fg-color); + font-size: 1.6em; + font-weight: 700; + line-height: 1.3; + letter-spacing: -0.01em; +} + +[data-md-color-scheme="slate"] .md-typeset .wp-cover ~ h1 { + color: #9cc0ff; +} + +[data-md-color-scheme="slate"] .md-typeset .wp-cover ~ h1 + h2 { + border-left-color: #8fb4ff; +} + +.md-typeset:has(> .wp-cover) h2 { + font-weight: 700; + letter-spacing: -0.01em; +} + +.md-typeset:has(> .wp-cover) h3 { + font-weight: 700; +} + +.md-typeset:has(> .wp-cover) h4 { + font-weight: 700; + color: var(--md-default-fg-color--light); +} + +/* Fiche d'identification du document : tableau clé / valeur sans ligne d'en-tête */ +.md-typeset .wp-meta thead { + display: none; +} + +.md-typeset .wp-meta table:not([class]) { + border: none; + border-top: 2px solid var(--otspi-primary); + border-radius: 0; + box-shadow: none; + font-size: 0.72rem; +} + +.md-typeset .wp-meta table:not([class]) td { + padding: 0.55em 0.9em; + border-top: 1px solid var(--md-default-fg-color--lightest); + vertical-align: top; +} + +.md-typeset .wp-meta table:not([class]) td:first-child { + width: 11em; + color: var(--md-default-fg-color--light); + white-space: nowrap; +} + +.md-typeset .wp-meta table:not([class]) tr:hover { + box-shadow: none; +} + +@media screen and (max-width: 44.9375em) { + .md-typeset .wp-meta table:not([class]) td:first-child { + width: auto; + white-space: normal; + } +} + /* -------------------------------------------------------------------------- Livre blanc — chiffres clés -------------------------------------------------------------------------- */ @@ -301,6 +574,20 @@ overflow-x: auto; } +.md-typeset .wp-diagram figcaption { + max-width: 820px; + margin: 0.8em auto 0; + color: var(--md-default-fg-color--light); + font-size: 0.72rem; + font-style: normal; + text-align: center; +} + +.md-typeset .wp-fig-num { + color: var(--md-default-fg-color); + font-weight: 700; +} + .md-typeset .wp-svg { display: block; width: 100%; @@ -310,6 +597,23 @@ margin: 0 auto; } +.md-typeset .wp-svg--wide { + max-width: 940px; +} + +/* Regroupements de l'organigramme de gouvernance (accueil) */ +.wp-svg .dg-group { + fill: var(--md-default-fg-color--lightest); + stroke: none; +} + +.wp-svg .dg-glabel { + fill: var(--otspi-muted); + font-size: 11px; + font-weight: 700; + letter-spacing: 0.08em; +} + .wp-svg .dg-box { fill: var(--md-default-bg-color); stroke: var(--md-default-fg-color--lighter); @@ -336,7 +640,7 @@ } .wp-svg .dg-s { - fill: var(--md-default-fg-color--light); + fill: var(--otspi-muted); font-size: 12px; } @@ -429,10 +733,11 @@ --md-typeset-a-color: #8fb4ff; } -[data-md-color-scheme="slate"] .otspi-badge { - color: #9cc0ff; +[data-md-color-scheme="slate"] .md-typeset .wp-meta table:not([class]) { + border-top-color: #8fb4ff; } + [data-md-color-scheme="slate"] .md-typeset a:not(.md-button):not(.headerlink):not(.md-annotation__index) { color: #8fb4ff; } diff --git a/docs/stylesheets/fonts.css b/docs/stylesheets/fonts.css new file mode 100644 index 0000000..fdbdd91 --- /dev/null +++ b/docs/stylesheets/fonts.css @@ -0,0 +1,68 @@ +/* ========================================================================== + Police Inter (SIL Open Font License 1.1, voir assets/fonts/inter/OFL.txt), + servie par le portail lui-même : aucune requête vers un service tiers. + Sous-ensemble latin (français, anglais) de la distribution Fontsource 5.3.0. + Le PDF du livre blanc l'embarque aussi : son rendu ne dépend plus des polices + installées sur la machine qui le génère. + ========================================================================== */ + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 300; + font-display: swap; + src: url("../assets/fonts/inter/inter-latin-300-normal.woff2") format("woff2"); +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url("../assets/fonts/inter/inter-latin-400-normal.woff2") format("woff2"); +} + +@font-face { + font-family: "Inter"; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url("../assets/fonts/inter/inter-latin-400-italic.woff2") format("woff2"); +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url("../assets/fonts/inter/inter-latin-600-normal.woff2") format("woff2"); +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url("../assets/fonts/inter/inter-latin-700-normal.woff2") format("woff2"); +} + +@font-face { + font-family: "Inter"; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url("../assets/fonts/inter/inter-latin-700-italic.woff2") format("woff2"); +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 800; + font-display: swap; + src: url("../assets/fonts/inter/inter-latin-800-normal.woff2") format("woff2"); +} + +/* Material compose le texte avec --md-text-font, suivi des polices du système en repli */ +:root { + --md-text-font: "Inter"; +} diff --git a/docs/stylesheets/print.css b/docs/stylesheets/print.css index 55ddffb..f101cac 100644 --- a/docs/stylesheets/print.css +++ b/docs/stylesheets/print.css @@ -23,35 +23,74 @@ @bottom-right { content: counter(page) " / " counter(pages); - font: 8pt Roboto, "Helvetica Neue", Arial, sans-serif; + font: 8pt Inter, "Helvetica Neue", Arial, sans-serif; color: #64748b; } } /* Gabarit nommé du livre blanc : pied de page identifiant le document */ @page whitepaper { + @top-right { + content: string(section); + font: 8pt Inter, "Helvetica Neue", Arial, sans-serif; + color: #64748b; + } + @bottom-left { content: "OTSPI — Livre blanc · document de consultation v0.9 · CC-BY-4.0"; - font: 8pt Roboto, "Helvetica Neue", Arial, sans-serif; + font: 8pt Inter, "Helvetica Neue", Arial, sans-serif; color: #64748b; } } @page whitepaper:first { + margin: 0; + @top-right { content: none; } @bottom-left { content: none; } @bottom-right { content: none; } } /* Gabarit anglais du livre blanc */ @page whitepaper-en { + @top-right { + content: string(section); + font: 8pt Inter, "Helvetica Neue", Arial, sans-serif; + color: #64748b; + } + @bottom-left { content: "OTSPI — White paper · public consultation v0.9 · CC-BY-4.0"; - font: 8pt Roboto, "Helvetica Neue", Arial, sans-serif; + font: 8pt Inter, "Helvetica Neue", Arial, sans-serif; color: #64748b; } } @page whitepaper-en:first { + margin: 0; + @top-right { content: none; } + @bottom-left { content: none; } + @bottom-right { content: none; } +} + +/* Gabarit des textes juridiques exportés en PDF (statuts, règlement intérieur, charte) : la page de + garde est ajoutée par scripts/export_pdf.py et porte la mention du pied de page (data-footer). */ +@page otspidoc { + @top-right { + content: string(section); + font: 8pt Inter, "Helvetica Neue", Arial, sans-serif; + color: #64748b; + } + + @bottom-left { + content: string(doc-footer); + font: 8pt Inter, "Helvetica Neue", Arial, sans-serif; + color: #64748b; + } +} + +@page otspidoc:first { + margin: 0; + @top-right { content: none; } @bottom-left { content: none; } @bottom-right { content: none; } } @@ -84,6 +123,11 @@ padding: 0 !important; } + /* Espaceur de Material en tête d'article : il décalerait la page de garde à fond perdu */ + .md-content__inner::before { + display: none !important; + } + .md-grid { max-width: none !important; } @@ -124,7 +168,6 @@ .md-typeset .admonition, .md-typeset details, .md-typeset pre, - .md-typeset .mermaid, .md-typeset tr, .md-typeset img { break-inside: avoid; @@ -147,13 +190,35 @@ page: whitepaper-en; } - .wp-cover { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - min-height: 245mm; - text-align: center; + body:has(.doc-cover) { + page: otspidoc; + } + + .md-typeset .doc-cover { + string-set: doc-footer attr(data-footer); + } + + /* Texte juridique : les titres s'enchaînent sans saut de page, l'en-tête courant suit les sections */ + .md-typeset .doc-cover ~ h2 { + break-before: auto; + margin-top: 9mm; + } + + .md-typeset .doc-cover ~ .doc-section { + string-set: section content(); + } + + /* Page de garde à fond perdu, aux couleurs du logo en version claire + (son fond #0b1120 se confond avec celui de la page) */ + .md-typeset .wp-cover { + display: block; + position: relative; + box-sizing: border-box; + height: 297mm; + margin: 0; + padding: 22mm 24mm 20mm; + background: #0b1120; + color: #f8fafc; break-after: page; } @@ -162,38 +227,59 @@ } .md-typeset .wp-cover .wp-cover-logo { - width: 55mm; - margin-bottom: 18mm; + display: block; + width: 58mm; + margin: 0 0 0 -8mm; } .md-typeset .wp-cover .wp-cover-kicker { + margin-top: 32mm; + margin-bottom: 7mm; + color: #38bdf8; font-size: 11pt; font-weight: 700; - letter-spacing: 0.2em; + letter-spacing: 0.24em; text-transform: uppercase; - color: var(--otspi-primary); - margin-bottom: 6mm; } .md-typeset .wp-cover .wp-cover-title { - font-size: 22pt; - font-weight: 700; - line-height: 1.25; - color: #0f172a; max-width: 150mm; - margin-bottom: 14mm; + color: #ffffff; + font-size: 30pt; + font-weight: 800; + line-height: 1.15; + letter-spacing: -0.015em; + } + + .md-typeset .wp-cover .wp-cover-title::after { + content: ""; + display: block; + width: 28mm; + height: 1.5mm; + margin-top: 11mm; + background: #fbbf24; } + /* Mentions ancrées en bas de page (l'ordre de lecture reste celui du document) */ .md-typeset .wp-cover .wp-cover-meta { + position: absolute; + right: 24mm; + bottom: 30mm; + left: 24mm; + padding-top: 6mm; + border-top: 0.5pt solid #334155; + color: #cbd5e1; font-size: 10.5pt; - color: #334155; line-height: 1.7; } .md-typeset .wp-cover .wp-cover-license { - margin-top: 24mm; + position: absolute; + right: 24mm; + bottom: 20mm; + left: 24mm; + color: #94a3b8; font-size: 8.5pt; - color: #64748b; } /* Le titre et le sous-titre écran sont remplacés par la page de garde */ @@ -224,20 +310,187 @@ .wp-cover ~ hr { display: none; } + + /* Typographie du livre blanc */ + /* Trait d'union ASCII pour la césure : le trait typographique (U+2010) manque à la police */ + .md-typeset:has(> .wp-cover) { + color: #1e293b; + hyphenate-character: "-"; + } + + .md-typeset:has(> .wp-cover) p, + .md-typeset:has(> .wp-cover) li { + text-align: justify; + hyphens: auto; + } + + /* Colonnes étroites et page de garde : le justifié y creuserait des blancs */ + .md-typeset .wp-cover p, + .md-typeset .grid.cards.wp-figures p, + .md-typeset .grid.cards.wp-figures li { + text-align: left; + hyphens: manual; + } + + .md-typeset .wp-cover ~ h2 { + margin: 0 0 6mm; + padding-bottom: 2.5mm; + border-bottom: 1.5pt solid var(--otspi-primary); + color: #0f2a6b; + font-size: 18pt; + font-weight: 700; + line-height: 1.25; + letter-spacing: -0.01em; + string-set: section content(); + } + + /* Le sous-titre masqué ne doit pas alimenter l'en-tête courant */ + .md-typeset .wp-cover ~ h1 + h2 { + string-set: section ""; + } + + .md-typeset .wp-cover ~ h3 { + margin: 7mm 0 2.5mm; + color: var(--otspi-primary); + font-size: 12.5pt; + font-weight: 700; + line-height: 1.3; + } + + .md-typeset .wp-cover ~ h4 { + margin: 5mm 0 2mm; + color: #1e293b; + font-size: 10.5pt; + font-weight: 700; + } + + /* Les titres ne restent jamais seuls en bas de page */ + .md-typeset .wp-cover ~ h3, + .md-typeset .wp-cover ~ h4 { + break-after: avoid; + break-inside: avoid; + } + + /* Encadrés : filet de couleur, sans ombre ni arrondi */ + .md-typeset:has(> .wp-cover) .admonition, + .md-typeset:has(> .wp-cover) details { + margin: 5mm 0; + border-width: 0 0 0 2.5pt; + border-radius: 0; + box-shadow: none; + font-size: 9pt; + } + + .md-typeset:has(> .wp-cover) .admonition-title, + .md-typeset:has(> .wp-cover) summary { + font-size: 9pt; + } + + /* Tableaux */ + .md-typeset:has(> .wp-cover) table:not([class]) { + border: none; + border-top: 1.5pt solid var(--otspi-primary); + border-bottom: 0.75pt solid #94a3b8; + border-radius: 0; + box-shadow: none; + } + + .md-typeset:has(> .wp-cover) table:not([class]) th { + background: #eef2fb; + color: #0f2a6b; + font-weight: 700; + } + + .md-typeset:has(> .wp-cover) table:not([class]) th, + .md-typeset:has(> .wp-cover) table:not([class]) td { + padding: 1.6mm 2.5mm; + border-top: 0.5pt solid #e2e8f0; + text-align: left; + hyphens: auto; + } + + .md-typeset:has(> .wp-cover) table:not([class]) thead { + display: table-header-group; + } + + .md-typeset:has(> .wp-cover) table:not([class]) tbody tr:nth-child(even) { + background: #f8fafc; + } + + .md-typeset .wp-meta table:not([class]) { + font-size: 9pt; + } + + /* Notes de bas de page */ + .md-typeset:has(> .wp-cover) .footnote { + font-size: 8pt; + color: #334155; + } + + .md-typeset:has(> .wp-cover) .footnote li { + text-align: left; + } + + /* Sommaire */ + .md-typeset .wp-toc h2, + .md-typeset .wp-toc h3 { + margin: 0 0 8mm; + padding-bottom: 2.5mm; + border-bottom: 1.5pt solid var(--otspi-primary); + color: #0f2a6b; + font-size: 18pt; + font-weight: 700; + } + + .md-typeset .wp-toc li { + margin: 0; + padding: 2.2mm 0; + border-bottom: 0.5pt solid #e2e8f0; + font-size: 10.5pt; + text-align: left; + } + + .md-typeset .wp-toc a { + color: #1e293b; + text-decoration: none !important; + } } /* Grille des chiffres clés du livre blanc */ @media print { + /* La grille porte sur la liste : posée sur le conteneur, elle tasserait les six cartes + dans la première colonne. */ .md-typeset .grid.cards.wp-figures { + display: block; + } + + .md-typeset .grid.cards.wp-figures > ul { display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 0.6rem; + grid-template-columns: repeat(3, 1fr); + gap: 4mm; + margin: 0; + padding: 0; + list-style: none; } - .md-typeset .grid.cards.wp-figures > ul > li, - .md-typeset .grid.cards.wp-figures li { + .md-typeset .grid.cards.wp-figures > ul > li { + margin: 0; + padding: 3.5mm 4mm; + border: 0.5pt solid #cbd5e1; + border-top: 2.5pt solid var(--otspi-primary); + border-radius: 0; + box-shadow: none; + background: #f8fafc; + font-size: 8.5pt; + line-height: 1.4; break-inside: avoid; } + + .md-typeset .grid.cards.wp-figures .wp-value { + color: var(--otspi-primary); + font-size: 17pt; + line-height: 1.15; + } } /* Schémas SVG du livre blanc : ne pas les couper d'une page à l'autre */ @@ -250,4 +503,14 @@ .md-typeset .wp-svg { min-width: 0; } + + .md-typeset .wp-diagram figcaption { + margin-top: 3mm; + color: #475569; + font-size: 8.5pt; + } + + .md-typeset .wp-fig-num { + color: #0f2a6b; + } } diff --git a/docs/white-paper/index.md b/docs/white-paper/index.md index 29eb7a4..4c5e3af 100644 --- a/docs/white-paper/index.md +++ b/docs/white-paper/index.md @@ -8,7 +8,7 @@ description: "White paper of the Open Trusted Service Provider Initiative: marke
-![OTSPI](../assets/logo-vertical.svg){ .wp-cover-logo } +![OTSPI](../assets/logo-vertical-dark.svg){ .wp-cover-logo }

White paper

A public-interest qualified trust service infrastructure for eIDAS 2.0

@@ -26,6 +26,8 @@ description: "White paper of the Open Trusted Service Provider Initiative: marke

The PDF is tagged and validated as PDF/UA-1 by the veraPDF checker; this automated validation does not replace testing with a screen reader. This page (HTML) remains the accessible reference version.

+
+ | | | |---|---| | **Issuer** | Open Trusted Service Provider Initiative (OTSPI), non-profit association under the French law of 1901, currently being formed | @@ -35,6 +37,8 @@ description: "White paper of the Open Trusted Service Provider Initiative: marke | **Licence** | Creative Commons Attribution 4.0 International (CC-BY-4.0) | | **Contact** | [contact@otspi.org](mailto:contact@otspi.org) | +
+ !!! note "Nature of this document" This white paper sets out an intention and a target architecture. It is neither a Time-Stamping Policy, nor a Certification Practice Statement (CPS), nor a contractual service commitment. References to products or suppliers are given for information only; their final selection will be subject to competitive procedures and to the approval of the Trust Policy Committee (CPC). @@ -377,8 +381,9 @@ Any vendor of a signing solution, commercial or free, can rely on the service to ### 4.1. Certification hierarchy -
+
OTSPI qualified certification hierarchyThe offline OTSPI root CA, under a quorum, certifies the time-stamping intermediate CA, which certifies time-stamping units TSU-1 (site A) and TSU-2 (site B). It will also certify future qualified CAs for seals, signatures and attestations.OTSPI root CAoffline · air-gapped · M-of-N quorumTime-stamping intermediate CAoffline or restricted onlineFuture qualified CAsseal · signature · attestationsTime-stamping unit TSU-1online HSM — site ATime-stamping unit TSU-2online HSM — site B +
Figure 1 — OTSPI qualified certification hierarchy
@@ -386,11 +391,12 @@ Any vendor of a signing solution, commercial or free, can rely on the service to - Each **time-stamping unit (TSU)** has its own key, reserved exclusively for signing time-stamp tokens, generated and stored in a certified cryptographic module. - The usage period of TSU keys is shorter than the validity of their certificate, in accordance with ETSI EN 319 421, so that tokens issued at the end of the period remain verifiable. - The future **qualified CAs** for seals, signatures and attestations (see § 6.1, phase 5) will be attached to this same qualified root, each under an intermediate CA dedicated to a single use. -- The future TLS certificate service (see § 2.4) relies on **two roots distinct** from the qualified time-stamping root: a **WebTrust root**, intended for the trust stores of operating systems and browsers, and a **QWAC root**, listed on the European trusted list. They apply the same governance principles (air gap, quorum, ceremonies) and share no key with the time-stamping, seal or signature CAs. +- The future TLS certificate service (see § 2.4) relies on **two roots distinct** from the qualified time-stamping root ([figure 2](#figure-2)): a **WebTrust root**, intended for the trust stores of operating systems and browsers, and a **QWAC root**, listed on the European trusted list. They apply the same governance principles (air gap, quorum, ceremonies) and share no key with the time-stamping, seal or signature CAs. -
+
OTSPI TLS certification hierarchiesTwo separate roots: a WebTrust root for browser stores, and a QWAC root listed on the European trusted list. The WebTrust root certifies a DV sub-CA, which issues DV server certificates through instant ACME. A hybrid OV / QWAC sub-CA, holding a single key, is certified by the WebTrust root and, through cross-signing, by the QWAC root; it issues OV / QWAC certificates.cross-signingWebTrust rootOS and browser storesQWAC rootEuropean trusted listDV sub-CAWebTrust only · standard HSMHybrid OV / QWAC sub-CAone key · two CA certificatesDV server certificatesgeneral Web use · instant ACMEOV / QWAC server certificatesPSD2, eIDAS · ACME with account binding +
Figure 2 — OTSPI TLS certification hierarchies
diff --git a/mkdocs.yml b/mkdocs.yml index f72dab9..3fbb9f9 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -16,20 +16,21 @@ theme: name: material custom_dir: overrides language: fr + # Couleurs de la charte OTSPI (bleu nuit du logo) : voir docs/stylesheets/extra.css palette: # Mode clair - media: "(prefers-color-scheme: light)" scheme: default - primary: indigo - accent: indigo + primary: custom + accent: custom toggle: icon: material/brightness-7 name: Passer au mode sombre # Mode sombre - media: "(prefers-color-scheme: dark)" scheme: slate - primary: indigo - accent: indigo + primary: custom + accent: custom toggle: icon: material/brightness-4 name: Passer au mode clair @@ -37,7 +38,8 @@ theme: favicon: assets/favicon.svg icon: repo: fontawesome/brands/github - # Polices du système : aucune requête vers Google Fonts (l'adresse IP du visiteur y serait transmise) + # Pas de Google Fonts (l'adresse IP du visiteur y serait transmise) : la police Inter est + # auto-hébergée (stylesheets/fonts.css) font: false features: - navigation.tabs @@ -57,10 +59,11 @@ theme: - content.action.view extra_javascript: - - javascripts/a11y.js + - javascripts/a11y.js?v=2 - javascripts/analytics.js?v=2 extra_css: + - stylesheets/fonts.css - stylesheets/extra.css - stylesheets/print.css @@ -79,11 +82,7 @@ markdown_extensions: emoji_index: !!python/name:material.extensions.emoji.twemoji emoji_generator: !!python/name:material.extensions.emoji.to_svg - pymdownx.details - - pymdownx.superfences: - custom_fences: - - name: mermaid - class: mermaid - format: !!python/name:pymdownx.superfences.fence_code_format + - pymdownx.superfences - pymdownx.highlight: anchor_linenums: true - pymdownx.inlinehilite @@ -98,6 +97,8 @@ markdown_extensions: permalink: true extra: + # Pas de mention « Made with Material for MkDocs » en pied de page + generator: false social: - icon: fontawesome/brands/github link: https://github.com/otspi/organisation @@ -111,30 +112,30 @@ extra: nav: - Accueil: index.md - - Livre Blanc: + - Livre blanc: - Livre blanc (FR): livre-blanc/index.md - White Paper (EN): white-paper/index.md - Historique des versions: livre-blanc/historique.md - - Statuts & Gouvernance: + - Statuts et gouvernance: - Projet de statuts: statuts/statuts-association.md - - Charte d'Éthique: gouvernance/charte-ethique.md - - Organisation Technique & CPC: gouvernance/comite-technique.md - - Règlement Intérieur: - - Règlement Intérieur: reglement-interieur/reglement-interieur.md - - Démarches Administratives: - - Procès-Verbal Constitutif (Modèle): administratif/pv-ag-constitutive-modele.md - - Demande de Rescrit Fiscal DGFIP: administratif/rescrit-fiscal-mecenat.md + - Charte d'éthique: gouvernance/charte-ethique.md + - Organisation technique et CPC: gouvernance/comite-technique.md + - Règlement intérieur: + - Règlement intérieur: reglement-interieur/reglement-interieur.md + - Démarches administratives: + - Procès-verbal constitutif (modèle): administratif/pv-ag-constitutive-modele.md + - Demande de rescrit fiscal DGFIP: administratif/rescrit-fiscal-mecenat.md - Dossier de constitution: administratif/dossier-constitution.md - Résolution d'amorçage (CPC et Bureau): administratif/resolution-transitoire.md - - Guide Démarches Préfecture (RNA, SIRET): administratif/declaration-prefecture.md - - Socle Technique & Audit (TSP / PKI): + - Démarches en préfecture (RNA, SIRET): administratif/declaration-prefecture.md + - Socle technique et audit (TSP / PKI): - Vue d'ensemble: cadrage/index.md - Cadre CP/CPS (RFC 3647): cadrage/cp-cps-cadre.md - - Politique de Sécurité des SI (PSSI): cadrage/pssi.md - - Plan de Fin d'Activité (Termination Plan): cadrage/termination-plan.md + - Politique de sécurité des SI (PSSI): cadrage/pssi.md + - Plan de fin d'activité (Termination Plan): cadrage/termination-plan.md - Arc d'OID et numéro IANA: cadrage/oid-arc.md - Demandes de devis (audit, HSM, hébergement): cadrage/demandes-de-devis.md - Transparence: - Hébergements: reunions/hebergements.md - Hosting transparency (EN): reunions/hosting.md - - Registre des Réunions & PV: reunions/index.md + - Registre des réunions et PV: reunions/index.md diff --git a/overrides/404.html b/overrides/404.html new file mode 100644 index 0000000..93cdedb --- /dev/null +++ b/overrides/404.html @@ -0,0 +1,20 @@ +{% extends "main.html" %} + +{#- Page d'erreur servie pour les 404 et les 403 (deploy/o2switch/htaccess) : bilingue, puisque + le visiteur peut venir de la version anglaise du livre blanc. -#} + +{% block content %} +
+ +

Page introuvable

+

La page demandée n'existe pas ou a été déplacée. Les documents de l'association restent accessibles depuis les rubriques ci-dessous ou par la recherche.

+

Page not found. The requested page does not exist or has moved.

+ +
+{% endblock %} diff --git a/overrides/home.html b/overrides/home.html new file mode 100644 index 0000000..39b6507 --- /dev/null +++ b/overrides/home.html @@ -0,0 +1,30 @@ +{% extends "main.html" %} + +{#- Page d'accueil : bandeau pleine largeur sous l'en-tête, puis le contenu de docs/index.md + sans le titre automatique ni les boutons d'édition. -#} + +{% block hero %} +
+
+
+

Association d'intérêt général (loi 1901) en cours de constitution

+

Open Trusted Service Provider Initiative

+

+ Construire, opérer et pérenniser des infrastructures de confiance numérique + ouvertes, souveraines et universellement accessibles, conformes aux standards + eIDAS 2.0, ETSI et WebTrust, sous une gouvernance strictement désintéressée. +

+ +
+ +
+
+{% endblock %} + +{% block content %} + {{ page.content }} +{% endblock %} diff --git a/overrides/main.html b/overrides/main.html index 90c9ee1..53bb72d 100644 --- a/overrides/main.html +++ b/overrides/main.html @@ -2,23 +2,27 @@ {% block extrahead %} {{ super() }} - {% if page and page.meta and page.meta.image %} + {#- Image de partage : celle de la page si elle en déclare une (livre blanc), sinon l'image par + défaut du portail (scripts/build_og_image.py) -#} + {% if page and page.meta is defined %} {% set og_title = page.meta.title or page.title %} {% set og_desc = page.meta.description or config.site_description %} - + {% set og_image = page.meta.image or (config.site_url ~ "assets/og/og-portail.png") %} + {% set og_image_alt = page.meta.image_alt or "OTSPI, Open Trusted Service Provider Initiative : portail de l'association" %} + - + - + - + {% endif %} {% endblock %} diff --git a/overrides/partials/actions.html b/overrides/partials/actions.html new file mode 100644 index 0000000..58b7c11 --- /dev/null +++ b/overrides/partials/actions.html @@ -0,0 +1,28 @@ +{#- + Actions de page de Material (partials/actions.html), précédées d'un lien de téléchargement + lorsque la page déclare sa version PDF dans ses métadonnées (`pdf:`, voir scripts/export_pdf.py). +-#} +{% if page.meta and page.meta.pdf %} + + {% include ".icons/material/file-pdf-box.svg" %} + +{% endif %} +{% if page.edit_url %} + {% if "content.action.edit" in features %} + + {% set icon = config.theme.icon.edit or "material/file-edit-outline" %} + {% include ".icons/" ~ icon ~ ".svg" %} + + {% endif %} + {% if "content.action.view" in features %} + {% if "/blob/" in page.edit_url %} + {% set part = "blob" %} + {% else %} + {% set part = "edit" %} + {% endif %} + + {% set icon = config.theme.icon.view or "material/file-eye-outline" %} + {% include ".icons/" ~ icon ~ ".svg" %} + + {% endif %} +{% endif %} diff --git a/scripts/build_diagrams.py b/scripts/build_diagrams.py index a9cc949..dc1cced 100644 --- a/scripts/build_diagrams.py +++ b/scripts/build_diagrams.py @@ -1,7 +1,8 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: EUPL-1.2 -"""Génère les deux schémas du livre blanc (hiérarchie qualifiée, hiérarchies TLS) en SVG intégré, -en français et en anglais, et les insère entre les marqueurs des pages du livre blanc. +"""Génère les schémas du portail en SVG intégré et les insère entre les marqueurs des pages : +les deux schémas du livre blanc (hiérarchie qualifiée, hiérarchies TLS), en français et en anglais, +et l'organigramme de gouvernance de la page d'accueil. Les schémas n'utilisent aucun script : ils remplacent des blocs Mermaid, dont la bibliothèque (environ 950 Ko, chargée depuis un service tiers) ralentissait fortement la page. @@ -112,8 +113,62 @@ def tls(t, uid): return "".join(parts) -def figure(svg): - return f'
\n{svg}\n
' +GOVERNANCE = { + "title": "Organisation de la gouvernance d'OTSPI", + "desc": "L'assemblée générale élit le conseil d'administration, qui désigne le bureau (direction opérationnelle) ; " + "le bureau assure les relations avec les autorités de contrôle et les auditeurs. L'assemblée générale confirme " + "aussi les nominations au comité des politiques de confiance (CPC), autorité normative indépendante du bureau, " + "qui désigne le RSSI pour un an, habilite et contrôle les officiers d'autorité et rend un avis conforme sur les " + "travaux du comité technique ; le bureau fournit à ce comité ses moyens.", + "ag": ("Assemblée générale", "titulaires et sympathisants"), + "ca": ("Conseil d'administration", "2 à 9 membres"), + "bureau": ("Bureau", "présidence · trésorerie · secrétariat"), + "cab": ("Autorités de contrôle", "et auditeurs (CAB)"), + "cpc": ("Comité des politiques de confiance", "CPC / PMA"), + "tsc": ("Comité technique (TSC)", "avis CPC · moyens du Bureau"), + "rssi": ("RSSI / CISO", "désigné par le CPC, 1 an"), + "oa": ("Officiers d'autorité", "et gardiens de clés"), + "left": "Direction opérationnelle", + "right": "Autorité normative indépendante", + "confirm": "confirmation des nominations", +} + + +def governance(t, uid): + parts = [ + f'', + f'{escape(t["title"])}{escape(t["desc"])}', + defs(uid), + '', + f'{escape(t["left"].upper())}', + '', + f'{escape(t["right"].upper())}', + f'', + f'', + f'{escape(t["confirm"])}', + f'', + f'', + f'', + f'', + f'', + f'', + box(330, 10, 240, 64, t["ag"], "dg-root"), + box(40, 110, 240, 64, t["ca"], "dg-box"), + box(30, 230, 260, 64, t["bureau"], "dg-box"), + box(30, 366, 260, 64, t["cab"], "dg-box"), + box(500, 230, 260, 64, t["cpc"], "dg-root"), + box(342, 366, 186, 64, t["tsc"], "dg-box"), + box(538, 366, 186, 64, t["rssi"], "dg-box"), + box(734, 366, 186, 64, t["oa"], "dg-box"), + '', + ] + return "".join(parts) + + +def figure(svg, number, title): + """Enveloppe un schéma dans une figure légendée et numérotée, ancrée sur #figure-N pour les renvois.""" + return (f'
\n{svg}\n' + f'
Figure {number} — {escape(title)}
\n
') def replace_block(text, name, block): @@ -126,11 +181,17 @@ def main(): for lang, path in (("fr", "docs/livre-blanc/index.md"), ("en", "docs/white-paper/index.md")): page = ROOT / path text = page.read_text(encoding="utf-8") - text = replace_block(text, "hierarchy", figure(hierarchy(TEXT[lang], f"{lang}-h"))) - text = replace_block(text, "tls", figure(tls(TEXT[lang], f"{lang}-t"))) + text = replace_block(text, "hierarchy", figure(hierarchy(TEXT[lang], f"{lang}-h"), 1, TEXT[lang]["h_title"])) + text = replace_block(text, "tls", figure(tls(TEXT[lang], f"{lang}-t"), 2, TEXT[lang]["t_title"])) page.write_text(text, encoding="utf-8") print(f"{path} : schémas régénérés") + home = ROOT / "docs/index.md" + text = replace_block(home.read_text(encoding="utf-8"), "governance", + f'
\n{governance(GOVERNANCE, "home-g")}\n
') + home.write_text(text, encoding="utf-8") + print("docs/index.md : organigramme régénéré") + if __name__ == "__main__": main() diff --git a/scripts/build_og_image.py b/scripts/build_og_image.py new file mode 100644 index 0000000..68b31dd --- /dev/null +++ b/scripts/build_og_image.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: EUPL-1.2 +"""Génère l'image de partage par défaut du portail (Open Graph, 1200 × 630 px) avec Chrome headless. + +Elle sert aux pages sans image propre (voir overrides/main.html) ; les pages du livre blanc gardent +la leur. Mise en page aux couleurs de la couverture du livre blanc, police Inter du portail. + +Usage : + python3 scripts/build_og_image.py # écrit docs/assets/og/og-portail.png + +La variable d'environnement CHROME permet d'imposer le binaire du navigateur. +""" + +import os +import shutil +import subprocess +import sys +import tempfile +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +OUTPUT = ROOT / "docs/assets/og/og-portail.png" +FONTS = ROOT / "docs/assets/fonts/inter" +LOGO = ROOT / "docs/assets/logo-vertical-dark.svg" + +HTML = """ + + +
+

Portail de l'association

+

Open Trusted Service Provider Initiative

+
+

Statuts, gouvernance, livre blanc et socle de conformité des services de confiance eIDAS 2.0

+

about.otspi.org

+
+ +""" + + +def find_chrome(): + if os.environ.get("CHROME"): + return os.environ["CHROME"] + for candidate in ("google-chrome", "google-chrome-stable", "chromium", "chromium-browser"): + path = shutil.which(candidate) + if path: + return path + sys.exit("Chrome ou Chromium introuvable : définir la variable d'environnement CHROME.") + + +def main(): + OUTPUT.parent.mkdir(parents=True, exist_ok=True) + with tempfile.TemporaryDirectory() as work: + page = Path(work) / "og.html" + page.write_text(HTML.format(fonts=FONTS.as_uri(), logo=LOGO.as_uri()), encoding="utf-8") + subprocess.run( + [find_chrome(), "--headless=new", "--disable-gpu", "--hide-scrollbars", f"--user-data-dir={work}/profile", + "--window-size=1200,630", "--virtual-time-budget=3000", f"--screenshot={OUTPUT}", page.as_uri()], + check=True, capture_output=True, timeout=120, + ) + print(f"Image générée : {OUTPUT.relative_to(ROOT)}") + + +if __name__ == "__main__": + main() diff --git a/scripts/check_a11y.js b/scripts/check_a11y.js new file mode 100644 index 0000000..8099080 --- /dev/null +++ b/scripts/check_a11y.js @@ -0,0 +1,82 @@ +#!/usr/bin/env node +// SPDX-License-Identifier: EUPL-1.2 +// Contrôle d'accessibilité (WCAG 2 AA, moteur axe) de toutes les pages du site construit, listées par +// son sitemap.xml. Le site est servi localement par ce script. +// +// axe distingue les violations confirmées des cas qu'il ne sait pas trancher (texte des schémas SVG, +// libellés tronqués…) : seules les premières font échouer le contrôle, les secondes sont listées. +// +// Usage : +// npm install --no-save pa11y@9 +// node scripts/check_a11y.js [site] +// +// La variable d'environnement CHROME permet d'imposer le navigateur (sinon celui de Puppeteer). + +const fs = require("fs"); +const http = require("http"); +const path = require("path"); +const pa11y = require("pa11y"); + +const SITE = path.resolve(process.argv[2] || "site"); +const TYPES = { + ".html": "text/html; charset=utf-8", ".css": "text/css", ".js": "text/javascript", ".json": "application/json", + ".svg": "image/svg+xml", ".png": "image/png", ".woff2": "font/woff2", ".xml": "application/xml", + ".pdf": "application/pdf", +}; + +function serve() { + const server = http.createServer((req, res) => { + let file = path.join(SITE, decodeURIComponent(new URL(req.url, "http://x").pathname)); + if (!file.startsWith(SITE)) { + res.writeHead(403).end(); + return; + } + if (fs.existsSync(file) && fs.statSync(file).isDirectory()) file = path.join(file, "index.html"); + fs.readFile(file, (error, data) => { + if (error) { + res.writeHead(404).end(); + return; + } + res.writeHead(200, { "Content-Type": TYPES[path.extname(file)] || "application/octet-stream" }).end(data); + }); + }); + return new Promise((resolve) => server.listen(0, "127.0.0.1", () => resolve(server))); +} + +(async () => { + const sitemap = fs.readFileSync(path.join(SITE, "sitemap.xml"), "utf-8"); + const pages = [...sitemap.matchAll(/([^<]+)<\/loc>/g)].map((m) => new URL(m[1]).pathname); + const server = await serve(); + const base = `http://127.0.0.1:${server.address().port}`; + const launch = { args: ["--no-sandbox"] }; + if (process.env.CHROME) launch.executablePath = process.env.CHROME; + + let confirmed = 0; + const review = new Map(); + try { + for (const page of pages) { + const result = await pa11y(base + page, { runners: ["axe"], standard: "WCAG2AA", timeout: 60000, chromeLaunchConfig: launch }); + for (const issue of result.issues) { + const context = issue.context.replace(/\s+/g, " ").slice(0, 90); + if (issue.runnerExtras && issue.runnerExtras.needsFurtherReview) { + review.set(`${issue.code} : ${page}`, (review.get(`${issue.code} : ${page}`) || 0) + 1); + continue; + } + confirmed += 1; + console.log(`ÉCHEC ${page} — ${issue.code} : ${issue.message.split(" (")[0]}\n ${issue.selector}\n ${context}`); + } + } + } finally { + server.close(); + } + + if (review.size) { + console.log("\nÀ examiner (axe n'a pas pu trancher, sans effet sur le résultat) :"); + for (const [key, count] of review) console.log(` ${key} (${count})`); + } + console.log(`\n${pages.length} pages contrôlées, ${confirmed} violation(s) confirmée(s).`); + process.exit(confirmed ? 1 : 0); +})().catch((error) => { + console.error(error); + process.exit(2); +}); diff --git a/scripts/check_pdf_ua.sh b/scripts/check_pdf_ua.sh index 48ce042..3c5d966 100755 --- a/scripts/check_pdf_ua.sh +++ b/scripts/check_pdf_ua.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # SPDX-License-Identifier: EUPL-1.2 -# Valide au format PDF/UA-1 les PDF du livre blanc générés dans ./site avec veraPDF. +# Valide au format PDF/UA-1 les PDF du portail générés dans ./site avec veraPDF. # Retourne un code d'erreur si un PDF n'est pas conforme. # # veraPDF (licence GPLv3 et MPL 2.0) est téléchargé en version épinglée, vérifié par empreinte SHA-256, @@ -38,7 +38,10 @@ fi if [ "$#" -gt 0 ]; then files=("$@") else - files=(site/livre-blanc/otspi-livre-blanc.pdf site/white-paper/otspi-white-paper.pdf) + files=(site/livre-blanc/otspi-livre-blanc.pdf site/white-paper/otspi-white-paper.pdf + site/statuts/statuts-association/otspi-projet-de-statuts.pdf + site/reglement-interieur/reglement-interieur/otspi-reglement-interieur.pdf + site/gouvernance/charte-ethique/otspi-charte-ethique.pdf) fi status=0 diff --git a/scripts/export_pdf.py b/scripts/export_pdf.py index 0c429e7..06e53d4 100644 --- a/scripts/export_pdf.py +++ b/scripts/export_pdf.py @@ -1,7 +1,10 @@ #!/usr/bin/env python3 -"""Génère les PDF de référence du livre blanc (français et anglais) à partir du site MkDocs construit. +"""Génère les PDF de référence du portail à partir du site MkDocs construit : le livre blanc (français et +anglais), le projet de statuts, le règlement intérieur et la charte d'éthique. Le site est servi localement puis imprimé ; la mise en page est portée par docs/stylesheets/print.css. +Le livre blanc porte sa page de garde et son sommaire dans sa source ; pour les textes juridiques, qu'on ne +modifie pas pour les besoins de l'impression, le script les ajoute au HTML avant l'impression (WeasyPrint). Deux moteurs sont disponibles : - weasyprint (par défaut) : PDF balisés, quasi conformes à PDF/UA-1 (voir requirements-pdf.txt) ; @@ -17,6 +20,7 @@ import argparse import base64 +import datetime import functools import http.server import os @@ -27,15 +31,55 @@ import tempfile import threading import urllib.request +from html import escape from pathlib import Path CHROME_CANDIDATES = ("google-chrome", "google-chrome-stable", "chromium", "chromium-browser") -# Pages exportées : répertoire de la page dans le site construit → nom du PDF +ROOT = Path(__file__).resolve().parent.parent + +# Pages exportées : répertoire de la page dans le site construit → PDF. Les textes juridiques reçoivent une +# page de garde (cover) et un sommaire des titres de niveau `section` ; `source` date la version. DOCUMENTS = { - "livre-blanc/": "otspi-livre-blanc.pdf", - "white-paper/": "otspi-white-paper.pdf", + "livre-blanc/": {"pdf": "otspi-livre-blanc.pdf"}, + "white-paper/": {"pdf": "otspi-white-paper.pdf"}, + "statuts/statuts-association/": { + "pdf": "otspi-projet-de-statuts.pdf", + "source": "docs/statuts/statuts-association.md", + "section": 3, + "cover": { + "kicker": "Projet de statuts", + "title": "Statuts de l'association Open Trusted Service Provider Initiative (OTSPI)", + "status": "Projet soumis à l'assemblée générale constitutive, non encore adopté", + "footer": "OTSPI — Projet de statuts, non adopté · CC-BY-4.0", + }, + }, + "reglement-interieur/reglement-interieur/": { + "pdf": "otspi-reglement-interieur.pdf", + "source": "docs/reglement-interieur/reglement-interieur.md", + "section": 2, + "cover": { + "kicker": "Règlement intérieur", + "title": "Règlement intérieur de l'association Open Trusted Service Provider Initiative (OTSPI)", + "status": "Projet, en application de l'article 14 du projet de statuts", + "footer": "OTSPI — Règlement intérieur, projet · CC-BY-4.0", + }, + }, + "gouvernance/charte-ethique/": { + "pdf": "otspi-charte-ethique.pdf", + "source": "docs/gouvernance/charte-ethique.md", + "section": 2, + "cover": { + "kicker": "Charte", + "title": "Charte d'éthique, de déontologie et de transparence publique", + "status": "Association en cours de constitution", + "footer": "OTSPI — Charte d'éthique · CC-BY-4.0", + }, + }, } +MONTHS = ("janvier", "février", "mars", "avril", "mai", "juin", "juillet", "août", "septembre", "octobre", + "novembre", "décembre") + class QuietHandler(http.server.SimpleHTTPRequestHandler): def log_message(self, *args): @@ -71,7 +115,7 @@ def print_pdf(url, output): f"--user-data-dir={profile}", "--no-pdf-header-footer", "--generate-pdf-document-outline", - # Laisse le temps au rendu des diagrammes Mermaid + # Laisse le temps au chargement complet de la page (polices, scripts) "--virtual-time-budget=20000", "--run-all-compositor-stages-before-draw", f"--print-to-pdf={output}", @@ -88,15 +132,15 @@ def print_pdf(url, output): .dg-box{fill:#fff;stroke:#b8bcc4;stroke-width:1.5} .dg-root{fill:#e8eefc;stroke:#1a3d8f;stroke-width:2} .dg-future{fill:none;stroke:#6b7280;stroke-width:1.5;stroke-dasharray:6 5} -.dg-t{fill:#1f2937;font-size:12.5px;font-weight:700;font-family:sans-serif} -.dg-s{fill:#4b5563;font-size:12px;font-family:sans-serif} +.dg-t{fill:#1f2937;font-size:12.5px;font-weight:700;font-family:Inter,sans-serif} +.dg-s{fill:#4b5563;font-size:12px;font-family:Inter,sans-serif} .dg-edge,.dg-cross{fill:none;stroke-width:1.8} .dg-edge{stroke:#4b5563} .dg-dash{stroke-dasharray:5 5} .dg-cross{stroke:#1a3d8f;stroke-dasharray:7 5} .dg-arrow{fill:#4b5563} .dg-arrow-cross{fill:#1a3d8f} -.dg-label{fill:#1a3d8f;font-size:12px;font-weight:700;font-family:sans-serif} +.dg-label{fill:#1a3d8f;font-size:12px;font-weight:700;font-family:Inter,sans-serif} """ @@ -129,7 +173,53 @@ def hoist_link_wrappers(html): ) -def print_weasyprint(url, output): +def version_date(source): + """Date du dernier commit du fichier source, en toutes lettres (date du jour à défaut).""" + result = subprocess.run(["git", "log", "-1", "--format=%cs", "--", source], cwd=ROOT, capture_output=True, text=True) + day = datetime.date.fromisoformat(result.stdout.strip()) if result.stdout.strip() else datetime.date.today() + return f"{day.day} {MONTHS[day.month - 1]} {day.year}" + + +def plain(fragment): + """Texte brut d'un fragment HTML de titre, sans le lien permanent « ¶ ».""" + return re.sub(r"<[^>]+>", "", re.sub(r'', "", fragment, flags=re.S)).strip() + + +def add_cover(html, doc): + """Ajoute une page de garde et un sommaire paginé à un texte juridique, sans toucher à sa source. + + La page de garde précède le titre de niveau 1 ; le sommaire suit ce titre (et son sous-titre), pour que + le premier titre du PDF reste de niveau 1. Les titres de section et de sous-section reçoivent les + classes doc-section et doc-sub, qui règlent l'en-tête courant et les signets (voir print.css). + """ + cover, level = doc["cover"], doc["section"] + start = html.index("", start) + len("") + subtitle = re.match(r"\s*", html[head_end:], re.S) + if subtitle: + head_end += subtitle.end() + + body = html[head_end:] + entries = [(m.group(1), plain(m.group(2))) for m in re.finditer(rf']*>(.*?)', body, re.S)] + body = re.sub(rf'' + '

' + f'

{escape(cover["kicker"])}

' + f'

{escape(cover["title"])}

' + f'

Open Trusted Service Provider Initiative (OTSPI)
{escape(cover["status"])}' + f'
Version du {version_date(doc["source"])}

' + '

Licence Creative Commons Attribution 4.0 International · contact@otspi.org

' + "
" + ) + toc = '

Sommaire

" + return html[:start] + page + html[start:head_end] + toc + body + + +def print_weasyprint(url, output, doc): """Imprime une page en PDF/UA-1 avec WeasyPrint ; renvoie None en cas de succès, sinon un message.""" try: import weasyprint @@ -139,6 +229,8 @@ def print_weasyprint(url, output): try: with urllib.request.urlopen(url) as response: html = hoist_link_wrappers(svg_to_img(response.read().decode("utf-8"))) + if "cover" in doc: + html = add_cover(html, doc) weasyprint.HTML(string=html, base_url=url).write_pdf(str(output), stylesheets=[override], pdf_variant="pdf/ua-1", pdf_identifier=True) except Exception as error: # noqa: BLE001 - toute erreur de rendu doit faire échouer l'export return str(error) @@ -158,7 +250,8 @@ def main(): httpd = serve(site) try: - for page_path, pdf_name in DOCUMENTS.items(): + for page_path, doc in DOCUMENTS.items(): + pdf_name = doc["pdf"] if not (site / page_path / "index.html").is_file(): sys.exit(f"Page introuvable : {site / page_path / 'index.html'} (lancer avec --build)") output = site / page_path / pdf_name @@ -168,7 +261,7 @@ def main(): result = print_pdf(url, output) error = result.stderr if result.returncode != 0 else None else: - error = print_weasyprint(url, output) + error = print_weasyprint(url, output, doc) if error or not output.is_file() or output.stat().st_size == 0: sys.stderr.write((error or "") + "\n") sys.exit(f"Échec de la génération du PDF : {pdf_name}") diff --git a/scripts/pdf-weasyprint.css b/scripts/pdf-weasyprint.css index d86eb10..5f4ff95 100644 --- a/scripts/pdf-weasyprint.css +++ b/scripts/pdf-weasyprint.css @@ -40,6 +40,8 @@ body { width: 1px !important; height: 1px !important; overflow: hidden !important; + padding: 0 !important; + border: 0 !important; break-before: auto !important; } @@ -65,17 +67,8 @@ body { padding-left: 0.6rem !important; } -/* Grille des chiffres clés : WeasyPrint ne gère pas la grille automatique de Material. */ -.md-typeset .grid.cards.wp-figures > ul { - display: grid !important; - grid-template-columns: repeat(2, 1fr) !important; - gap: 0.6rem !important; -} - -.md-typeset .grid.cards.wp-figures > ul > li { - margin: 0 !important; -} - +/* Grille des chiffres clés (trois colonnes, voir print.css) : WeasyPrint ne gère pas la grille + automatique de Material ni ses largeurs minimales. */ .md-typeset .grid.cards.wp-figures, .md-typeset .grid.cards.wp-figures > ul { width: 100% !important; @@ -83,19 +76,11 @@ body { box-sizing: border-box !important; } -.md-typeset .grid.cards.wp-figures > ul { - margin: 0 !important; - padding: 0 !important; - list-style: none !important; -} - .md-typeset .grid.cards.wp-figures > ul > li { - padding: 0.6rem 0.7rem !important; min-width: 0 !important; } .md-typeset .grid.cards.wp-figures .wp-value { - font-size: 1.25rem !important; overflow-wrap: anywhere; } @@ -127,3 +112,52 @@ body { .footnote li > p:first-child { display: inline !important; } + +/* Sommaire : numéro de page de chaque section, précédé de points de conduite. */ +.md-typeset .wp-toc a::after { + content: leader(".") target-counter(attr(href url), page); + color: #64748b; +} + +/* Signets du PDF : sections et annexes au premier niveau, sous-sections repliées. Le titre et le + sous-titre masqués n'en produisent pas : la page de garde et le titre du document en tiennent lieu. */ +.wp-cover ~ h1, +.wp-cover ~ h1 + h2 { + bookmark-level: none !important; +} + +.md-typeset .wp-toc h2, +.md-typeset .wp-toc h3, +.md-typeset .wp-cover ~ h2 { + bookmark-level: 1; +} + +.md-typeset .wp-cover ~ h2 { + bookmark-state: closed; +} + +.md-typeset .wp-cover ~ h3 { + bookmark-level: 2; +} + +.md-typeset .wp-cover ~ h4 { + bookmark-level: 3; +} + +/* Textes juridiques (page de garde ajoutée par export_pdf.py) : signets limités aux sections + (titres) et à leurs sous-sections (articles), quel que soit leur niveau de titre. */ +.md-typeset .doc-cover ~ h2, +.md-typeset .doc-cover ~ h3, +.md-typeset .doc-cover ~ h4, +.md-typeset .doc-cover ~ h5 { + bookmark-level: none; +} + +.md-typeset .doc-cover ~ .doc-section { + bookmark-level: 1; + bookmark-state: closed; +} + +.md-typeset .doc-cover ~ .doc-sub { + bookmark-level: 2; +}