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=true → MAINTENANCE - flags.length === 0 ET maintenanceMode=false → HEALTHY - 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 >= 2 → DEGRADED (é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 > keyRotationPeriod → degradationStateService.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_TRANSITIONsigné/horodaté. - Toute alerte critique → événement
PV_TSA_CRITICAL_ALERTsigné/horodaté. - Tout refus RBAC → événement
PV_TSA_MAINTENANCE_TOGGLE_DENIEDsigné/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.
signatureKeyIdpré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
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:cryptoetnode:timers/promisesutilisent le préfixenode:. - TypeScript : strict mode, target ES2022.
- Framework de test : Jest (aligné avec le backend existant). Configuration
jest.config.tsexistante avects-jesttransformer. 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
testexistant (runnerovh-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 (
CloudHsmPkcs11Provideravec provider de test). Aucune dépendance matérielle. - BullMQ : v5+ (
getJobSchedulers(), pasgetRepeatableJobs()— learning PD-55). - PostgreSQL :
ALTER TYPE ADD VALUEaveccommitTransaction()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.jsondu backend. - ntpd-rs : Dépendance runtime optionnelle (H-IMPL-01). Si indisponible,
NtsMonitoringServiceutilisera une implémentation NTS directe via socket UDP.
Wiring NestJS¶
- Nouveau controller :
TsaMonitoringControllerdanssrc/modules/tsa/controllers/— séparé duTsaControllerexistant (émission TST). - Enregistrement providers : Tous les services/schedulers PD-265 sont enregistrés dans
TsaModule.providers[]. Les entities sont déclarées dansTypeOrmModule.forFeature([...])du module TSA. - Scheduling :
@nestjs/scheduleest 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.