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_STALEest traité comme un code métier complémentaire (6e code). Pour R-02, la transitionPOSTCHECK_RUNNING → DRAFTest impossible selon §5.2 ; le plan implémentePOSTCHECK_RUNNING → ROLLED_BACKpuis nouvelle campagne depuisDRAFT. 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_signaturevia HSM (CloudHSM,CKM_ECDSAavec clé dédiée, labelpv-migration-manifest-*). - Stockage de
manifest_hash+manifest_signaturedans un registre indépendant (tablemanifest_registry). - Revalidation au postcheck : recalcul hash + vérification signature HSM (
crypto.verify(null, hash, key, sig)— pascreateVerify, 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 :
- IntegrityCheck :
sha3_before == sha3_afterpour chaque objet (INV-254-01). - MerkleCheck :
merkle_root_before == merkle_root_afterobjet par objet (INV-254-02). - GlobalRootHash : tri lexicographique hex lowercase des
merkle_root, concaténation, SHA3-256 (INV-254-03). - AnchorCheck : vérification de chaque
tx_hashdans le registre blockchain (INV-254-04). - 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).
- SourceObjectCountCheck : revalidation
source_object_countau postcheck (CA-17, protection TOCTOU). - ManifestIntegrityCheck : revalidation
manifest_hash+manifest_signatureau 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 :
- Lock distribué : clé Redis
migration:lock:{migration_id}, TTL configurable (60-3600s, défaut 600s). SET NX. - Idempotence : clé
migration:idemp:{migration_id}:{phase}, TTLidempotency_ttl(1-720h). Stocke verdict + hash artefacts logiques. - Reconciliation : cron BullMQ
reconciliation_interval(1-60min). Détection orphelins (last_activity > orphan_threshold). Reprise idempotente ouRECONCILIATION_FAILED. - Rate-limiting : clé Redis
migration:rate:{initiator}, fenêtre glissante 1h, max 3 lancements. - Clearing conditionnel : compteur
clearing_counterpersisté 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-runou 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_expiryavant toute opération destructive. - Émission événement sécurité
WORM_VIOLATION_BLOCKEDsi 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)¶
-
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 transitionPOSTCHECK_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é. -
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.
-
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).
-
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.
-
HSM label convention : Labels
pv-migration-*doivent suivre la conventionpv-test-*pour les tests (learning PD-63). Les clés éphémères de test ne doivent pas polluer le cluster de production. -
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. -
Clearing conditionnel : Le compteur
clearing_counterest 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
NotificationPortimplé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 | — |