Aller au contenu

PD-22 — Créer module de configuration avec validation (Joi) et support multi-environnements (.env)

EPIC de référence : PD-186 — BACKEND CORE


1. Objectif

Définir le contrat canonique d’un module de configuration applicative backend garantissant : - un chargement strictement déterministe de la configuration selon l’environnement, - une validation exhaustive et formelle de tous les paramètres via un schéma explicite (ex. Joi), - un échec immédiat et explicite au démarrage en cas de configuration invalide, - l’absence totale de comportements implicites ou silencieux, - une structuration stricte par namespaces, sans dette technique.

L’objectif est d’assurer robustesse opérationnelle, sécurité, auditabilité, maintenabilité du code et prévisibilité en production.


2. Périmètre / Hors périmètre

Périmètre

  • Chargement de configuration à partir de sources explicites (fichier .env.<APP_ENV>, variables d’environnement du processus, Vault).
  • Support de plusieurs environnements isolés (dev / test / prod).
  • Validation obligatoire et complète de la configuration avant toute utilisation.
  • Rejet du démarrage applicatif en cas d’erreur de configuration.
  • Accès centralisé, contrôlé et immuable à la configuration validée.
  • Journalisation maîtrisée des erreurs de configuration sans fuite de secrets.
  • Application stricte d’une convention de nommage par namespaces.

Hors périmètre

  • Implémentation technique des backends de secrets (Vault, KMS, HSM), leur déploiement et leur exploitation.
  • Rechargement dynamique ou à chaud de la configuration.
  • Configuration côté client / front-end.
  • Génération automatique de fichiers .env.
  • Injection de variables par CI/CD ou orchestrateur.

⚠️ Clarification : le choix normatif d’utiliser Vault comme source de secrets est dans le périmètre contractuel ; en revanche, l’implémentation et l’exploitation de Vault ne le sont pas.


3. Définitions

  • Configuration applicative : ensemble des paramètres requis pour l’exécution correcte du service.
  • Environnement applicatif : contexte d’exécution explicitement identifié (dev, test, prod).
  • Source de configuration : origine des paramètres (.env.<APP_ENV>, variables du processus, Vault).
  • Schéma de validation : description formelle des paramètres attendus (type, contraintes, optionnalité).
  • Variable obligatoire : paramètre devant impérativement être présent et valide.
  • Variable optionnelle : paramètre pouvant être absent uniquement si une valeur par défaut explicite est définie.
  • Secret : toute valeur sensible (clé, mot de passe, token) nécessitant un masquage systématique.
  • Namespace : préfixe imposé à toute variable afin d’indiquer explicitement son domaine fonctionnel.

4. Invariants (non négociables)

  1. Aucune variable de configuration ne peut être utilisée sans validation préalable.
  2. Aucune valeur implicite silencieuse n’est autorisée.
  3. Les valeurs par défaut sont autorisées uniquement si :
  4. elles sont explicitement définies dans le schéma,
  5. leur application est journalisée au démarrage.
  6. Politique sur les variables inconnues : REJET STRICT (applicable aux sources de configuration applicative ; les variables système de process.env hors namespace autorisé sont filtrées silencieusement).
  7. L’environnement d’exécution est identifié par la variable obligatoire APP_ENV, appartenant à une liste fermée (dev, test, prod).
  8. L’ordre de chargement des sources de configuration est strict, documenté et immuable.
  9. Les secrets applicatifs doivent provenir exclusivement de Vault, à l’exception strictement définie des secrets de bootstrap Vault.
  10. Les secrets applicatifs (DB, JWT, SMTP, S3, etc.) ne peuvent jamais être fournis via .env ou variables d’environnement du processus.
  11. Les secrets de bootstrap Vault (VAULT_ROLE_ID, VAULT_SECRET_ID) sont autorisés uniquement pour permettre l’accès initial à Vault.
  12. Aucun autre fallback local n’est autorisé.
  13. Aucun secret ne peut être fourni via .env ou variables d’environnement du processus.
  14. Aucun fallback local n’est autorisé.
  15. Les secrets ne doivent jamais apparaître en clair dans les logs.
  16. Les erreurs de configuration sont déterministes, reproductibles et identifiées par un code stable.
  17. La configuration validée est immuable pendant toute la durée d’exécution du service.
  18. Toute variable doit appartenir à un namespace explicite listé comme autorisé ; aucune exception n’est autorisée.

5. Flux nominaux

Flux N1 — Détermination de l’environnement

  1. Le système lit la variable obligatoire APP_ENV.
  2. Si APP_ENV est absente ou invalide, le démarrage échoue explicitement.

Flux N2 — Ordre de chargement des sources

Les sources sont chargées exclusivement dans l'ordre suivant (priorité croissante) : 1. valeurs par défaut définies dans le schéma, 2. fichier .env.<APP_ENV> (optionnel, réservé aux paramètres non secrets et aux secrets de bootstrap Vault), 3. variables d'environnement du processus (non secrètes ou secrets de bootstrap Vault), 4. secrets applicatifs fournis par Vault.

Clarification process.env : Les variables d'environnement du processus (process.env) contiennent des variables système (PATH, HOME, USER, etc.) qui ne relèvent pas de la configuration applicative. Seules les variables appartenant à un namespace autorisé (§10.1) sont extraites et soumises à validation. Les variables hors namespace sont ignorées silencieusement (ce n'est pas une erreur). En revanche, toute variable dans un namespace autorisé mais absente du schéma provoque un échec UNKNOWN_VARIABLE.

Règles strictes : - Si un secret applicatif est détecté dans .env.<APP_ENV> ou dans les variables d’environnement du processus, le démarrage échoue immédiatement (SECRET_SOURCE_FORBIDDEN). - Si un secret de bootstrap Vault est absent et que Vault est requis, le démarrage échoue immédiatement (CONFIG_SOURCE_MISSING). - Si Vault est indisponible et qu’au moins un secret est requis, le démarrage échoue immédiatement (CONFIG_SOURCE_MISSING). - Les règles d’écrasement ne s’appliquent qu’aux variables non secrètes.

Flux N3 — Validation

  1. L’ensemble des paramètres résultants est validé contre le schéma.
  2. Toute erreur entraîne un échec immédiat du démarrage.
  3. En cas de succès, la configuration est marquée comme valide.

— Validation 1. L’ensemble des paramètres résultants est validé contre le schéma. 2. Toute erreur entraîne un échec immédiat du démarrage. 3. En cas de succès, la configuration est marquée comme valide.

Flux N4 — Mise à disposition

  1. La configuration validée est exposée via une interface contrôlée.
  2. Toute tentative d’accès à une clé inexistante est refusée.

5bis. Diagrammes Mermaid

Diagramme d’états — Cycle de vie de la configuration

Ce diagramme couvre les états traversés par le module de configuration au démarrage. Les transitions référencent les invariants INV-1 (validation préalable), INV-5 (APP_ENV obligatoire), INV-6 (ordre de chargement strict), INV-9 (erreurs déterministes) et INV-10 (immuabilité).

stateDiagram-v2
    [*] --> LectureAppEnv : Démarrage service

    LectureAppEnv --> Echec_ENV_INVALID : APP_ENV absente ou invalide [INV-5, INV-9]
    LectureAppEnv --> ChargementSources : APP_ENV valide (dev/test/prod)

    ChargementSources --> ChargementEnvFile : 1. Défauts schéma [INV-2, INV-6]
    ChargementEnvFile --> ChargementProcessEnv : 2. .env.APP_ENV [INV-6]
    ChargementProcessEnv --> ChargementVault : 3. process.env (namespace filtré) [INV-6, INV-11]
    ChargementVault --> Validation : 4. Secrets Vault [INV-6, INV-7]

    ChargementVault --> Echec_CONFIG_SOURCE_MISSING : Vault indisponible [INV-7, INV-9]
    ChargementEnvFile --> Echec_SECRET_SOURCE_FORBIDDEN : Secret détecté dans .env [INV-7, INV-9]
    ChargementProcessEnv --> Echec_SECRET_SOURCE_FORBIDDEN : Secret détecté dans process.env [INV-7, INV-9]

    Validation --> Echec_VALIDATION : Variable manquante/invalide/inconnue [INV-1, INV-4, INV-9]
    Validation --> ConfigurationValide : Schéma conforme [INV-1]

    ConfigurationValide --> Prete : Immuable, exposée [INV-10]

    Echec_ENV_INVALID --> [*]
    Echec_SECRET_SOURCE_FORBIDDEN --> [*]
    Echec_CONFIG_SOURCE_MISSING --> [*]
    Echec_VALIDATION --> [*]

Diagramme de séquence — Flux de chargement avec Vault (N2)

Ce diagramme détaille l’interaction entre le service, les sources locales et Vault lors du chargement de configuration. Il référence les invariants INV-6 (ordre strict), INV-7 (secrets exclusivement Vault), INV-8 (secrets jamais en clair dans les logs) et INV-3 (défauts journalisés).

sequenceDiagram
    participant S as Service (bootstrap)
    participant Sch as Schéma (Joi)
    participant Env as .env.APP_ENV
    participant Proc as process.env
    participant V as HashiCorp Vault

    S->>Proc: Lire APP_ENV [INV-5]
    alt APP_ENV absente ou invalide
        S-->>S: STOP — ENV_INVALID [INV-9]
    end

    Note over S,Sch: Phase 1 — Défauts schéma [INV-6 priorité 1]
    S->>Sch: Extraire valeurs par défaut
    Sch-->>S: Défauts (journalisés) [INV-2, INV-3]

    Note over S,Env: Phase 2 — Fichier .env [INV-6 priorité 2]
    S->>Env: Charger .env.{APP_ENV}
    Env-->>S: Paramètres non secrets
    S->>S: Vérifier absence secrets [INV-7]
    alt Secret détecté dans .env
        S-->>S: STOP — SECRET_SOURCE_FORBIDDEN [INV-9]
    end

    Note over S,Proc: Phase 3 — Variables processus [INV-6 priorité 3]
    S->>Proc: Filtrer namespaces autorisés [INV-11]
    Proc-->>S: Variables filtrées
    S->>S: Vérifier absence secrets [INV-7]
    alt Secret détecté dans process.env
        S-->>S: STOP — SECRET_SOURCE_FORBIDDEN [INV-9]
    end

    Note over S,V: Phase 4 — Secrets Vault [INV-6 priorité 4, INV-7]
    S->>V: Authentification (VAULT_ROLE_ID + VAULT_SECRET_ID)
    alt Vault indisponible
        S-->>S: STOP — CONFIG_SOURCE_MISSING [INV-9]
    end
    V-->>S: Secrets applicatifs (masqués en log) [INV-8]

    Note over S,Sch: Phase 5 — Validation [INV-1, INV-4]
    S->>Sch: Valider ensemble fusionné
    alt Variable inconnue dans namespace
        Sch-->>S: STOP — UNKNOWN_VARIABLE [INV-4, INV-9]
    end
    alt Variable obligatoire manquante ou invalide
        Sch-->>S: STOP — MISSING/INVALID [INV-1, INV-9]
    end
    Sch-->>S: Configuration validée

    S->>S: Geler configuration (immuable) [INV-10]

6. Cas d’erreur

  • ENV_INVALIDAPP_ENV absent ou non autorisé.
  • MISSING_VARIABLE — Variable obligatoire manquante.
  • INVALID_TYPE — Type incorrect.
  • INVALID_VALUE — Valeur hors contraintes.
  • UNKNOWN_VARIABLE — Variable inconnue ou hors namespace autorisé.
  • SECRET_LOG_ATTEMPT — Tentative de journalisation d’un secret.
  • SECRET_SOURCE_FORBIDDEN — Secret fourni via une source interdite (hors Vault).
  • CONFIG_SOURCE_MISSING — Vault indisponible alors que des secrets sont requis.

7. Critères d’acceptation (testables)

CA1. Une configuration complète et valide permet le démarrage du service. CA2. Toute variable obligatoire absente provoque un échec de démarrage. CA3. Toute variable invalide (type ou contrainte) provoque un échec de démarrage. CA4. Les valeurs par défaut sont appliquées uniquement si elles sont explicitement définies et journalisées. CA5. Les secrets n’apparaissent jamais en clair dans les logs. CA6. La configuration validée est immuable pendant l’exécution. CA7. Toute variable inconnue dans un namespace autorisé provoque un échec de démarrage (les variables système hors namespace sont filtrées). CA8. Tout secret présent hors Vault provoque un échec de démarrage.


8. Scénarios de test (Given / When / Then)

S1 — Configuration valide

Given un APP_ENV valide et une configuration conforme When le service démarre Then le service démarre avec une configuration validée.

S2 — Variable inconnue dans un namespace autorisé

Given une variable appartenant à un namespace autorisé mais non définie dans le schéma (ex: DB_UNKNOWN_VAR) When le service démarre Then le démarrage échoue avec UNKNOWN_VARIABLE.

S2b — Variable système hors namespace (comportement normal)

Given des variables système dans process.env (ex: PATH, HOME, USER) When le service démarre Then ces variables sont ignorées silencieusement (pas d'erreur).

S3 — Valeur par défaut

Given une variable optionnelle absente mais avec valeur par défaut When le service démarre Then la valeur est appliquée et journalisée.

S4 — Secret hors Vault

Given un secret fourni via .env ou variable de processus When le service démarre Then le démarrage échoue avec SECRET_SOURCE_FORBIDDEN.


9. Hypothèses explicites

H1. Le backend s’exécute dans un environnement Node.js. H2. Le schéma de validation est défini via une bibliothèque de type Joi. H3. Vault est disponible pour fournir les secrets applicatifs.


10. Spécifications détaillées (sections fermées)

10.1 Namespaces autorisés (liste fermée)

Tous les paramètres doivent appartenir à un et un seul namespace.

Namespaces autorisés : - APP_ — application / environnement - HTTP_ — exposition réseau / HTTP - DB_ — base de données - REDIS_ — cache / sessions - JWT_ — authentification JWT - CRYPTO_ — primitives cryptographiques - RATE_LIMIT_ — limitation de débit - CAPTCHA_ — captcha / anti-bot - TURNSTILE_ — Cloudflare Turnstile - SMTP_ — email / SMTP - EMAIL_ — workers email - S3_ — stockage objet - GLACIER_ — archivage - INTERNAL_ — endpoints internes - VAULT_ — bootstrap Vault - CLOUDHSM_ — intégration CloudHSM (PKCS#11)

Tout ajout de namespace constitue une évolution explicite de la spécification.

10.2 Liste exhaustive des variables de configuration (contractuelle)

Toute variable non listée ci-dessous est interdite.

Application / Environnement

  • APP_ENV

Règle d’environnement : - APP_ENV est la seule source de vérité contractuelle. - Aucune autre variable d’environnement applicatif n’est autorisée pour déterminer l’environnement.

HTTP

  • HTTP_PORT (default: 3000)
  • HTTP_CORS_ORIGIN (default: http://localhost:3000)

Base de données / Redis

  • DB_DATABASE_URL (secret via Vault uniquement)
  • DB_HOST, DB_PORT, DB_NAME, DB_USER (non secrets)
  • DB_PASSWORD (secret via Vault uniquement)
  • DB_SSL, DB_SSL_REJECT_UNAUTHORIZED
  • DB_POOL_MIN, DB_POOL_MAX, DB_POOL_IDLE_TIMEOUT, DB_POOL_ACQUIRE_TIMEOUT, DB_STATEMENT_TIMEOUT
  • DB_LOGGING, DB_SLOW_QUERY_THRESHOLD
  • REDIS_HOST, REDIS_PORT, REDIS_DB (non secrets)
  • REDIS_PASSWORD (secret via Vault uniquement)
  • REDIS_TLS_ENABLED (booléen, requis)

Sécurité / Crypto

  • JWT_SECRET (secret via Vault uniquement)
  • JWT_EXPIRATION, JWT_REFRESH_EXPIRATION
  • CRYPTO_BCRYPT_SALT_ROUNDS

Rate limiting / CAPTCHA

  • RATE_LIMIT_REGISTRATION_MAX, RATE_LIMIT_REGISTRATION_WINDOW_MS
  • RATE_LIMIT_REGISTRATION_SOFT, RATE_LIMIT_REGISTRATION_HARD
  • CAPTCHA_PROVIDER
  • TURNSTILE_SECRET_KEY (secret via Vault uniquement)
  • TURNSTILE_TIMEOUT

Email

  • SMTP_HOST, SMTP_PORT, SMTP_SECURE
  • SMTP_USER, SMTP_PASS (secrets via Vault uniquement)
  • SMTP_FROM
  • APP_URL
  • EMAIL_WORKER_INTERVAL_MS, EMAIL_MAX_RETRIES

Stockage

  • S3_ENDPOINT, S3_REGION, S3_BUCKET
  • S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY (secrets via Vault uniquement)
  • GLACIER_ENABLED, GLACIER_REGION, GLACIER_VAULT_NAME

Interne / Vault

  • INTERNAL_API_KEY (secret via Vault uniquement)
  • INTERNAL_ALLOWED_NETWORKS
  • VAULT_ADDR
  • VAULT_ROLE_ID, VAULT_SECRET_ID (secrets de bootstrap Vault)
  • VAULT_DYNAMIC_DB_ENABLED (booléen, default: false)
  • VAULT_DB_ROLE (nom de rôle Vault pour Database Secrets Engine)

CloudHSM (PKCS#11)

  • CLOUDHSM_LIBRARY_PATH (default: /opt/cloudhsm/lib/libcloudhsm_pkcs11.so)
  • CLOUDHSM_SLOT (default: 0)
  • CLOUDHSM_USER (default: crypto_user)
  • CLOUDHSM_PIN (secret via Vault uniquement)
  • CLOUDHSM_SESSION_TIMEOUT (default: 300000)
  • CLOUDHSM_MAX_SESSIONS (default: 10)

10.3 Typologie des secrets et règles de masquage (fermée)

  • Interdiction absolue de journaliser un secret en clair.
  • Utilisation exclusive de placeholders (***SECRET***) ou suffixes masqués.

Exigences d'exploitation (hors périmètre implémentation, à configurer sur l'infrastructure de logs) :

  • Rétention maximale des logs de configuration : 7 jours.
  • Accès restreint aux rôles Ops / SecOps.

11. Spécification fermée

La présente spécification est complète, cohérente et contractuelle. Toute évolution future nécessite une modification explicite de la spec.