Aller au contenu

PD-254 — Plan d'implémentation : Protocole formel de migration probatoire vérifiable

Story : PD-254 Projet : ProbatioVault-doc (documentation) + ProbatioVault-backend (vérification/outillage) Domaine : legal-compliance Étape : 4 — Plan d'implémentation Date : 2026-03-13 Statut : DRAFT Gate 3 : RESERVE v2 (7.625/10) — 2 bloquants résiduels (R-01, R-02), 3 majeurs, 3 mineurs


1. Découpage en composants

PD-254 est une story hybride : documentation du protocole (ProbatioVault-doc) et implémentation du moteur de vérification (ProbatioVault-backend, §10.2). Aucune migration effective de données n'est produite.

C1 — Protocol Document (Markdown)

Propriété Valeur
Chemin cible docs/epics/legal-compliance/PD-254-protocole-migration-probatoire/protocole-migration-probatoire.md
Type Nouveau fichier Markdown normatif
Responsabilité Documenter le protocole pas-à-pas pour les 3 types de migration (support, provider, région)
Agent owner agent-doc-writer

Contenu :

  • Description des 3 types de migration couverts (support, provider, région)
  • Diagramme d'états de la machine formelle (10 états + 5 codes métier + 1 code métier résiduel R-01)
  • Table des transitions autorisées/interdites
  • Procédure opérationnelle pas-à-pas (F1→F5)
  • Prérequis et checklist pré-migration
  • Runbook de rollback
  • Références normatives (NF Z42-013 §12.1, ISO 14641)

C2 — MigrationCampaignEntity + StateMachine

Propriété Valeur
Chemin cible src/migration/entities/migration-campaign.entity.ts + src/migration/migration-state-machine.ts
Type Entity TypeORM + module de transitions
Responsabilité Persister l'état de campagne, appliquer les transitions autorisées, rejeter les interdites
Agent owner agent-state-machine

Colonnes entity :

  • migration_id (PK, UUID v4, normalisé lowercase)
  • protocol_version (semver)
  • state (enum des 10 états formels)
  • manifest_hash (hex SHA3-256, 64 chars)
  • manifest_signature (base64, 88-684 chars)
  • source_object_count (integer)
  • hash_global_before / hash_global_after (hex SHA3-256)
  • initiator (service account ou user id)
  • clearing_counter (integer, défaut 0)
  • precheck_completed_at (timestamp, pour TTL)
  • last_activity (timestamp, pour orphan detection)
  • created_at / updated_at (timestamps)

Machine d'états :

  • Table de transitions Map<State, Map<State, {allowed: boolean, guard?: Function}>>.
  • Les codes métier (INVALID_INPUT, INVALID_MANIFEST, CONCURRENT_EXECUTION_DENIED, ATTESTATION_FAILED, PRECHECK_EXPIRED) ne provoquent AUCUNE transition — ils sont retournés comme codes de réponse.
  • Décision technique pour R-01/R-02 : MANIFEST_STALE est traité comme un code métier complémentaire (6e code). Pour R-02, la transition POSTCHECK_RUNNING → DRAFT est impossible selon §5.2 ; le plan implémente POSTCHECK_RUNNING → ROLLED_BACK puis nouvelle campagne depuis DRAFT. Voir H-T01/H-T02.

C3 — ManifestService

Propriété Valeur
Chemin cible src/migration/services/manifest.service.ts
Type Service NestJS injectable
Responsabilité Génération, sérialisation canonique, hash SHA3-256 et signature HSM du manifest
Agent owner agent-manifest

Mécanismes :

  • Sérialisation JSON canonique via RFC 8785 (JCS) — résout R-03 (canonicalisation non spécifiée). Bibliothèque : canonicalize (npm).
  • Calcul manifest_hash = SHA3-256 du JSON canonique.
  • Signature manifest_signature via HSM (CloudHSM, CKM_ECDSA avec clé dédiée, label pv-migration-manifest-*).
  • Stockage de manifest_hash + manifest_signature dans un registre indépendant (table manifest_registry).
  • Revalidation au postcheck : recalcul hash + vérification signature HSM (crypto.verify(null, hash, key, sig) — pas createVerify, cf. learning PD-282).

C4 — VerificationEngine

Propriété Valeur
Chemin cible src/migration/services/verification-engine.service.ts
Type Service NestJS injectable
Responsabilité Exécuter les contrôles precheck/postcheck : SHA3, Merkle, GRH, ancrage blockchain, lisibilité
Agent owner agent-verification

Sous-contrôles :

  1. IntegrityCheck : sha3_before == sha3_after pour chaque objet (INV-254-01).
  2. MerkleCheck : merkle_root_before == merkle_root_after objet par objet (INV-254-02).
  3. GlobalRootHash : tri lexicographique hex lowercase des merkle_root, concaténation, SHA3-256 (INV-254-03).
  4. AnchorCheck : vérification de chaque tx_hash dans le registre blockchain (INV-254-04).
  5. ReadabilityCheck : échantillon aléatoire, 3 critères — (a) ouverture I/O, (b) MIME détecté == MIME enregistré, © 1024 premiers octets décodables (INV-254-05).
  6. SourceObjectCountCheck : revalidation source_object_count au postcheck (CA-17, protection TOCTOU).
  7. ManifestIntegrityCheck : revalidation manifest_hash + manifest_signature au début du postcheck (INV-254-12).

Mode dry-run : F3 compare source/source (pas de dataset cible requis).

C5 — AttestationService

Propriété Valeur
Chemin cible src/migration/services/attestation.service.ts
Type Service NestJS injectable
Responsabilité Génération de migration_attestation.json, signature HSM, archivage probatoire
Agent owner agent-attestation

Mécanismes :

  • Génération JSON conforme au schéma §5.14 (incluant protocol_version, manifest_hash).
  • Signature HSM via clé dédiée (label pv-migration-attestation-*).
  • SLA configurable (défaut 15min, max 4h). Dépassement → ATTESTATION_FAILED → rollback.
  • Archivage en stockage probatoire (bucket S3 WORM, prefix contractuel — Q-07 non résolu, paramétrable).
  • Notification DevOps/TechLead/PO (canal configurable — Q-05 non résolu, abstraction NotificationPort).

C6 — DistributedProtectionService

Propriété Valeur
Chemin cible src/migration/services/distributed-protection.service.ts
Type Service NestJS injectable (Redis-backed)
Responsabilité Lock distribué, idempotence, reconciliation, rate-limiting, clearing conditionnel
Agent owner agent-protection

Mécanismes :

  1. Lock distribué : clé Redis migration:lock:{migration_id}, TTL configurable (60-3600s, défaut 600s). SET NX.
  2. Idempotence : clé migration:idemp:{migration_id}:{phase}, TTL idempotency_ttl (1-720h). Stocke verdict + hash artefacts logiques.
  3. Reconciliation : cron BullMQ reconciliation_interval (1-60min). Détection orphelins (last_activity > orphan_threshold). Reprise idempotente ou RECONCILIATION_FAILED.
  4. Rate-limiting : clé Redis migration:rate:{initiator}, fenêtre glissante 1h, max 3 lancements.
  5. Clearing conditionnel : compteur clearing_counter persisté par campagne. N cycles conformes consécutifs requis (défaut 3). Un seul non-conforme → compteur = 0.

C7 — CI/CD Pipeline Integration

Propriété Valeur
Chemin cible .gitlab-ci.yml (extension) + scripts/migration-gate.sh
Type Job GitLab CI + script shell
Responsabilité Quality gate bloquant, dry-run automatisé, notification
Agent owner agent-ci

Mécanismes :

  • Job migration-verify : exécute le protocole en mode --dry-run ou full.
  • Exit codes : 0 = GO, 1 = KO (invariant violé), 2 = erreur technique — Q-06 non résolu, valeurs choisies par convention.
  • Blocage pipeline sur exit != 0.
  • Artefacts CI : manifest, rapports precheck/postcheck/diff, attestation.

C8 — WormGuard

Propriété Valeur
Chemin cible src/migration/guards/worm.guard.ts
Type Guard NestJS
Responsabilité Intercepter toute tentative de suppression/altération d'objet WORM pendant une campagne active
Agent owner agent-verification

Mécanismes :

  • Vérification retention_expiry avant toute opération destructive.
  • Émission événement sécurité WORM_VIOLATION_BLOCKED si violation détectée.
  • Arrêt immédiat de la campagne.

2. Flux techniques

Flux F1 — Préparation (DRAFT → PRECHECK_RUNNING)

1. Réception requête avec migration_id + migration_type + protocol_version
2. Validation format migration_id (UUID v4, normalisation lowercase)
   → Si invalide : code métier INVALID_INPUT, aucune transition
3. Vérification idempotence (pas de campagne active avec même migration_id)
4. Acquisition lock distribué migration:lock:{migration_id}
   → Si lock actif : code métier CONCURRENT_EXECUTION_DENIED
5. Vérification rate-limit initiateur (max 3/h)
   → Si dépassement : rejet explicite
6. Vérification préconditions : accès source, accès cible, accès registre blockchain, accès HSM, runbook rollback présent
7. Création entity MigrationCampaign en état DRAFT
8. Transition DRAFT → PRECHECK_RUNNING

Flux F2 — Precheck + Manifest (PRECHECK_RUNNING → PRECHECK_PASSED|PRECHECK_FAILED)

1. Inventaire des objets source (doc_id, sha3, merkle_root, s3_object_key, size, timestamp, tx_hash)
2. Validation format de chaque champ (regex §5.1)
   → Si invalide : objet INVALID_METADATA, migration KO si >0
3. Génération manifest.json (JSON canonique RFC 8785, clés triées)
4. Calcul manifest_hash = SHA3-256(manifest_canonique)
5. Signature manifest via HSM → manifest_signature
6. Stockage manifest_hash + manifest_signature dans manifest_registry (table séparée)
7. Enregistrement source_object_count
8. Calcul hash_global_before (GRH) :
   a. Extraire tous les merkle_root
   b. Trier par ordre lexicographique hex lowercase
   c. Concaténer les chaînes triées
   d. SHA3-256(concaténation)
9. Exécution contrôles precheck :
   a. IntegrityCheck (SHA3 objet)
   b. MerkleCheck (merkle_root objet)
   c. AnchorCheck (tx_hash vs registre blockchain)
   d. ReadabilityCheck (échantillon, 3 critères)
10. Production rapport precheck JSON
11. Si 100% invariants OK → PRECHECK_PASSED (+ enregistrement precheck_completed_at)
    Sinon → PRECHECK_FAILED

Flux F3 — Postcheck (CUTOVER_AUTHORIZED → POSTCHECK_RUNNING → VERIFIED|ROLLED_BACK)

1. Vérification TTL precheck (precheck_completed_at + precheck_ttl)
   → Si expiré : code métier PRECHECK_EXPIRED, transition PRECHECK_PASSED → DRAFT
2. Transition CUTOVER_AUTHORIZED → POSTCHECK_RUNNING
3. Chargement manifest de référence
4. Revalidation manifest :
   a. Recalcul SHA3-256 du manifest chargé
   b. Comparaison avec manifest_hash stocké → Si diverge : KO immediate
   c. Vérification manifest_signature via HSM → Si invalide : KO immediate
5. Revalidation source_object_count actuel vs enregistré
   → Si divergence : KO, code métier MANIFEST_STALE (cf. H-T01)
   → Transition → ROLLED_BACK (cf. H-T02 — pas DRAFT depuis POSTCHECK_RUNNING)
6. Inventaire dataset cible post-migration
7. Calcul hash_global_after (même algorithme GRH que F2)
8. Comparaison pre/post :
   a. IntegrityCheck objet par objet (sha3, merkle_root, size, présence)
   b. AnchorCheck (tx_hash blockchain)
   c. ReadabilityCheck échantillon post-migration (3 critères)
9. Production rapport différentiel JSON
10. Si invariants OK → VERIFIED
    Sinon → ROLLED_BACK (ou RECONCILIATION_FAILED si récupération impossible)

Flux F4 — Attestation (VERIFIED → ATTESTED)

1. Génération migration_attestation.json (conforme schéma §5.14)
   - Inclut : protocol_version, manifest_hash, hash_global_before/after,
     total_objects, readability_sample_count, readability_pass_rate,
     anchor_verification_rate, verdict
2. Signature attestation via HSM
   → Si HSM indisponible au-delà du SLA : code métier ATTESTATION_FAILED, rollback
3. Archivage attestation comme objet probatoire (bucket S3 WORM)
4. Notification DevOps/TechLead/PO
5. Transition VERIFIED → ATTESTED (état terminal)

Flux F5 — Dry-run

1. Exécution F1 + F2 normalement
2. Simulation F3 : postcheck compare source avec source (source/source)
   - Pas de dataset cible requis
   - Aucune opération de migration/copie
3. Interdiction toute transition vers exécution effective
4. Production rapport dry-run avec code retour CI (0=GO, 1=NO_GO)

Flux — Expiration TTL precheck (PRECHECK_PASSED → DRAFT)

Mécanisme de détection (résout R-05) : vérification LAZY
- Le TTL est vérifié au moment de la tentative de transition PRECHECK_PASSED → CUTOVER_AUTHORIZED
- Si precheck_completed_at + precheck_ttl < now() :
  → Code métier PRECHECK_EXPIRED émis
  → Transition PRECHECK_PASSED → DRAFT
  → La campagne doit relancer un precheck complet
Note : pas de cron proactif — le TTL est garanti vérifié car le seul
chemin vers CUTOVER_AUTHORIZED passe par ce guard.

2b. Diagrammes Mermaid

Graphe de dépendances entre composants

graph TD
    C1["C1 — Protocol Document<br/>(Markdown normatif)"]
    C2["C2 — MigrationCampaignEntity<br/>+ StateMachine"]
    C3["C3 — ManifestService"]
    C4["C4 — VerificationEngine"]
    C5["C5 — AttestationService"]
    C6["C6 — DistributedProtectionService<br/>(Redis-backed)"]
    C7["C7 — CI/CD Pipeline<br/>(migration-gate.sh)"]
    C8["C8 — WormGuard"]

    C7 -->|"lance dry-run / full"| C4
    C7 -->|"exit codes"| C2
    C4 -->|"contrôles precheck/postcheck"| C2
    C4 -->|"revalidation manifest"| C3
    C5 -->|"génère attestation après VERIFIED"| C2
    C5 -->|"signe via HSM"| C3
    C6 -->|"lock / idempotence / reconciliation"| C2
    C8 -->|"interception WORM violation"| C2

    C3 -->|"hash SHA3-256 + signature HSM"| HSM["HSM CloudHSM<br/>(clés pv-migration-*)"]
    C5 -->|"signature attestation"| HSM
    C4 -->|"AnchorCheck"| BC["Registre Blockchain"]
    C4 -->|"ReadabilityCheck"| S3["Stockage S3"]
    C5 -->|"archivage probatoire"| S3WORM["S3 WORM"]
    C6 -->|"lock / idempotence / rate-limit"| Redis["Redis"]

    style C2 fill:#e1f5fe,stroke:#0277bd
    style C4 fill:#e8f5e9,stroke:#2e7d32
    style C3 fill:#fff3e0,stroke:#ef6c00
    style C5 fill:#fce4ec,stroke:#c62828
    style C6 fill:#f3e5f5,stroke:#7b1fa2

Diagramme de séquence — Flux complet F1→F4 (multi-services)

sequenceDiagram
    participant Init as Initiateur
    participant SM as C2 StateMachine
    participant DP as C6 DistributedProtection
    participant MS as C3 ManifestService
    participant VE as C4 VerificationEngine
    participant HSM as HSM CloudHSM
    participant AS as C5 AttestationService
    participant S3 as S3 WORM

    Note over Init,S3: F1 — Préparation (DRAFT → PRECHECK_RUNNING)
    Init->>DP: Vérification lock + rate-limit
    DP-->>Init: OK (lock acquis)
    Init->>SM: Création campagne (DRAFT)
    SM->>SM: Transition DRAFT → PRECHECK_RUNNING

    Note over Init,S3: F2 — Precheck + Manifest (→ PRECHECK_PASSED)
    SM->>VE: Lancement precheck
    VE->>MS: Génération manifest (JSON canonique RFC 8785)
    MS->>HSM: Signature manifest (CKM_ECDSA)
    HSM-->>MS: manifest_signature
    MS-->>VE: manifest_hash + manifest_signature
    VE->>VE: IntegrityCheck + MerkleCheck + GRH
    VE->>VE: AnchorCheck + ReadabilityCheck
    VE-->>SM: Rapport precheck (100% OK)
    SM->>SM: Transition → PRECHECK_PASSED

    Note over Init,S3: Autorisation cutover (manuelle)
    Init->>SM: Autorisation cutover
    SM->>SM: Guard TTL precheck
    SM->>SM: Transition → CUTOVER_AUTHORIZED

    Note over Init,S3: F3 — Postcheck (→ VERIFIED)
    SM->>SM: Transition → POSTCHECK_RUNNING
    SM->>VE: Lancement postcheck
    VE->>MS: Revalidation manifest (hash + signature HSM)
    MS->>HSM: crypto.verify(null, hash, key, sig)
    HSM-->>MS: Signature valide
    VE->>VE: Revalidation source_object_count
    VE->>VE: IntegrityCheck + MerkleCheck + GRH post
    VE->>VE: AnchorCheck + ReadabilityCheck post
    VE-->>SM: Rapport postcheck (invariants OK)
    SM->>SM: Transition → VERIFIED

    Note over Init,S3: F4 — Attestation (→ ATTESTED)
    SM->>AS: Génération attestation
    AS->>HSM: Signature attestation (CKM_ECDSA)
    HSM-->>AS: attestation_signature
    AS->>S3: Archivage attestation (bucket WORM)
    AS-->>SM: Attestation archivée
    SM->>SM: Transition → ATTESTED (terminal)

Machine d'états — Transitions autorisées

stateDiagram-v2
    [*] --> DRAFT
    DRAFT --> PRECHECK_RUNNING : F1 (préconditions OK)
    PRECHECK_RUNNING --> PRECHECK_PASSED : Precheck 100% OK
    PRECHECK_RUNNING --> PRECHECK_FAILED : Invariant violé
    PRECHECK_FAILED --> DRAFT : Correction + relance
    PRECHECK_PASSED --> CUTOVER_AUTHORIZED : Autorisation (guard TTL)
    PRECHECK_PASSED --> DRAFT : PRECHECK_EXPIRED (TTL dépassé)
    CUTOVER_AUTHORIZED --> POSTCHECK_RUNNING : F3 lancement
    POSTCHECK_RUNNING --> VERIFIED : Invariants OK
    POSTCHECK_RUNNING --> ROLLED_BACK : Invariant violé / MANIFEST_STALE (H-T02)
    VERIFIED --> ATTESTED : F4 attestation signée
    VERIFIED --> ROLLED_BACK : ATTESTATION_FAILED (SLA HSM)
    POSTCHECK_RUNNING --> RECONCILIATION_FAILED : Récupération impossible

    ATTESTED --> [*]
    ROLLED_BACK --> [*]
    RECONCILIATION_FAILED --> [*]

    note right of DRAFT : Codes métier (sans transition) :<br/>INVALID_INPUT<br/>INVALID_MANIFEST<br/>CONCURRENT_EXECUTION_DENIED<br/>PRECHECK_EXPIRED<br/>MANIFEST_STALE<br/>ATTESTATION_FAILED

3. Mapping invariants → mécanismes

Invariant ID Exigence Mécanisme Composant Observable Risque
INV-254-01-integrite-objet sha3_before == sha3_after pour 100% des objets IntegrityCheck : calcul SHA3-256 de chaque objet cible, comparaison avec référence manifest C4 VerificationEngine Rapport precheck/postcheck avec verdict par doc_id Faux positif si objet modifié légitimement entre precheck et postcheck (couvert par freeze H-05)
INV-254-02-merkle merkle_root_before == merkle_root_after MerkleCheck : comparaison objet par objet des merkle_root du manifest vs cible C4 VerificationEngine Rapport postcheck sans écart Merkle Dépendance sur les données Merkle existantes (H-01)
INV-254-03-global-root-hash hash_global_before == hash_global_after GRH : tri lexicographique hex lowercase des merkle_root, concaténation, SHA3-256 C4 VerificationEngine Valeur GRH tracée dans les artefacts, comparaison stricte Ordre de tri ambigu si merkle_root dupliqués (impossible par contrainte d'unicité doc_id)
INV-254-04-blockchain-anchor 100% des tx_hash vérifiés AnchorCheck : requête registre blockchain pour chaque tx_hash C4 VerificationEngine anchor_verification_rate = 1.0000 dans rapport Disponibilité registre blockchain (H-02)
INV-254-05-lisibilite pass_rate >= readability_pass_threshold, 3 critères ReadabilityCheck : (a) ouverture I/O, (b) MIME détecté == enregistré, © décodage 1024 octets C4 VerificationEngine readability_sample_count, pass_rate, critère en échec par objet Politique de sampling non figée (Q-04)
INV-254-06-worm Aucune suppression/altération WORM avant retention expiry WormGuard : interception + événement WORM_VIOLATION_BLOCKED + arrêt campagne C8 WormGuard Événement sécurité horodaté Dépendance sur PD-44 pour enforcement WORM côté stockage
INV-254-07-rollback Plan rollback déclaratif et exécutable Vérification présence runbook en précondition F1 + mesure durée en exercice C2 StateMachine (precondition) Durée observée <= rollback_target (P95) Rollback non testable automatiquement (exercice manuel)
INV-254-08-attestation Attestation signée HSM archivée AttestationService : génération + signature HSM + archivage S3 WORM C5 AttestationService migration_attestation.json valide contre schéma §5.14 Disponibilité HSM (H-03)
INV-254-09-reproductibilite Résultat déterministe à paramètres identiques Idempotence : même entrée → mêmes artefacts logiques (verdict, écarts, hash) C6 DistributedProtection + C4 Replay retourne même verdict Seed de sampling doit être figé pour reproductibilité
INV-254-10-transitions Toute transition explicitement autorisée ou interdite StateMachine : table de transitions exhaustive, rejet explicite avec audit C2 StateMachine Audit de transition (autorisée/refusée) Spec incomplète sur R-01/R-02 (voir H-T01/H-T02)
INV-254-12-manifest-integrity Manifest protégé par hash SHA3 + signature HSM ManifestService : hash RFC 8785 + signature HSM, revalidation au postcheck C3 ManifestService manifest_hash + manifest_signature stockés et vérifiés crypto.verify(null, ...) obligatoire (learning PD-282)

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

Critère ID Mécanisme(s) Composant Observable Risque
CA-01 Rédaction protocole Markdown 3 types migration C1 Protocol Document Fichier présent + sections support/provider/région Aucun
CA-02 ManifestService.generate() conforme schéma §5.14 C3 ManifestService manifest.json valide JSON Schema, inclut protocol_version Canonicalisation JSON (résolu par RFC 8785)
CA-03 IntegrityCheck SHA3 bloquant C4 VerificationEngine Verdict KO si mismatch + doc_id fautif dans rapport Aucun
CA-04 MerkleCheck pre/post C4 VerificationEngine Rapport postcheck sans écart Merkle Aucun
CA-05 GRH tri lexicographique + SHA3-256 C4 VerificationEngine hash_global_before == hash_global_after Aucun
CA-06 AnchorCheck 100% C4 VerificationEngine anchor_verification_rate = 1.0000 Disponibilité blockchain (H-02)
CA-07 ReadabilityCheck 3 critères + seuil contractuel C4 VerificationEngine sample_count + pass_rate dans rapport Seed sampling
CA-08 Flag --dry-run, postcheck source/source C4 VerificationEngine Aucun transfert dans logs, verdict readiness Aucun
CA-09 AttestationService.generate() + sign HSM + archive C5 AttestationService Attestation valide §5.14 avec protocol_version + manifest_hash SLA HSM
CA-10 CI/CD quality gate bloquant C7 CI Pipeline Job status = failed si invariant violé Mapping exit codes (Q-06)
CA-11 Vérification runbook + mesure délai exercice C2 (precondition) Runbook présent, durée <= rollback_target Exercice manuel requis
CA-12 Lock/idempotence/reconciliation testables C6 DistributedProtection Tests concurrence/replay/orphelin passés Aucun
CA-13 Table transitions complète + codes métier distincts C2 StateMachine + C1 Document Table dans doc + implémentation exhaustive R-01 résiduel (MANIFEST_STALE)
CA-14 États terminaux sans transition sortante C2 StateMachine Rejet systématique + audit Aucun
CA-15 Dossier audit complet C1 + C3 + C4 + C5 manifest + rapports + attestation corrélés par migration_id Aucun
CA-16 Manifest hash + signature HSM vérifiés au postcheck C3 ManifestService Revalidation au début du postcheck Aucun
CA-17 Revalidation source_object_count C4 VerificationEngine Comparaison actuel vs enregistré R-02 (transition impossible, résolu par H-T02)
CA-18 Clearing conditionnel N cycles conformes C6 DistributedProtection Compteur persisté + remise à zéro Aucun

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

Test ID Référence spec Mécanisme(s) Point(s) d'observation Niveau de test visé
TC-NOM-01 CA-01 Document Markdown avec 3 sections migration Fichier présent, sections détectables par grep Unit (validation structure)
TC-NOM-02 CA-02, §5.14 ManifestService.generate() + JSON Schema validation Manifest conforme schéma, manifest_hash/manifest_signature générés Integration
TC-NOM-03 INV-01..05, INV-12, CA-03..07, CA-16 VerificationEngine.precheck() + postcheck() complet Revalidation manifest + contrôles SHA3/Merkle/GRH/ancrage/lisibilité Integration (fixture dataset pré-positionné)
TC-NOM-04 INV-08, CA-09 AttestationService.generate() + sign + archive Attestation valide §5.14, transition → ATTESTED Integration
TC-NOM-05 CA-08 VerificationEngine mode --dry-run, compare source/source Aucun transfert, verdict readiness GO/NO_GO Integration
TC-NOM-06 CA-10 Script migration-gate.sh + exit codes Job failed si invariant KO E2E (CI pipeline)
TC-NOM-07 CA-12, §5.10 DistributedProtectionService (lock/idemp/reconciliation) Lock refuse concurrent, replay même verdict, orphelin repris Integration (Redis requis)
TC-NOM-08 CA-15 Export dossier audit complet 5 artefacts corrélés par migration_id Integration
TC-ERR-01 ERR-01 Validation manifest champs obligatoires Code INVALID_MANIFEST, aucune transition Unit
TC-ERR-02 ERR-02, INV-01 IntegrityCheck avec sha3 mismatch injecté Verdict KO, transition → ROLLED_BACK, doc_id fautif Unit
TC-ERR-03 ERR-03, INV-02 MerkleCheck avec merkle_root divergent Verdict KO, rollback, journal audit Unit
TC-ERR-04 ERR-04, INV-04 AnchorCheck avec tx_hash invalide KO, rollback, taux < 100% Unit
TC-ERR-05 ERR-05, INV-05 ReadabilityCheck avec pass_rate sous seuil KO, rollback, delta seuil/observé Unit
TC-ERR-06 ERR-06, §5.10 Lock Redis SET NX Code CONCURRENT_EXECUTION_DENIED, aucune transition Integration (Redis)
TC-ERR-07 ERR-07, §5.3 Timeout verification_timeout Job échoué, réconciliation Integration
TC-ERR-08 ERR-08, INV-08 HSM indisponible au-delà SLA Code ATTESTATION_FAILED, rollback Integration (HSM mock pour timeout)
TC-ERR-09 ERR-09, INV-06 WormGuard interception suppression WORM Opération refusée, WORM_VIOLATION_BLOCKED Unit
TC-ERR-10 ERR-10, §5.13 Donnée inter-module manquante (sha3/merkle/tx_hash) KO, correction requise Unit
TC-ERR-11 ERR-11, INV-12 ManifestService.revalidate() avec hash falsifié KO immédiate, rollback Unit
TC-ERR-12 ERR-12, CA-17 SourceObjectCountCheck divergent KO, code MANIFEST_STALE, transition → ROLLED_BACK (H-T02) Integration
TC-INV-01 INV-01, CA-03 IntegrityCheck 100% objets sha3 before == after par doc_id Unit
TC-INV-02 INV-02, CA-04 MerkleCheck 100% objets merkle_root before == after Unit
TC-INV-03 INV-03, CA-05 GRH tri + SHA3-256 Deux GRH strictement égaux Unit
TC-INV-04 INV-04, CA-06 AnchorCheck N/N Taux 100%, aucun anchor_unverified Integration (mock blockchain)
TC-INV-05 INV-05, CA-07 ReadabilityCheck 3 critères pass_rate >= threshold, critère échec identifié Unit
TC-INV-06 INV-06 WormGuard + événement sécurité Rejet + WORM_VIOLATION_BLOCKED horodaté Unit
TC-INV-07 INV-07, CA-11 Precondition runbook + mesure délai Exécution complète <= rollback_target Manual/E2E
TC-INV-08 INV-08, CA-09 AttestationService roundtrip (sign + verify) Signature valide, attestation archivée, inclut protocol_version + manifest_hash Integration
TC-INV-09 INV-09 Replay idempotent Même verdict + artefacts logiques Integration
TC-INV-10A INV-10, CA-13 StateMachine toutes transitions Autorisées passent, interdites rejetées, 6 codes métier distincts Unit
TC-INV-10B INV-10, CA-14 StateMachine états terminaux Rejet systématique ATTESTED/ROLLED_BACK/RECONCILIATION_FAILED Unit
TC-INV-12 INV-12, CA-16 ManifestService.revalidate() hash + signature Hash recalculé == stocké, signature HSM valide Integration
TC-CLR-01 CA-18, §5.10 ClearingService compteur persisté Compteur incrémente/reset, clearing après N cycles Unit
TC-TOCTOU-01 CA-17, ERR-12 SourceObjectCountCheck Divergence détectée, KO + MANIFEST_STALE Unit
TC-GRH-SORT-01 INV-03, §3 GRH tri lexicographique 3 objets Ordre B, C, A vérifié Unit
TC-LISIB-01 INV-05, §3 ReadabilityCheck 5 objets (2 OK, 3 KO) pass_rate = 0.4000, critère échec identifié Unit
TC-CODES-METIER-01 §3, §5.2 StateMachine code INVALID_INPUT Code retourné, aucune transition Unit
TC-NR-01..05 Non-régression Tests existants inchangés Aucune régression sur flux archivage/CI/WORM/modules tiers Integration/E2E
TC-NEG-01..11 §5.1, §5.10 Validation format + rejection adversariale Rejets corrects pour entrées invalides Unit
TC-PRECHECK-EXP-01 §5.2, §5.4, R-04 TTL guard sur transition PRECHECK_PASSED → CUTOVER_AUTHORIZED Code PRECHECK_EXPIRED, retour DRAFT Unit (nouveau test, résout R-04)

6. Gestion des erreurs

Code métier Condition Transition Traitement Observable
INVALID_INPUT migration_id non UUID v4, protocol_version invalide Aucune Rejet requête, log validation HTTP 400 + code métier
INVALID_MANIFEST Champ obligatoire absent dans manifest Aucune Rejet precheck, aucun artefact de succès HTTP 422 + code métier + champs manquants
CONCURRENT_EXECUTION_DENIED Lock actif sur même migration_id Aucune Rejet lancement, aucun artefact créé HTTP 409 + code métier
ATTESTATION_FAILED HSM indisponible au-delà SLA attestation VERIFIED → ROLLED_BACK Rollback obligatoire, incident majeur HTTP 503 + alerte DevOps
PRECHECK_EXPIRED TTL precheck dépassé à tentative de cutover PRECHECK_PASSED → DRAFT Retour DRAFT, relance precheck complète HTTP 410 + code métier
MANIFEST_STALE source_object_count diverge au postcheck POSTCHECK_RUNNING → ROLLED_BACK (H-T02) KO immédiate, nouvelle campagne requise HTTP 409 + code métier + delta count

Traitement erreurs techniques (non métier) :

  • Timeout verification : job en échec, entrée réconciliation, pas d'attestation.
  • Erreur I/O stockage : retry 3× avec backoff exponentiel, puis RECONCILIATION_FAILED.
  • WORM violation : arrêt immédiat, événement sécurité WORM_VIOLATION_BLOCKED, campagne stoppée.

Principe fail-closed : tout catch block sur les opérations d'audit/signature DOIT propager l'erreur (learning PD-85, PD-63, PD-250). Pas de .catch(() => logger.error(...)) sur les appels HSM.


7. Impacts sécurité

Risque Mitigation Composant Observable
Altération manifest entre precheck et postcheck Hash SHA3-256 + signature HSM stockés séparément, revalidés au postcheck C3 ManifestService TC-ERR-11, TC-INV-12
Bypass WORM (suppression objet en rétention) WormGuard + événement sécurité + arrêt campagne C8 WormGuard TC-INV-06, TC-ERR-09
Exécution concurrente (race condition) Lock distribué Redis SET NX avec TTL C6 DistributedProtection TC-ERR-06
Déni de service par flood de campagnes Rate-limiting 3/h/initiateur C6 DistributedProtection TC-NEG-06
TOCTOU objets source modifiés entre precheck/postcheck Revalidation source_object_count + manifest hash C4 + C3 TC-TOCTOU-01
Attestation forgée Signature HSM avec clé dédiée, vérification crypto.verify(null, ...) C5 AttestationService TC-INV-08
Double-hash HSM (learning PD-282) crypto.verify(null, hash, key, sig) — JAMAIS createVerify() C3, C5 Tests roundtrip sign-verify
Anti-énumération Messages d'erreur uniformes (learning PD-85) C2 Aucune distinction "not found" vs "not authorized"

Journalisation : tout changement d'état, toute tentative de transition (acceptée ou refusée), tout événement sécurité est tracé dans le journal d'audit avec : acteur (initiateur), action, transition, motif, horodatage UTC RFC3339, hash de corrélation.


8. Hypothèses techniques

ID Hypothèse Impact si faux
H-01 Métadonnées SHA3/Merkle/tx_hash existent pour 100% des objets cibles Vérification impossible, campagne KO systématique
H-02 Registre blockchain accessible en lecture pendant precheck/postcheck AnchorCheck impossible, campagne KO
H-03 Clé HSM dédiée provisionnée (labels pv-migration-manifest-*, pv-migration-attestation-*) Signature/attestation non produisibles
H-04 Inventaires source/cible cohérents (pas de filtrage implicite) Faux écarts, faux positifs
H-05 Périmètre objets freeze pendant la campagne (validé par source_object_count) Comparaison non déterministe
H-06 protocol_version semver dans manifest et attestation Évolution non maîtrisée
H-T01 R-01 Gate 3 : MANIFEST_STALE traité comme 6e code métier (non listé dans §3/§5.2 de la spec). Implémenté en anticipation de la correction spec Si spec refuse ce code : remplacer par INVALID_MANIFEST avec motif étendu
H-T02 R-02 Gate 3 : POSTCHECK_RUNNING → DRAFT impossible selon §5.2. Implémenté comme POSTCHECK_RUNNING → ROLLED_BACK puis nouvelle campagne depuis DRAFT Si spec autorise → DRAFT : simplifier en transition directe
H-T03 R-03 Gate 3 : Canonicalisation JSON via RFC 8785 (JCS). Bibliothèque canonicalize (npm) Si un autre standard est choisi : adapter ManifestService
H-T04 R-05 Gate 3 : Détection expiration precheck LAZY (au moment de la tentative de cutover, pas par cron) Si détection proactive requise : ajouter scheduler BullMQ
H-T05 R-06 Gate 3 : readability_sample_count hors bornes → clamp au max (comportement spec), pas de rejet Si rejet requis : ajouter validation stricte
H-T06 Q-06 : Exit codes CI → 0=GO, 1=KO invariant, 2=erreur technique Si codification différente requise par pipeline : adapter script

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

  1. R-01/R-02 non résolus dans la spec : Les 2 bloquants résiduels de Gate 3 portent sur MANIFEST_STALE (code non défini) et la transition POSTCHECK_RUNNING → DRAFT (impossible). Le plan contourne via H-T01/H-T02 mais une correction de la spec est nécessaire avant Gate 5 pour lever l'ambiguïté.

  2. Canonicalisation JSON : Le choix RFC 8785 (JCS) est une décision technique (R-03). Si le manifest est généré par un système externe non-JCS, le hash divergera. S'assurer que tous les producteurs/consommateurs utilisent la même bibliothèque.

  3. Test TC-NOM-03 : Présuppose un dataset cible post-migration (R-08). Pour les tests automatisés, utiliser une fixture pré-positionnée identique à la source (copie en RAM ou S3 local).

  4. Dépendances inter-modules non contractualisées : Le VerificationEngine consomme des données de modules archives, Merkle, blockchain, HSM. Les interfaces de ces modules doivent être stables. Tout changement de contrat dans PD-44, PD-54, PD-55, PD-36/37 impacte PD-254.

  5. HSM label convention : Labels pv-migration-* doivent suivre la convention pv-test-* pour les tests (learning PD-63). Les clés éphémères de test ne doivent pas polluer le cluster de production.

  6. Performance GRH : Le tri lexicographique + SHA3-256 sur N merkle_root est O(N log N + N). Pour des lots > 100k objets, monitorer le temps de calcul vs verification_timeout.

  7. Clearing conditionnel : Le compteur clearing_counter est persisté en DB, pas en Redis. Un crash Redis ne doit pas réinitialiser le compteur.


10. Hors périmètre

  • Migration effective des données : orchestration de copie, bascule réseau, opérations runbook de production. Le protocole PD-254 vérifie l'intégrité, il ne déplace pas les données.
  • Changement de format probatoire : couvert par PD-245.
  • Migration de schéma base de données : TypeORM/migrations DDL existantes.
  • Réconciliation de litiges juridiques : ex-post migration, hors périmètre opérationnel.
  • Double signature attestation : Q-08 non résolu. Signature HSM unique implémentée. Co-signature à ajouter si requise.
  • Canal de notification : Q-05 non résolu. Abstraction NotificationPort implémentée, canal concret à configurer.
  • Localisation archivage attestation : Q-07 non résolu. Bucket/prefix paramétrable.
  • Politique de sampling : Q-04 non résolu. Tirage uniforme implémenté par défaut, stratifié possible via configuration.

11. Périmètre de test

Niveau de test In scope Hors scope (justification)
Unitaire StateMachine (transitions, codes métier), VerificationEngine (SHA3, Merkle, GRH, lisibilité), ManifestService (génération, hash, revalidation), WormGuard, ClearingService, validations format §5.1
Intégration ManifestService + HSM (signature/vérification roundtrip), DistributedProtection + Redis (lock/idempotence/reconciliation), VerificationEngine + fixture dataset, AttestationService + HSM + archivage S3
E2E Pipeline CI quality gate (script migration-gate.sh), flux complet DRAFT → ATTESTED sur fixture Migration effective de données (hors périmètre PD-254)
Manuel Exercice rollback (INV-254-07, TC-INV-07) — mesure durée

Tous les niveaux de test sont couverts, aucune exclusion. Couverture minimale attendue : 80% sur le périmètre in scope.


12. Mécanismes cross-module

Aucune modification de routes d'autres modules. PD-254 est un composant protocolaire autonome (offline/batch). Les données inter-modules (archives, Merkle, blockchain, HSM) sont consommées en lecture seule. Le WormGuard (C8) est enregistré dans le MigrationModule, pas en global.

Aucune modification d'autres modules.


13. Constats Gate 3 résiduels — Traitement

Constat Gravité Traitement plan Hypothèse
R-01 MANIFEST_STALE non défini Bloquant Implémenté comme 6e code métier H-T01
R-02 POSTCHECK_RUNNING → DRAFT impossible Bloquant Transition via ROLLED_BACK puis nouvelle campagne H-T02
R-03 Canonicalisation JSON non spécifiée Majeur RFC 8785 (JCS) choisi H-T03
R-04 Aucun test pour PRECHECK_EXPIRED Majeur TC-PRECHECK-EXP-01 ajouté
R-05 Mécanisme détection PRECHECK_EXPIRED non spécifié Majeur Vérification LAZY au moment de la tentative de cutover H-T04
R-06 Clamp vs rejet readability_sample_count Mineur Clamp au max (comportement spec) H-T05
R-07 Transitions retour CUTOVER_AUTHORIZED sans test Mineur Couvert par TC-INV-10A (matrice exhaustive)
R-08 TC-NOM-03 présuppose dataset cible Mineur Fixture pré-positionnée en test