Aller au contenu

PD-265 — Plan d'implémentation

Clé Valeur
Story PD-265
Titre Monitoring TSA RFC 3161 et cycle de vie des clés opérationnelles
Projet ProbatioVault-backend
Gate 3 GO (v3, 8.75/10)
Auteur Claude Opus 4.6
Date 2026-03-02

Décisions architecturales

DA-01 — Agrégation NTS multi-serveurs (adresse R-20)

Décision : Stratégie worst-case (fail-closed). Si AU MOINS UN serveur de ntsReferenceServers retourne un offset > ntsAlertThreshold ou est injoignable, la condition NTS est activée. La métrique tsa_nts_offset_ms expose le maximum des offsets absolus observés.

Justification : Cohérent avec le principe fail-closed d'INV-265-01. Un serveur compromis ou instable ne doit pas être masqué par les autres. Pour le clearing, les 2 contrôles consécutifs conformes s'appliquent à TOUS les serveurs (i.e. 2 cycles complets où tous les serveurs sont conformes).

Alternative écartée : Majorité/médiane — masquerait un serveur compromis, risque de faux négatif.

DA-02 — Enforcement des SLA post-rotation (adresse R-21)

Décision : Ajout d'un KeyLifecycleSlaScheduler qui vérifie périodiquement l'âge des clés en statut RETIRED et ARCHIVED. Le dépassement de keyRetiredToArchivedDelay ou keyArchivedToDestroyedDelay déclenche : 1. Un événement d'audit CRITICAL signé/horodaté (reasonCode KEY_RETIRED_SLA_BREACH / KEY_ARCHIVED_SLA_BREACH). 2. Une métrique tsa_key_lifecycle_sla_breach (gauge, label status). 3. Notification exploitation.

Contractualisation des reasonCodes : Les codes KEY_RETIRED_SLA_BREACH et KEY_ARCHIVED_SLA_BREACH sont ajoutés à l'enum reasonCode de la spec §5.9 (événements d'audit) comme extension documentée.

Deux enums distinctes (conformément à la spec §5.1 et §5.9) : - refusalReasonCode (§5.1 — refus d'émission TST, 6 valeurs spec) : MAINTENANCE | NTS_DEGRADED | REVOCATION_DEGRADED | TRUSTLIST_DEGRADED | KEY_LIFECYCLE_DEGRADED | COMPOSITE_DEGRADED. Fichier : src/modules/tsa/enums/refusal-reason-code.enum.ts. - reasonCode (§5.9 — événements d'audit, 13+2 valeurs) : NTS_TIMEOUT | NTS_OFFSET | OCSP_REVOKED | OCSP_UNKNOWN | OCSP_STALE | CRL_LISTED | CRL_UNAVAILABLE | CRL_STALE | TL_STALE | TL_UNTRUSTED | KEY_OVERDUE | REH_TIMESTAMP_DEADLINE | MAX_DEGRADED_DURATION | KEY_RETIRED_SLA_BREACH | KEY_ARCHIVED_SLA_BREACH. Fichier : src/modules/tsa/enums/audit-reason-code.enum.ts.

Les nommages respectent exactement les noms de la spec. Les 2 codes ajoutés (suffixe _SLA_BREACH) sont marqués comme extension plan dans le fichier enum.

Justification : La spec dit "ne peut pas durer au-delà" mais ne définit pas de flag de dégradation associé. La destruction (ARCHIVED→DESTROYED) reste une action opérateur explicite. L'enforcement est donc par alerte critique, pas par flag de dégradation.

DA-03 — Politique de re-horodatage par état (adresse R-22)

Décision : RehorodatageService.canRehorodatage() consulte TsaDegradationStateService avant tout traitement. Les 7 politiques de la spec §5.4 sont implémentées comme une lookup table exhaustive. Le path conditionnel DEGRADED_KEY_LIFECYCLE vérifie en plus : (a) flag unique dans tsaServiceDegradationFlags, (b) certificat TSA actif valide/non révoqué via RevocationMonitoringService.getLastStatus(), © TL valide via TrustedListService.isAuthorityTrusted().

Justification : Couverture exhaustive des 7 états. Le path conditionnel est critique et doit vérifier les 3 conditions explicitement.

DA-04 — Architecture module

Décision : Nouveau sous-module tsa-monitoring au sein du module tsa existant, avec ses propres entities, services, schedulers. Pas de module NestJS séparé — le monitoring est intrinsèquement lié au domaine TSA.

Justification : Le module TSA existant contient déjà ReferenceClockService, les entités TST et le processor de batch. Séparer le monitoring ajouterait de la complexité d'injection sans valeur ajoutée.

DA-05 — Persistance état de dégradation

Décision : Les flags (tsaServiceDegradationFlags) sont persistés en base (table tsa_degradation_state, colonnes flags: DegradationFlag[], maintenanceMode: boolean, lastTransitionAt: Date, flagActivationTimestamps: Record<DegradationFlag, Date>) ET en mémoire (cache service). L'état dérivé (tsaServiceState) est calculé à la volée par TsaDegradationStateService.getState() à partir des flags actifs, conformément à la spec §5.4 : - maintenanceMode=trueMAINTENANCE - flags.length === 0 ET maintenanceMode=falseHEALTHY - flags.length === 1 → état spécifique : DEGRADED_NTS si flag=NTS, DEGRADED_REVOCATION si flag=REVOCATION, DEGRADED_TRUSTLIST si flag=TRUSTLIST, DEGRADED_KEY_LIFECYCLE si flag=KEY_LIFECYCLE - flags.length >= 2DEGRADED (état composite, les flags individuels sont accessibles via getFlags())

La table tsa_degradation_state ne contient pas de colonne state — l'état est toujours dérivé. La base est la source de vérité pour les flags ; le cache est rafraîchi au démarrage (onModuleInit) et synchronisé à chaque modification (pattern write-through). Les métriques Prometheus sont exposées depuis le cache.

Comportement fail-closed au démarrage : Si le cache C3 n'est pas initialisé au moment d'un appel canEmitTst() (DB indisponible au onModuleInit), le service retourne { allowed: false, refusalReasonCode: 'MAINTENANCE' } — fail-closed conformément à INV-265-01. L'application NE DÉMARRE PAS si la DB est indisponible pendant plus de C1.dbStartupTimeout (configurable, défaut 30s).

Justification : La persistance des flags en base garantit la reprise après crash (INV-265-09). L'état dérivé calculé élimine toute incohérence état/flags. Le cache évite les requêtes DB pour chaque vérification de guard (performance). Le fail-closed au démarrage garantit qu'aucun TST n'est émis dans un état inconnu.


1. Découpage en composants

C1 — TsaMonitoringConfigService

Responsabilité : Chargement, validation (Joi) et exposition de tous les paramètres configurables (§5.2), incluant les contraintes croisées.

Fichiers : - src/modules/tsa/config/tsa-monitoring.config.ts - src/config/config.schema.ts (extension)

Dépendances : ConfigModule (NestJS)

C2 — Entities & Migration DDL

Responsabilité : Modèle de données pour l'état de dégradation, les métadonnées de cycle de vie des clés, et le tracking de re-horodatage.

Entities : - TsaDegradationState : singleton row (flags, maintenanceMode, lastTransitionAt, flagActivationTimestamps). Note : pas de colonne state — l'état (tsaServiceState) est dérivé à la volée par C3 depuis les flags (cf. DA-05). - TsaKeyLifecycleMetadata : statut logique par clé (ACTIVE/RETIRED/ARCHIVED/DESTROYED), dates de transition, relation avec KeyRecord - TsaRehorodatageRecord : tracking des TST re-horodatés (originalTstId, newTstId, processedAt, batchId)

Fichiers : - src/modules/tsa/entities/tsa-degradation-state.entity.ts - src/modules/tsa/entities/tsa-key-lifecycle-metadata.entity.ts - src/modules/tsa/entities/tsa-rehorodatage-record.entity.ts - src/database/migrations/XXXX-PD265-TsaMonitoringEntities.ts

Schema : vault_secure

C3 — TsaDegradationStateService

Responsabilité : Machine d'états composite. Gestion des flags (NTS, REVOCATION, TRUSTLIST, KEY_LIFECYCLE), dérivation de tsaServiceState, application des règles de clearing, journalisation des transitions, gestion du maintenanceMode.

Interfaces clés : - activateFlag(flag: DegradationFlag): Promise<void> — active un flag, recalcule l'état, journalise - clearFlag(flag: DegradationFlag): Promise<void> — désactive un flag (si règles de clearing ok), recalcule, journalise - getState(): TsaServiceState — état courant dérivé (depuis cache) - getFlags(): DegradationFlag[] — flags actifs (trié lexicographiquement) - setMaintenanceMode(enabled: boolean, actorId: string): Promise<void> — bascule MAINTENANCE (appelé par C10 après RBAC) - canEmitTst(): { allowed: boolean; refusalReasonCode?: RefusalReasonCode } — politique émission §5.4 - canRehorodatage(): { allowed: boolean; reason?: string } — politique re-horodatage §5.4, incluant le path conditionnel DA-03 - getFlagActiveDuration(flag: DegradationFlag): number — durée continue en secondes (pour C13)

Fichiers : - src/modules/tsa/services/tsa-degradation-state.service.ts - src/modules/tsa/enums/tsa-service-state.enum.ts - src/modules/tsa/enums/degradation-flag.enum.ts - src/modules/tsa/enums/refusal-reason-code.enum.ts (6 valeurs spec §5.1) - src/modules/tsa/enums/audit-reason-code.enum.ts (15 valeurs spec §5.9 + extension DA-02) - src/modules/tsa/interfaces/tsa-monitoring.interfaces.ts

C4 — NtsMonitoringService + Scheduler

Responsabilité : Contrôle périodique NTS contre tous les serveurs de ntsReferenceServers. Agrégation worst-case (DA-01). Clearing après 2 cycles consécutifs conformes.

Mécanisme : 1. Pour chaque serveur de ntsReferenceServers : mesure offset via ntpd-rs observe.json ou appel NTS direct. 2. Si ANY offset > ntsAlertThreshold OU ANY serveur injoignable → degradationStateService.activateFlag(NTS). 3. Clearing : compteur consecutiveOkCount incrémenté si ALL serveurs OK. Reset à 0 si ANY échoue. Clearing à consecutiveOkCount >= 2.

Observable : tsa_nts_offset_ms (gauge, max des offsets absolus), événement d'audit sur activation flag.

Fichiers : - src/modules/tsa/services/nts-monitoring.service.ts - src/modules/tsa/schedulers/nts-monitoring.scheduler.ts - src/modules/tsa/services/reference-clock.service.ts (fichier existant — extension avec méthode measureOffset(server: string): Promise<number>)

Frontière avec ReferenceClockService : Seule la méthode measureOffset(server) est ajoutée. Le service existant (getReferenceTime(), etc.) reste inchangé. L'injection dans NtsMonitoringService se fait via l'interface ReferenceClockService existante (pas de nouveau provider).

C5 — RevocationMonitoringService + Scheduler

Responsabilité : Contrôle périodique OCSP et CRL du certificat TSA actif. Vérification de la fraîcheur par rapport à ocspMaxAge/crlMaxAge.

Mécanisme : 1. Appel OCSP via OcspClientService existant (legal-pre module) → statut + timestamp. 2. Téléchargement CRL → parsing, recherche du serial du certificat TSA actif. 3. Évaluation des conditions d'activation du flag REVOCATION (§5.4). 4. Clearing si OCSP=GOOD ET CRL=NOT_LISTED ET fraîcheur <= max ages.

Observable : tsa_ocsp_age_seconds, tsa_crl_age_seconds (gauges).

Fichiers : - src/modules/tsa/services/revocation-monitoring.service.ts - src/modules/tsa/schedulers/revocation-monitoring.scheduler.ts

C6 — TrustedListService + Scheduler

Responsabilité : Téléchargement périodique de la Trusted List ETSI, cache avec TTL, vérification de la présence de l'autorité émettrice TSA.

Mécanisme : 1. Fetch TL ETSI (XML) → parse → extraire services de confiance qualifiés. 2. Cache en mémoire avec TTL (trustedListCacheTtl). 3. Vérification : l'autorité émettrice du certificat TSA actif est-elle dans la TL ? 4. Activation flag TRUSTLIST si TL expirée au-delà du TTL OU autorité absente. 5. Clearing si TL valide ET autorité présente. 6. Stockage du trustedListSequenceNumber + trustedListDigestSha256 pour traçabilité.

Observable : tsa_trusted_list_cache_age_seconds (gauge).

Fichiers : - src/modules/tsa/services/trusted-list.service.ts - src/modules/tsa/schedulers/trusted-list.scheduler.ts

C7 — KeyLifecycleExtensionService + SLA Scheduler

Responsabilité : Extension du système de rotation existant (HsmKeyManagerService) avec les états RETIRED, ARCHIVED, DESTROYED, le monitoring d'âge, la prévention de réactivation, et l'enforcement des SLA post-rotation (DA-02).

Mécanisme : 1. Extension de KeyRecord ou nouvelle entity TsaKeyLifecycleMetadata avec statut logique. 2. Rotation : ACTIVE → RETIRED (ancienne clé). L'ancienne clé reste accessible pour vérification/audit mais pas pour signature. 3. RETIRED → ARCHIVED : action opérateur ou timer, retrait des chemins applicatifs. 4. ARCHIVED → DESTROYED : action opérateur explicite, HSM delete. 5. Réactivation depuis RETIRED/ARCHIVED/DESTROYED : INTERDITE, échec + événement d'audit. 6. Age monitoring : si âge clé active > keyRotationPerioddegradationStateService.activateFlag(KEY_LIFECYCLE). 7. SLA scheduler : vérifie périodiquement l'âge des clés RETIRED et ARCHIVED, alerte CRITICAL si SLA dépassé.

Observable : tsa_active_key_age_days (gauge), tsa_key_lifecycle_sla_breach (gauge).

Fichiers : - src/modules/tsa/services/key-lifecycle-extension.service.ts - src/modules/tsa/schedulers/key-lifecycle-sla.scheduler.ts - src/modules/tsa/entities/tsa-key-lifecycle-metadata.entity.ts

C8 — RehorodatageService + Processor + Scheduler

Responsabilité : Détection des TST éligibles au re-horodatage, traitement par batch (BullMQ), lien supersedesTstId, monitoring du rehorodatageProcessingDeadline, politique par état (DA-03).

Mécanisme : 1. Scheduler cadencé toutes les 12h (marge 12h vs deadline rehorodatageProcessingDeadline de 24h — résout l'écart DIV-05 de marge nulle) : sélection des TST dont le certificat émetteur expire dans <= rehorodatageLeadTime. 2. Vérification de la politique par état via degradationStateService.canRehorodatage(). 3. Enqueue en BullMQ par lots de rehorodatageBatchSize (rejet explicite si dépassement runtime, pas de clamp — INV-265-12, TC-NEG-05). 4. Processor : pour chaque TST éligible, émission d'un nouveau TST avec supersedesTstId = originalTstId. 5. Deadline monitoring : si un TST éligible n'est pas traité dans rehorodatageProcessingDeadline → alerte CRITICAL + export probatoire. 6. Idempotence : un TST déjà re-horodaté (existant dans tsa_rehorodatage_record) est ignoré.

Observable : tsa_rehorodatage_backlog_total (gauge).

Fichiers : - src/modules/tsa/services/rehorodatage.service.ts - src/modules/tsa/processors/rehorodatage.processor.ts - src/modules/tsa/schedulers/rehorodatage.scheduler.ts

C9 — TstEmissionGuard

Responsabilité : Guard NestJS sur les endpoints d'émission de TST. Fail-closed selon la politique §5.4. Refus avec refusalReasonCode normé.

Mécanisme : 1. Avant chaque émission : degradationStateService.canEmitTst(). 2. Si non autorisé : HTTP 503 avec body contenant refusalReasonCode, tsaServiceState, tsaServiceDegradationFlags. 3. Enregistrement comme APP_GUARD sur les routes TST du module TSA (pas global).

Fichiers : - src/modules/tsa/guards/tst-emission.guard.ts - src/modules/tsa/dto/tst-emission-refusal.dto.ts

C10 — MaintenanceToggleService

Responsabilité : Bascule MAINTENANCE protégée RBAC. Journalisation des tentatives (autorisées et refusées).

Mécanisme : 1. Vérification du rôle de l'acteur contre maintenanceToggleAllowedRoles. 2. Si autorisé : degradationStateService.setMaintenanceMode(enabled, actorId). 3. Si refusé : émission événement PV_TSA_MAINTENANCE_TOGGLE_DENIED via C12, HTTP 403. 4. Endpoint : POST /tsa/maintenance avec body { enabled: boolean }.

Wiring NestJS : - Endpoint POST /tsa/maintenance déclaré dans TsaMonitoringController (nouveau controller dédié au monitoring TSA, séparé du TsaController existant pour l'émission de TST). - Le controller injecte MaintenanceToggleService et applique @UseGuards(JwtAuthGuard, RolesGuard) + @Roles(Role.TSA_OPERATOR). - Le controller délègue immédiatement au service (pas de logique métier dans le controller).

Fichiers : - src/modules/tsa/controllers/tsa-monitoring.controller.ts (nouveau — wiring endpoint MAINTENANCE + health) - src/modules/tsa/services/maintenance-toggle.service.ts - src/modules/tsa/dto/maintenance-toggle.dto.ts

C11 — TsaMetricsService

Responsabilité : Exposition de toutes les métriques contractuelles avec le préfixe tsa_ (§5.9). Intégration Prometheus via @willsoto/nestjs-prometheus ou prom-client.

Métriques contractuelles : - tsa_service_state (gauge, label state) - tsa_service_degradation_flag (gauge, label flag, valeur 0/1) - tsa_nts_offset_ms (gauge) - tsa_ocsp_age_seconds (gauge) - tsa_crl_age_seconds (gauge) - tsa_trusted_list_cache_age_seconds (gauge) - tsa_active_key_age_days (gauge) - tsa_rehorodatage_backlog_total (gauge) - tsa_degraded_duration_seconds (gauge, label flag) - tsa_key_lifecycle_sla_breach (gauge, label status)

Cardinalité : Labels à cardinalité bornée (enums uniquement, pas d'URL/fingerprint/UUID).

Fichiers : - src/modules/tsa/services/tsa-metrics.service.ts

C12 — TsaAuditEventService

Responsabilité : Émission des événements d'audit signés (JCS + clé audit) pour les alertes critiques, transitions d'état, refus RBAC, et tentatives de réactivation.

Types d'événements : - PV_TSA_CRITICAL_ALERT (CRITICAL) : revocation, NTS, trustlist, key overdue, re-horodatage deadline, maxDegradedDuration - PV_TSA_STATE_TRANSITION (non-CRITICAL) : toute transition effective - PV_TSA_MAINTENANCE_TOGGLE_DENIED (non-CRITICAL) : refus RBAC

Mécanisme : 1. Construction du payload JSON avec tous les champs obligatoires (§5.9). 2. Canonicalisation RFC 8785 (JCS) via JsonCanonicalizeService existant. 3. Signature avec la clé d'audit (auditSignatureKeyId) via AuditSignatureService existant. 4. Persistance via AuditLogService existant.

Fichiers : - src/modules/tsa/services/tsa-audit-event.service.ts

C13 — DegradationEscalationService + Scheduler

Responsabilité : Tracking de la durée continue de dégradation par flag. Alerte CRITICAL si maxDegradedDuration est dépassée.

Mécanisme : 1. Scheduler périodique (intervalle = min(ntsCheckInterval, 60s)). 2. Pour chaque flag actif : calcul de la durée continue via degradationStateService.getFlagActiveDuration(flag). 3. Si durée > maxDegradedDuration → émission alerte CRITICAL via C12, notification exploitation.

Fichiers : - src/modules/tsa/services/degradation-escalation.service.ts - src/modules/tsa/schedulers/degradation-escalation.scheduler.ts

C14 — CI Label Validation

Responsabilité : Script de validation CI des labels HSM. Pipeline rouge si label hors convention.

Mécanisme : 1. Script ou test dédié exécuté en CI. 2. Scan de tous les labels HSM déclarés/utilisés dans le code. 3. Validation regex ^pv-tsa-signing-[0-9]{4}$ (prod) et ^pv-test-tsa-[A-Za-z0-9._-]+$ (test). 4. Exit code non-zero si non-conformité.

Fichiers : - src/modules/tsa/__tests__/hsm-label-validation.spec.ts


2. Flux techniques

F1 — Contrôle périodique NTS (nominal)

NtsMonitoringScheduler (cron: ntsCheckInterval)
  → NtsMonitoringService.checkAll()
    → pour chaque server in ntsReferenceServers:
      → ReferenceClockService.measureOffset(server)
      → collecte offset ou erreur
    → agrégation worst-case (DA-01)
    → si ANY offset > threshold OU ANY erreur:
      → TsaDegradationStateService.activateFlag(NTS)
      → TsaAuditEventService.emitCriticalAlert(NTS_TIMEOUT|NTS_OFFSET)
      → TsaMetricsService.updateNtsOffset(maxOffset)
    → si ALL ok:
      → incrémenter consecutiveOkCount
      → si consecutiveOkCount >= 2:
        → TsaDegradationStateService.clearFlag(NTS)

F2 — Contrôle périodique OCSP/CRL (nominal)

RevocationMonitoringScheduler (cron: min(ocspCheckInterval, crlCheckInterval))
  → RevocationMonitoringService.check()
    → OCSP: OcspClientService.checkStatus(certSerial)
      → évaluer statut + fraîcheur vs ocspMaxAge
    → CRL: fetch CRL, parse, chercher serial
      → évaluer statut + fraîcheur vs crlMaxAge
    → si conditions REVOCATION activées:
      → TsaDegradationStateService.activateFlag(REVOCATION)
      → TsaAuditEventService.emitCriticalAlert(reasonCode)
    → si conditions REVOCATION cleared:
      → TsaDegradationStateService.clearFlag(REVOCATION)
    → TsaMetricsService.updateRevocationMetrics(ocspAge, crlAge)

F3 — Rotation clé HSM TSA (nominal)

KeyLifecycleSlaScheduler (cron: daily)
  → KeyLifecycleExtensionService.checkActiveKeyAge()
    → si âge > keyRotationPeriod:
      → TsaDegradationStateService.activateFlag(KEY_LIFECYCLE)
      → TsaAuditEventService.emitCriticalAlert(KEY_OVERDUE)
  → KeyLifecycleExtensionService.checkRetiredKeys()
    → si âge RETIRED > keyRetiredToArchivedDelay: alerte CRITICAL (DA-02)
  → KeyLifecycleExtensionService.checkArchivedKeys()
    → si âge ARCHIVED > keyArchivedToDestroyedDelay: alerte CRITICAL (DA-02)

Rotation (manuelle via endpoint):
  → KeyLifecycleExtensionService.rotateActiveKey(newKeyLabel)
    → validate label regex pv-tsa-signing-YYYY
    → HSM: générer nouvelle clé
    → ancienne clé: ACTIVE → RETIRED (metadata)
    → nouvelle clé: ACTIVE
    → TsaDegradationStateService.clearFlag(KEY_LIFECYCLE)
    → TsaAuditEventService.emitStateTransition()

F4 — Re-horodatage (nominal)

RehorodatageScheduler (cron: every 12h — marge 12h vs deadline 24h)
  → RehorodatageService.detectEligibleTsts()
    → SELECT tst WHERE cert_expiry <= NOW() + rehorodatageLeadTime
      AND tst.id NOT IN (SELECT original_tst_id FROM tsa_rehorodatage_record)
    → vérifier: degradationStateService.canRehorodatage()
    → si non autorisé: log + skip
    → si count > rehorodatageBatchSize: rejet explicite (pas de clamp)
    → enqueue RehorodatageProcessor (BullMQ)

RehorodatageProcessor.process(batch):
  → pour chaque TST:
    → émettre nouveau TST via TSA
    → INSERT tsa_rehorodatage_record (originalTstId, newTstId, processedAt)
    → TsaMetricsService.decrementBacklog()

RehorodatageScheduler.checkDeadlines():
  → SELECT tst éligibles non traités depuis > rehorodatageProcessingDeadline
  → si trouvés: TsaAuditEventService.emitCriticalAlert(REH_TIMESTAMP_DEADLINE) + export probatoire

F5 — Bascule MAINTENANCE (nominal)

POST /tsa/maintenance { enabled: true }
  → MaintenanceToggleService.toggle(enabled, actor)
    → vérifier rôle actor ∈ maintenanceToggleAllowedRoles
    → si non autorisé:
      → TsaAuditEventService.emitMaintenanceDenied(actor)
      → HTTP 403
    → si autorisé:
      → TsaDegradationStateService.setMaintenanceMode(true, actor)
      → TsaAuditEventService.emitStateTransition(MAINTENANCE)
      → HTTP 200

F6 — Émission TST refusée (dégradé)

POST /tsa/timestamp (endpoint existant)
  → TstEmissionGuard.canActivate()
    → degradationStateService.canEmitTst()
    → si { allowed: false, refusalReasonCode }:
      → HTTP 503 { refusalReasonCode, tsaServiceState, tsaServiceDegradationFlags }

Diagrammes Mermaid

Graphe de dépendances des composants

graph TD
    C1[C1 — TsaMonitoringConfigService]
    C2[C2 — Entities & Migration DDL]
    C3[C3 — TsaDegradationStateService]
    C4[C4 — NtsMonitoringService + Scheduler]
    C5[C5 — RevocationMonitoringService + Scheduler]
    C6[C6 — TrustedListService + Scheduler]
    C7[C7 — KeyLifecycleExtensionService + SLA Scheduler]
    C8[C8 — RehorodatageService + Processor + Scheduler]
    C9[C9 — TstEmissionGuard]
    C10[C10 — MaintenanceToggleService]
    C11[C11 — TsaMetricsService]
    C12[C12 — TsaAuditEventService]
    C13[C13 — DegradationEscalationService + Scheduler]
    C14[C14 — CI Label Validation]

    C3 --> C1
    C3 --> C2
    C3 --> C12

    C4 --> C1
    C4 --> C3
    C4 --> C11
    C4 --> C12
    C4 -.->|ReferenceClockService| EXT_NTS[NTS Servers]

    C5 --> C1
    C5 --> C3
    C5 --> C11
    C5 --> C12
    C5 -.->|OcspClientService| EXT_OCSP[OCSP / CRL Endpoints]

    C6 --> C1
    C6 --> C3
    C6 --> C11
    C6 --> C12
    C6 -.-> EXT_TL[ETSI Trusted List]

    C7 --> C1
    C7 --> C2
    C7 --> C3
    C7 --> C11
    C7 --> C12
    C7 -.->|HsmKeyManagerService| EXT_HSM[HSM PKCS#11]

    C8 --> C1
    C8 --> C2
    C8 --> C3
    C8 --> C11
    C8 --> C12
    C8 -.->|BullMQ| EXT_REDIS[Redis]

    C9 --> C3

    C10 --> C3
    C10 --> C12

    C11 --> C1

    C13 --> C3
    C13 --> C12

    C14 -.-> EXT_CI[CI Pipeline]

    style C3 fill:#f9d,stroke:#333,stroke-width:2px
    style C9 fill:#fca,stroke:#333,stroke-width:2px
    style C12 fill:#adf,stroke:#333,stroke-width:2px

Diagramme de sequence — Emission TST refusee (F6) et controle NTS (F1)

sequenceDiagram
    participant Client
    participant Guard as C9 — TstEmissionGuard
    participant State as C3 — TsaDegradationStateService
    participant NTS as C4 — NtsMonitoringService
    participant Clock as ReferenceClockService
    participant Audit as C12 — TsaAuditEventService
    participant Metrics as C11 — TsaMetricsService
    participant DB as PostgreSQL

    Note over NTS,Clock: F1 — Controle periodique NTS
    loop cron: ntsCheckInterval
        NTS->>Clock: measureOffset(server) pour chaque serveur
        Clock-->>NTS: offset ou erreur
        alt ANY offset > threshold OU erreur
            NTS->>State: activateFlag(NTS)
            State->>DB: persist flags
            State->>Audit: emitCriticalAlert(NTS_OFFSET)
            NTS->>Metrics: updateNtsOffset(maxOffset)
        else ALL ok, consecutiveOkCount >= 2
            NTS->>State: clearFlag(NTS)
            State->>DB: persist flags
        end
    end

    Note over Client,DB: F6 — Emission TST refusee (etat degrade)
    Client->>Guard: POST /tsa/timestamp
    Guard->>State: canEmitTst()
    State-->>Guard: { allowed: false, refusalReasonCode }
    Guard-->>Client: HTTP 503 { refusalReasonCode, tsaServiceState, flags }

Diagramme de sequence — Re-horodatage batch (F4)

sequenceDiagram
    participant Sched as RehorodatageScheduler
    participant Svc as C8 — RehorodatageService
    participant State as C3 — TsaDegradationStateService
    participant Queue as BullMQ
    participant Proc as RehorodatageProcessor
    participant TSA as TSA Emission
    participant DB as PostgreSQL
    participant Audit as C12 — TsaAuditEventService
    participant Metrics as C11 — TsaMetricsService

    loop cron: every 12h
        Sched->>Svc: detectEligibleTsts()
        Svc->>DB: SELECT tst WHERE cert_expiry <= NOW() + leadTime
        DB-->>Svc: eligible TSTs
        Svc->>State: canRehorodatage()
        alt non autorise
            State-->>Svc: { allowed: false }
            Svc->>Svc: log + skip
        else autorise, count <= batchSize
            State-->>Svc: { allowed: true }
            Svc->>Queue: enqueue batch
            Queue->>Proc: process(batch)
            loop pour chaque TST
                Proc->>TSA: emettre nouveau TST (supersedesTstId)
                TSA-->>Proc: newTstId
                Proc->>DB: INSERT tsa_rehorodatage_record
                Proc->>Metrics: decrementBacklog()
            end
        else count > batchSize
            State-->>Svc: { allowed: true }
            Svc->>Svc: rejet explicite (INV-265-12)
        end
    end

    Note over Sched,Audit: Deadline monitoring
    Sched->>DB: SELECT eligible non traites > deadline
    alt trouves
        Sched->>Audit: emitCriticalAlert(REH_TIMESTAMP_DEADLINE)
    end

3. Mapping invariants → mécanismes

Invariant ID Exigence Mécanisme Composant Observable Risque
INV-265-01 NTS fail-closed → dégradation + blocage TST C4 (worst-case multi-serveur) + C3 (flag NTS) + C9 (guard) NtsMonitoringService, TsaDegradationStateService, TstEmissionGuard tsa_nts_offset_ms, état DEGRADED_NTS, refus HTTP 503 Faux positif si un seul serveur instable (DA-01 accepte ce risque)
INV-265-02 Cert TSA GOOD OCSP + absent CRL + fraîcheur C5 (OCSP+CRL check) + C3 (flag REVOCATION) + C9 (guard) RevocationMonitoringService, TsaDegradationStateService tsa_ocsp_age_seconds, tsa_crl_age_seconds, état DEGRADED_REVOCATION Dépendance aux endpoints OCSP/CRL distants (H-02)
INV-265-03 Labels HSM prod pv-tsa-signing-YYYY, test pv-test-tsa-* C7 (validation label à la rotation) + C14 (CI) KeyLifecycleExtensionService, CI test Pipeline rouge, rejet rotation Aucun
INV-265-04 Label non conforme → échec pipeline CI C14 (test CI dédié) CI label validation test Exit code CI non-zero Aucun
INV-265-05 Âge clé active borné par keyRotationPeriod C7 (age check) + C3 (flag KEY_LIFECYCLE) KeyLifecycleExtensionService, TsaDegradationStateService tsa_active_key_age_days, état DEGRADED_KEY_LIFECYCLE Aucun
INV-265-06 TST éligible re-horodaté + deadline alerting C8 (re-horodatage batch + deadline monitoring) RehorodatageService, RehorodatageProcessor tsa_rehorodatage_backlog_total, paires supersedesTstId, alerte CRITICAL si deadline dépassée Dépendance TSA pour émission nouveau TST (H-03)
INV-265-07 Autorité TSA dans TL ETSI valide C6 (TL fetch + cache + vérification autorité) + C3 (flag TRUSTLIST) TrustedListService, TsaDegradationStateService tsa_trusted_list_cache_age_seconds, état DEGRADED_TRUSTLIST Format TL ETSI instable (H-04)
INV-265-08 Envelope encryption — secrets chiffrés au repos Hérité des modules existants (crypto/HSM). Vérification via test TC-ERR-09. CryptoModule (existant) Scan conformité at-rest Aucun (déjà implémenté)
INV-265-09 Idempotence + retry-safe + rattrapage post-crash C2 (persistance état) + C8 (idempotence re-horodatage) + C3 (reprise état au démarrage) Entities, RehorodatageService, TsaDegradationStateService Cohérence DB post-crash, pas de duplication Aucun
INV-265-10 Tests roundtrip sign+verify en CI Test contractuel dédié dans la suite CI CI test Pipeline vert sur roundtrip Aucun
INV-265-11 Machine d'états exhaustive — transitions non listées interdites C3 (validation transitions dans activateFlag/clearFlag/setMaintenanceMode) TsaDegradationStateService Refus de transition non autorisée, journal Aucun
INV-265-12 Formats définis en §5.1 — pas d'hypothèse implicite C1 (validation config Joi) + DTOs (class-validator) + C3 (validation format flags) TsaMonitoringConfigService, DTOs Rejet validation explicite Aucun
INV-265-13 Canonicalisation RFC 8785 (JCS) + signature clé audit C12 (JCS + AuditSignatureService) TsaAuditEventService, JsonCanonicalizeService (existant), AuditSignatureService (existant) signatureKeyId dans chaque événement, signature vérifiable Aucun
INV-265-14 RBAC sur MAINTENANCE + journalisation refus C10 (RBAC) + C12 (événement denied) MaintenanceToggleService, TsaAuditEventService Refus HTTP 403, événement PV_TSA_MAINTENANCE_TOGGLE_DENIED Aucun

4. Mapping critères d'acceptation → mécanismes

Critère ID Mécanisme(s) Composant Observable Risque
CA-01 Audit Prolog completeness : les invariants PD-265 sont vérifiés formellement. Score >= ⅚ via rapport signé JCS + signatureKeyId C12 (signature rapport) Rapport d'audit signé, score Dépend de l'intégration Prolog (hors scope direct)
CA-02 OCSP/CRL périodique opérationnel : C5 scheduler + métriques + changement flags C5, C3, C11 Métriques tsa_ocsp_age_seconds, tsa_crl_age_seconds, transitions état Aucun
CA-03 NTS fail-closed 100ms : C4 vérifie offset, C3 active flag, C9 bloque TST C4, C3, C9 tsa_nts_offset_ms, état DEGRADED_NTS, refus émission Aucun
CA-04 Rotation clé avec labels conformes : C7 valide label, publie métriques âge C7, C11 tsa_active_key_age_days, journal événement, validation label Aucun
CA-05 Re-horodatage dans fenêtre : C8 détecte éligibles, crée paires, alerte si deadline C8, C12 Paires supersedesTstId, tsa_rehorodatage_backlog_total Aucun
CA-06 TL ETSI active avec TTL : C6 fetch + cache + vérification autorité C6, C3, C11 tsa_trusted_list_cache_age_seconds, état flags Aucun
CA-07 CI échoue pour label hors convention : C14 script de validation C14 Pipeline rouge Aucun
CA-08 Config hors bornes rejetée : C1 validation Joi avec bornes + contraintes croisées C1 Erreur explicite au chargement Aucun
CA-09 Pas de secret en clair au repos : hérité modules existants + test CryptoModule (existant) Scan conformité Aucun
CA-10 Roundtrip sign+verify CI : test contractuel dédié CI test Pipeline vert Aucun
CA-11 Refus TST expose motif normé + état/flags : C9 guard avec refusalReasonCode C9 HTTP 503 avec refusalReasonCode, tsaServiceState, tsaServiceDegradationFlags Aucun
CA-12 Toggle MAINTENANCE protégé RBAC + journalisé : C10 vérification rôle + C12 événement C10, C12 HTTP 403 sur refus, événement PV_TSA_MAINTENANCE_TOGGLE_DENIED Aucun

5. Mapping tests (TC-*) → mécanismes + observables

Test ID Référence spec Mécanisme(s) Point(s) d'observation Niveau
TC-NOM-01 INV-265-01, CA-03, §5.4 C4 (NTS check) + C3 (flag activation) + C9 (refus) flags=[NTS], state=DEGRADED_NTS, refusalReasonCode=NTS_DEGRADED Unit + Integration
TC-NOM-02 §5.4 clearing NTS C4 (2 cycles ok) + C3 (clearFlag) flags=[], state=HEALTHY, émission réouverte Unit + Integration
TC-NOM-03 INV-265-02, CA-02 C5 (OCSP/CRL ok) + C3 (clearFlag REVOCATION) flags cleared, state=HEALTHY, métriques OCSP/CRL Unit + Integration
TC-NOM-04 INV-265-03, CA-04 C7 (rotation) + validation label clé ACTIVE→RETIRED, nouvelle clé ACTIVE, métriques âge Unit + Integration
TC-NOM-05 INV-265-06, CA-05 C8 (re-horodatage) nouveau TST avec supersedesTstId Unit + Integration
TC-NOM-06 INV-265-07, CA-06 C6 (TL valide) + C3 (pas de flag) flags ne contient pas TRUSTLIST, métriques refresh Unit
TC-NOM-07 INV-265-10, CA-10 Test roundtrip sign+verify Pipeline CI vert Integration
TC-NOM-08 CA-01, INV-265-13 C12 (signature JCS + signatureKeyId) Rapport signé, score >= ⅚ Integration
TC-NOM-09 CA-12, §5.4 C10 (RBAC ok) + C3 (MAINTENANCE) + C9 (refus TST) state=MAINTENANCE, refusalReasonCode=MAINTENANCE Unit + Integration
TC-NOM-10 §5.4 sortie MAINTENANCE C10 (disable) + C3 (recalcul) state=DEGRADED (si flags actifs) Unit + Integration
TC-NOM-11 §5.2 ntsReferenceServers C4 (mesure sur serveurs autorisés) + C1 (validation MIN 2) Mesures sur serveurs de la config uniquement Unit
TC-NOM-12 §5.9 nomenclature métriques C11 (exposition métriques tsa_*) Toutes métriques contractuelles présentes Integration
TC-NOM-CLEAR-TRUSTLIST §5.4 clearing TRUSTLIST C6 + C3 flags cleared, PV_TSA_STATE_TRANSITION Unit
TC-NOM-CLEAR-KEY-LIFECYCLE §5.4 clearing KEY_LIFECYCLE C7 + C3 flags cleared, PV_TSA_STATE_TRANSITION Unit
TC-ERR-01 INV-265-01, §5.9 C4 (timeout) + C3 + C12 (alerte critique) flags=[NTS], alerte CRITICAL immédiate Unit
TC-ERR-02 INV-265-02, §5.9 C5 (OCSP REVOKED) + C3 + C12 flags=[REVOCATION], alerte CRITICAL Unit
TC-ERR-03 INV-265-02, §5.3 C5 (CRL LISTED / OCSP UNKNOWN / fraîcheur) + C3 flags=[REVOCATION] Unit
TC-ERR-04 INV-265-07 C6 (TL expirée) + C3 flags=[TRUSTLIST] Unit
TC-ERR-05 INV-265-05, §5.9 C7 (âge > keyRotationPeriod) + C3 + C12 flags=[KEY_LIFECYCLE], alerte KEY_OVERDUE Unit
TC-ERR-06 INV-265-12, CA-08 C1 (validation Joi) Rejet config avec motif Unit
TC-ERR-07 INV-265-12, §5.1 DTOs (class-validator) Rejet données invalides Unit
TC-ERR-08 INV-265-03, INV-265-04 C14 (CI label) + C7 (validation rotation) Pipeline rouge Unit
TC-ERR-09 INV-265-08 Modules existants (crypto) Scan conformité at-rest Integration
TC-ERR-10 INV-265-06 C8 (deadline monitoring) + C12 (alerte CRITICAL) Alerte + export probatoire TST en retard Integration
TC-ERR-11 INV-265-09 C3 (reprise état) + C8 (idempotence) Cohérence DB post-crash, pas de duplication Integration
TC-ERR-12 INV-265-11 C3 (refus transition directe) Refus + journal "transition interdite" Unit
TC-ERR-13 §5.4 composite C4 + C5 + C3 flags=[NTS, REVOCATION], state=DEGRADED Unit
TC-ERR-14 §5.4 maxDegradedDuration C13 (escalation) + C12 (alerte) Alerte CRITICAL, métriques, notification Unit + Integration
TC-ERR-15 §5.5 réactivation interdite C7 (refus réactivation) + C12 (événement) Refus explicite + événement d'audit Unit
TC-ERR-16 §5.2 ntsReferenceServers MIN 2 C1 (validation Joi) Rejet config "ntsReferenceServers MIN 2" Unit
TC-CROSS-01 §5.2 contrainte croisée C1 (validation) Rejet "maxDegradedDuration >= 3 × ntsCheckInterval" Unit
TC-CROSS-02 §5.2 contrainte croisée C1 (validation) Rejet "rehorodatageProcessingDeadline < rehorodatageLeadTime×24" Unit
TC-NEG-01 §5.1 DTOs Rejet certificateSerialHex avec char non hex Unit
TC-NEG-02 §5.1 DTOs Rejet ocspStatus casse incorrecte Unit
TC-NEG-03 §5.1 C7 / C14 Rejet label hsmKeyLabel prod non conforme Unit
TC-NEG-04 §5.1 DTOs Rejet tstId non UUIDv4 Unit
TC-NEG-05 §5.2 C8 Rejet batch > rehorodatageBatchSize (pas de clamp) Unit
TC-NEG-06 §5.1 DTOs Rejet trustedListDigestSha256 longueur 63 Unit
TC-NEG-07 §5.4 C3 Refus sortie MAINTENANCE sans action manuelle Unit
TC-NEG-08 INV-265-08 Modules existants Contrôle conformité KO si secret en clair Integration
TC-NEG-09 INV-265-14, CA-12 C10 + C12 Refus RBAC + événement denied + état inchangé Unit + Integration

6. Gestion des erreurs

Code / Situation Mécanisme Composant Observable
NTS timeout/source invalide Activation flag NTS, alerte CRITICAL immédiate, blocage TST C4, C3, C12, C9 tsa_nts_offset_ms, état DEGRADED_NTS, événement PV_TSA_CRITICAL_ALERT
NTS offset > seuil Idem timeout C4, C3, C12, C9 Idem
OCSP REVOKED / UNKNOWN Activation flag REVOCATION, alerte CRITICAL, blocage TST C5, C3, C12, C9 état DEGRADED_REVOCATION, événement CRITICAL
CRL LISTED / UNAVAILABLE Activation flag REVOCATION C5, C3 Idem
Fraîcheur OCSP/CRL > maxAge Activation flag REVOCATION (fail-closed) C5, C3 tsa_ocsp_age_seconds / tsa_crl_age_seconds
TL expirée/invalide au-delà TTL Activation flag TRUSTLIST C6, C3 état DEGRADED_TRUSTLIST
Autorité TSA absente de la TL Activation flag TRUSTLIST C6, C3 Idem
Âge clé > keyRotationPeriod Activation flag KEY_LIFECYCLE, alerte KEY_OVERDUE C7, C3, C12 tsa_active_key_age_days, état DEGRADED_KEY_LIFECYCLE
Re-horodatage deadline dépassée Alerte CRITICAL + export probatoire C8, C12 Liste TST en retard, événement CRITICAL
Dégradation > maxDegradedDuration (par flag) Alerte CRITICAL + notification exploitation C13, C12 tsa_degraded_duration_seconds, événement CRITICAL
Config hors bornes / contrainte croisée Rejet au chargement, application ne démarre pas C1 Erreur Joi avec paramètre et borne
Données invalides (format §5.1) Rejet validation, pas de persistance DTOs Erreur class-validator
Label HSM non conforme Rejet rotation / pipeline CI rouge C7, C14 Échec CI, refus rotation
Réactivation clé RETIRED/ARCHIVED/DESTROYED Refus explicite + événement d'audit C7, C12 Événement d'audit, pas de changement d'état
Toggle MAINTENANCE sans rôle HTTP 403 + événement PV_TSA_MAINTENANCE_TOGGLE_DENIED C10, C12 Événement denied, pas de changement d'état
Transition d'état non autorisée Refus explicite + journal C3 Journal "transition interdite"
Crash post-commit État DB cohérent, rattrapage async idempotent C2, C3, C8 Reprise sans duplication
Batch re-horodatage > rehorodatageBatchSize Rejet explicite (pas de clamp) C8 Erreur "hors bornes" traçable

7. Impacts sécurité

Confidentialité

  • INV-265-08 : Tout secret crypto temporaire chiffré au repos (AES-256-GCM ou HSM envelope). Vérifié par TC-ERR-09.
  • Les événements d'audit ne contiennent JAMAIS de secret (champ details = valeurs observées sans secret).
  • Les métriques Prometheus n'exposent aucun label à haute cardinalité (pas d'URL, fingerprint, UUID).

Intégrité

  • INV-265-13 : Tous les événements/rapports signés avec canonicalisation RFC 8785 (JCS) + clé d'audit dédiée (auditSignatureKeyId).
  • INV-265-10 : Roundtrip sign+verify obligatoire en CI (learning PD-282).
  • La machine d'états est la seule interface de modification des flags. Pas d'accès direct en écriture à la table tsa_degradation_state.

Disponibilité

  • Fail-closed : En cas de doute, l'émission TST est bloquée (principe de précaution).
  • maxDegradedDuration : Escalade si dégradation prolongée, empêche le silence opérationnel.
  • MAINTENANCE mode : Arrêt contrôlé via RBAC, pas de bascule accidentelle.

Journalisation

  • Toute transition d'état → événement PV_TSA_STATE_TRANSITION signé/horodaté.
  • Toute alerte critique → événement PV_TSA_CRITICAL_ALERT signé/horodaté.
  • Tout refus RBAC → événement PV_TSA_MAINTENANCE_TOGGLE_DENIED signé/horodaté.
  • Tout refus d'émission TST → log avec refusalReasonCode + état + flags.

Clé d'audit

  • La clé d'audit (auditSignatureKeyId) est distincte de la clé TSA RFC 3161.
  • Protégée HSM/KMS, accès restreint.
  • Rotation indépendante de la clé TSA.
  • signatureKeyId présent dans chaque événement/rapport pour traçabilité.

8. Hypothèses techniques

ID Hypothèse Impact si faux
H-01 Le service TSA expose un état opérationnel consommable via une table DB singleton (tsa_degradation_state) Sans table d'état, pas de persistance ni reprise après crash
H-02 Les endpoints OCSP/CRL de l'autorité sont accessibles depuis l'environnement d'exécution backend Faux positifs permanents de dégradation REVOCATION
H-03 Les TST historiques sont accessibles en base (timestamp_token) pour requête de re-horodatage Re-horodatage impossible, non-conformité long terme
H-04 La Trusted List ETSI est récupérable en XML dans un format stable et parsable Vérification TL impossible, flag TRUSTLIST permanent
H-05 Le périmètre reste backend TSA — pas de nouveau module inter-services Si cross-module nécessaire, extension de spec requise
H-IMPL-01 ntpd-rs est disponible en production et expose observe.json avec les données NTS multi-serveurs Si ntpd-rs non disponible, le NTS monitoring doit utiliser une implémentation NTS directe
H-IMPL-02 Le module audit existant (AuditLogService, JsonCanonicalizeService, AuditSignatureService) est compatible avec le format d'événement PD-265 Si incompatible, adaptation nécessaire du format d'événement
H-IMPL-03 OcspClientService existant dans legal-pre expose une méthode utilisable pour les vérifications périodiques (pas seulement one-shot) Si couplé au flux legal-pre, un nouveau client OCSP sera nécessaire
H-IMPL-04 BullMQ v5 est la version installée (API getJobSchedulers(), pas getRepeatableJobs()) Si BullMQ v4, adapter l'API
H-IMPL-05 La librairie Prometheus (prom-client ou @willsoto/nestjs-prometheus) est disponible ou installable Si non disponible, utiliser un autre mécanisme d'exposition métriques

9. Points de vigilance (risques, dette, pièges)

V-01 — Faux positifs NTS (worst-case)

La stratégie worst-case (DA-01) peut générer des faux positifs si un seul serveur NTS est temporairement instable. Le clearing par 2 cycles consécutifs conformes atténue ce risque mais ne l'élimine pas. Mitigation : ntsReferenceServers configurable, choisir des serveurs fiables (ex: Cloudflare, Netnod).

V-02 — Cardinalité métriques Prometheus

Les labels des métriques sont strictement bornés aux enums. Ne JAMAIS ajouter de label libre (URL, fingerprint, UUID) sous peine d'explosion de cardinalité.

V-03 — Dépendance OCSP/CRL externes

Les endpoints OCSP/CRL sont des services tiers. Les timeouts (ocspTimeout, crlTimeout) doivent être configurés de manière réaliste pour éviter les blocages. Un timeout est traité comme un échec (fail-closed).

V-04 — Format Trusted List ETSI

Le format XML de la Trusted List ETSI peut évoluer. Le parser doit être robuste aux variations mineures. Documenter la version de schéma supportée.

V-05 — Performance du re-horodatage batch

Le re-horodatage par lots peut être coûteux (émission de TST = opération HSM). Le rehorodatageBatchSize (défaut 500) et le scheduling doivent être ajustés pour ne pas surcharger le HSM en production.

V-06 — ALTER TYPE ADD VALUE PostgreSQL

Si de nouveaux enums sont ajoutés en migration (ex: états de service, reasonCodes), utiliser commitTransaction() avant toute clause WHERE utilisant la nouvelle valeur (learning PD-282/PD-279).

V-07 — Durée continue de dégradation par flag

La mesure de flagActivationTimestamps doit être résistante aux redémarrages. La persistance en base (C2) garantit que le timestamp d'activation n'est pas perdu au restart. Le scheduler C13 recalcule la durée depuis la base.

V-08 — SLA post-rotation non enforcés par flag

Les SLA keyRetiredToArchivedDelay et keyArchivedToDestroyedDelay sont enforcés par alerte uniquement (DA-02), pas par flag de dégradation. L'opérateur doit agir manuellement. Risque de clé RETIRED persistante si alertes ignorées.

V-09 — Spec review ecarts mineurs résiduels

Les 8 écarts mineurs résiduels (R-05, R-06, R-09, R-10, R-11, R-13, R-14, R-18) sont documentés mais non bloquants. L'implémentation les traite comme suit : - R-05 (TC-NOM-04 transition complète) : le test vérifie uniquement ACTIVE→RETIRED, pas la transition complète. - R-06 (maxDegradedDuration ratio par flag) : accepté tel quel, opérateur ajuste si nécessaire. - R-09 (MAINTENANCE→HEALTHY non testé) : ajout d'un test unitaire supplémentaire pour ce path. - R-10 (rehorodatageBatchSize runtime) : traité comme validation de configuration au chargement (rejet si hors bornes). En runtime, si le nombre de TST éligibles dépasse la taille configurée, rejet explicite + alerte. - R-11 (re-horodatage en DEGRADED_KEY_LIFECYCLE contestable) : risque explicitement accepté par la spec. - R-13 (durée dégradation NTS vs horloge locale) : hypothèse documentée (dérive ms vs mesure heures). - R-14 (flags doublons/tri/inconnu) : les flags sont calculés en interne (Set), pas d'entrée externe. Tri lexicographique à la sérialisation. - R-18 (format export probatoire) : format JSON signé avec liste nominative des TST en retard (tstId, eligibleSince, deadline).


10. Hors périmètre

  • Modification du worker d'ancrage blockchain PD-55.
  • Modification du mécanisme de nonce anti-rejeu PD-264.
  • Provisionnement d'infrastructure Prometheus/Grafana/HSM.
  • Migration du cluster HSM existant PD-7.
  • Implémentation d'un fallback NTP.
  • Toute implémentation de mécanisme non explicitement défini dans la spécification.
  • Alerting rules Prometheus/Grafana (consommation des métriques exposées).
  • Destruction effective des clés HSM (procédure opérateur manuelle).
  • Configuration de ntpd-rs pour supporter les serveurs ntsReferenceServers.

11. Périmètre de test

Niveau de test In scope Hors scope (justification)
Unitaire Tous les composants C1-C14 (state machine, monitoring, lifecycle, guard, metrics, events, config validation, label CI)
Intégration Interactions C3↔C4/C5/C6/C7 (monitoring→state machine), C3↔C9 (state→guard), C8↔C3 (re-horodatage→state), C12↔AuditModule (events→signature), C11↔Prometheus (metrics), crash/recovery (C2↔C3)
E2E Flux complets F1-F6 en environnement de test avec DB PostgreSQL réelle et mocks HSM/OCSP/CRL/TL Flux avec HSM physique réel (non disponible en CI, simulé via mock PKCS#11)

Justifications hors scope : - HSM physique : non disponible en CI. Les tests HSM utilisent le mock PKCS#11 existant (CloudHsmPkcs11Provider avec provider de test). - Endpoints OCSP/CRL réels : les tests utilisent des mocks/stubs HTTP pour simuler les réponses OCSP/CRL. - Trusted List ETSI réelle : les tests utilisent une fixture XML de TL pour le parsing.

Tous les niveaux de test intra-story sont couverts. Aucune exclusion non justifiée.


12. Mécanismes cross-module

Élément Détail
Module audit Utilisation de AuditLogService, JsonCanonicalizeService, AuditSignatureService existants pour signer les événements PD-265. Pas de modification de ces services — utilisation via injection.
Module legal-pre Utilisation de OcspClientService existant pour les requêtes OCSP périodiques. Si l'interface ne convient pas (couplage au flux legal-pre), création d'un client OCSP dédié dans le module TSA.
Module crypto Lecture de KeyRecord pour les métadonnées de clés HSM. Extension possible avec TsaKeyLifecycleMetadata lié par FK. Pas de modification du RotationOrchestratorService existant.
Module tsa (existant) Extension du module avec les nouveaux services/entities/schedulers. ReferenceClockService étendu avec méthode measureOffset(server). TsaModule modifié pour enregistrer les nouveaux providers.

Impact : Non-breaking sur tous les modules existants. Les extensions sont additives.


13. Contraintes techniques

Runtime & module system

  • Node.js : >=20 LTS (support ESM natif, crypto.randomUUID() stable).
  • Module system : CommonJS (aligné avec le reste du backend NestJS). Les imports node:crypto et node:timers/promises utilisent le préfixe node:.
  • TypeScript : strict mode, target ES2022.
  • Framework de test : Jest (aligné avec le backend existant). Configuration jest.config.ts existante avec ts-jest transformer. Mocks : jest.mock() pour les dépendances NestJS. Timers : jest.useFakeTimers() pour les schedulers. Runner CI : npm test -- --ci --coverage.

CI/CD

  • Pipeline GitLab : Les tests PD-265 sont exécutés dans le job test existant (runner ovh-shell-dev).
  • Sonar : Les nouveaux fichiers PD-265 sont inclus dans l'analyse SonarQube (pas d'exclusion).
  • HSM mock : Les tests CI utilisent le mock PKCS#11 (CloudHsmPkcs11Provider avec provider de test). Aucune dépendance matérielle.
  • BullMQ : v5+ (getJobSchedulers(), pas getRepeatableJobs() — learning PD-55).
  • PostgreSQL : ALTER TYPE ADD VALUE avec commitTransaction() séparé (learning PD-282/PD-279).

Dépendances

  • Nouvelles : aucune dépendance npm ajoutée. Toutes les librairies nécessaires (prom-client, BullMQ, TypeORM, class-validator, Joi) sont déjà dans le package.json du backend.
  • ntpd-rs : Dépendance runtime optionnelle (H-IMPL-01). Si indisponible, NtsMonitoringService utilisera une implémentation NTS directe via socket UDP.

Wiring NestJS

  • Nouveau controller : TsaMonitoringController dans src/modules/tsa/controllers/ — séparé du TsaController existant (émission TST).
  • Enregistrement providers : Tous les services/schedulers PD-265 sont enregistrés dans TsaModule.providers[]. Les entities sont déclarées dans TypeOrmModule.forFeature([...]) du module TSA.
  • Scheduling : @nestjs/schedule est déjà configuré globalement. Les schedulers PD-265 utilisent @Cron() decorator.

Ordonnancement des agents

Phase Agent Composants Dépendance
1 (Foundation) agent-foundation C1, C2, C3 Aucune
2 (Monitoring) agent-monitoring C4, C5, C6 Phase 1
2 (Lifecycle) agent-lifecycle C7, C8 Phase 1
2 (CI) agent-ci C14 Aucune
3 (Integration) agent-integration C9, C10, C11, C12, C13 Phases 1+2

Parallélisation : Phase 2 agents (agent-monitoring, agent-lifecycle, agent-ci) en parallèle après Phase 1.