Aller au contenu

PD-254 — Protocole formel de migration probatoire verifiable

1. Objectif

La User Story PD-254 doit definir un protocole contractuel, auditable et reproductible pour toute migration probatoire de stockage (support, provider, region), afin de garantir simultanement :

  • l'integrite cryptographique des objets probatoires (SHA-3, Merkle, ancrage blockchain),
  • la lisibilite post-migration,
  • la tracabilite complete via manifest pre-migration, rapport de verification et attestation signee HSM,
  • la conformite demonstrable a NF Z42-013:2020 §12.1.

Le protocole couvre la verification et la preuve de conformite de migration.
L'execution technique de migration de donnees n'est pas incluse dans cette story.


2. Perimetre / Hors perimetre

Inclus

  • Definition d'un protocole pas-a-pas pour 3 types de migration : support, provider, region.
  • Generation d'un manifest.json pre-migration (golden dataset), protege par hash SHA-3 et signature HSM.
  • Verification pre/post migration :
  • inventaire source/cible,
  • SHA-3 objet,
  • coherence racines Merkle,
  • coherence ancrages blockchain,
  • lisibilite sur echantillon aleatoire.
  • Calcul et comparaison du Global Root Hash avant/apres.
  • Generation d'un rapport differentiel JSON exploitable CI.
  • Mode --dry-run (verification sans migration).
  • Generation d'une attestation migration_attestation.json signee HSM.
  • Integration CI/CD avec quality gate bloquant et notification DevOps.

Exclu

  • Migration effective des donnees (orchestration de copie, bascule reseau, operations runbook de production).
  • Changement de format probatoire (couvert par PD-245).
  • Migration de schema base de donnees (TypeORM/migrations DDL existantes).
  • Reconciliation de litiges juridiques ex-post migration (hors perimetre operationnel de la story).

3. Definitions

  • Migration probatoire : transfert d'objets et metadonnees de preuve sans alteration de chaine de preuve.
  • Manifest pre-migration : jeu de reference immuable des objets a migrer, protege par un hash SHA-3 (manifest_hash) et une signature HSM (manifest_signature).
  • Global Root Hash (GRH) : SHA3-256(concatenation triee des merkle_root). L'ordre de tri est lexicographique sur la chaine hex lowercase des merkle_root (comparaison caractere par caractere, code ASCII croissant). Exemple : 0a1b... < 0a2c... < ff00....
  • Precheck : controles avant migration (inventaire, hash, Merkle, ancrage, lisibilite).
  • Postcheck : memes controles apres migration + comparaison pre/post + revalidation du manifest.
  • Attestation de migration : preuve probatoire signee HSM des resultats.
  • Dry-run : execution complete des verifications sans action de migration. En mode F3 (postcheck), le dry-run compare le dataset source avec lui-meme (source/source) pour valider la chaine de verification sans migration effective.
  • Etat terminal : etat sans transition sortante autorisee.
  • KO : echec bloquant interdisant la bascule.
  • Lisibilite : un objet est considere « lisible » si les trois criteres suivants sont satisfaits simultanement : (a) le fichier s'ouvre sans erreur I/O, (b) le type MIME detecte correspond au type MIME enregistre dans les metadonnees, © les 1024 premiers octets sont decodables sans erreur selon l'encodage attendu.
  • Artefacts logiques : ensemble constitue du verdict (GO/KO), de la liste des ecarts detectes, et du hash des objets verifies. Sont exclus des artefacts logiques : timestamps d'execution, identifiants de job, et metadonnees d'execution.
  • Initiateur : identifiant du service account ou de l'utilisateur authentifie qui declenche la campagne de migration. L'initiateur est trace dans le journal d'audit et soumis au rate-limiting.
  • Codes d'erreur metier : codes de rejet retournes par le protocole lorsqu'une precondition ou validation echoue. Ces codes (INVALID_INPUT, INVALID_MANIFEST, CONCURRENT_EXECUTION_DENIED, ATTESTATION_FAILED, PRECHECK_EXPIRED) ne sont pas des etats de la machine d'etats formelle (§5.2) mais des codes de reponse metier qui empechent ou provoquent une transition. Voir §6 pour le detail de chaque code.

4. Invariants (non negociables)

ID Regle Justification
INV-254-01-integrite-objet Pour chaque doc_id, sha3_before == sha3_after obligatoire. Toute divergence rend la migration KO. NF Z42-013 §12.1
INV-254-02-merkle Pour chaque objet, merkle_root_before == merkle_root_after obligatoire. Integrite chaine de preuve
INV-254-03-global-root-hash hash_global_before == hash_global_after obligatoire. Le GRH est calcule par tri lexicographique des merkle_root hex lowercase puis concatenation et SHA3-256. Verification macroscopique N objets
INV-254-04-blockchain-anchor Chaque tx_hash d'ancrage reference est present et coherent dans le registre cible. ISO 14641 / non-alteration preuve
INV-254-05-lisibilite Taux de succes lisibilite echantillon >= seuil contractuel (voir §5.3). La lisibilite est evaluee selon les 3 criteres definis en §3. NF Z42-013 §12.1 (lisibilite)
INV-254-06-worm Aucune suppression/alteration d'objet verrouille WORM avant retention expiry. Contrainte WORM PD-44
INV-254-07-rollback Un plan de rollback declaratif et executable doit exister avant bascule. Securite operationnelle
INV-254-08-attestation Toute migration validee produit une attestation signee HSM archivee. Preuve audit
INV-254-09-reproductibilite A parametres identiques et dataset identique, le resultat de verification est deterministe. Les artefacts logiques (verdict, ecarts, hash objets) sont identiques. Fiabilite CI/CD
INV-254-10-transitions Toute transition d'etat doit etre explicitement autorisee ou interdite (aucune implicite). Robustesse machine a etats
INV-254-12-manifest-integrity Le manifest est protege par un hash SHA-3 (manifest_hash) et une signature HSM (manifest_signature). Toute alteration du manifest est detectee et rend la campagne KO. Integrite du jeu de reference

Note INV-254-11 : L'invariant INV-254-11 (envelope encryption) initialement envisage a ete retire du perimetre de PD-254. Aucun DEK, fragment, cle de session ou material de rekey n'est genere par ce protocole. Le chiffrement au repos des objets probatoires est couvert par les stories existantes (PD-44, PD-4). La numerotation saute de 10 a 12 pour eviter toute confusion avec d'anciennes references.


5. Flux nominaux

5.1 Modele de donnees contractuel (formats et validations)

Donnee Format / Encodage Taille Jeu caracteres / Case Regex Comportement si invalide
migration_id UUID v4 texte 36 caracteres ASCII, case-insensitive en entree, normalise lowercase ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$ Rejet de la campagne (code d'erreur metier INVALID_INPUT, aucune transition d'etat)
doc_id UUID v4 texte 36 ASCII, case-insensitive en entree, normalise lowercase idem migration_id Objet marque INVALID_METADATA, migration KO si >0
sha3 hex SHA3-256 64 [a-f0-9], case-sensitive (lowercase impose) ^[a-f0-9]{64}$ Objet KO, migration KO
merkle_root hex SHA3-256 64 [a-f0-9], case-sensitive ^[a-f0-9]{64}$ Objet KO, migration KO
tx_hash hash blockchain hex prefixe 66 [a-f0-9x], case-sensitive (lowercase impose) ^0x[a-f0-9]{64}$ Objet KO, migration KO
s3_object_key UTF-8 path key 1..1024 UTF-8, case-sensitive hors regex globale (validation longueur + UTF-8 valide) Objet exclu du lot, migration KO
size entier non signe bytes 1..5,497,558,138,880 numerique ^[0-9]{1,13}$ Objet KO, migration KO
timestamp RFC3339 UTC 20..35 ASCII, case-sensitive (Z) ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,6})?Z$ Objet KO, migration KO
hash_global_before/after hex SHA3-256 64 [a-f0-9], case-sensitive ^[a-f0-9]{64}$ Verification globale KO
signature_hsm base64 standard 88..684 [A-Za-z0-9+/=], case-sensitive ^[A-Za-z0-9+/=]+$ Attestation invalide, code d'erreur metier ATTESTATION_FAILED
readability_sample_pass_rate decimal string 0.0000..1.0000 numerique + . ^(0(\.\d{1,4})?|1(\.0{1,4})?)$ Rapport KO
manifest_hash hex SHA3-256 64 [a-f0-9], case-sensitive ^[a-f0-9]{64}$ Campagne KO, rejet cutover ou postcheck
manifest_signature base64 standard 88..684 [A-Za-z0-9+/=], case-sensitive ^[A-Za-z0-9+/=]+$ Campagne KO, manifest considere non fiable
protocol_version semver 5..14 ASCII ^\d+\.\d+\.\d+$ Rejet campagne (incompatibilite protocole)
source_object_count entier non signe 1..999999999 numerique ^[0-9]{1,9}$ Revalidation manifest echouee

Ordre de tri du Global Root Hash : les merkle_root sont tries par ordre lexicographique croissant sur leur representation hex lowercase (64 caracteres). La concatenation des chaines triees est ensuite hashee par SHA3-256 pour produire le GRH.

5.2 Etats et transitions (avec transitions retour explicites)

Etats formels :
DRAFT, PRECHECK_RUNNING, PRECHECK_PASSED, PRECHECK_FAILED, CUTOVER_AUTHORIZED, POSTCHECK_RUNNING, VERIFIED, ATTESTED, ROLLED_BACK, RECONCILIATION_FAILED.

Codes d'erreur metier (distincts des etats formels, voir §3) :
INVALID_INPUT, INVALID_MANIFEST, CONCURRENT_EXECUTION_DENIED, ATTESTATION_FAILED, PRECHECK_EXPIRED.

  • DRAFT
  • DRAFT -> PRECHECK_RUNNING : AUTORISEE (preconditions satisfaites)
  • DRAFT -> * autre : INTERDITE (ordre de processus)
  • PRECHECK_RUNNING
  • -> PRECHECK_PASSED : AUTORISEE (tous controles OK)
  • -> PRECHECK_FAILED : AUTORISEE (au moins un controle KO)
  • -> DRAFT : INTERDITE (etat execution)
  • PRECHECK_PASSED
  • -> CUTOVER_AUTHORIZED : AUTORISEE (si precheck non expire, TTL <= 168h)
  • -> PRECHECK_RUNNING : AUTORISEE (revalidation explicite)
  • -> DRAFT : AUTORISEE (si precheck expire au-dela du TTL, code metier PRECHECK_EXPIRED emis avant retour)
  • PRECHECK_FAILED
  • -> PRECHECK_RUNNING : AUTORISEE (correction puis relance)
  • -> CUTOVER_AUTHORIZED : INTERDITE (gate integrite non valide)
  • CUTOVER_AUTHORIZED
  • -> POSTCHECK_RUNNING : AUTORISEE (migration executee puis controle)
  • -> PRECHECK_PASSED : AUTORISEE (retour arriere avant execution, conserve le manifest initial, aucun ecrasement)
  • -> ROLLED_BACK : AUTORISEE (annulation operationnelle)
  • POSTCHECK_RUNNING
  • -> VERIFIED : AUTORISEE (tous controles OK)
  • -> ROLLED_BACK : AUTORISEE (echec postcheck)
  • -> RECONCILIATION_FAILED : AUTORISEE (timeouts/retries epuises)
  • VERIFIED
  • -> ATTESTED : AUTORISEE (attestation generee et signee dans SLA)
  • -> ROLLED_BACK : AUTORISEE (si attestation impossible dans SLA)
  • ATTESTED (terminal succes)
  • -> * : INTERDITE (etat terminal, resolution manuelle uniquement)
  • Transition retour ATTESTED -> VERIFIED : INTERDITE (attestation immuable)
  • ROLLED_BACK (terminal echec maitrise)
  • -> * : INTERDITE (etat terminal, resolution manuelle uniquement)
  • Retour VERIFIED -> ROLLED_BACK conserve toutes preuves d'echec, interdit purge
  • RECONCILIATION_FAILED (terminal echec technique)
  • -> * : INTERDITE (etat terminal, resolution manuelle uniquement)

Regles generales :

  • Quotas/limites de securite sont reappliques immediatement lors de tout retour non terminal.
  • Les codes d'erreur metier (INVALID_INPUT, INVALID_MANIFEST, CONCURRENT_EXECUTION_DENIED) n'entrainent aucune transition d'etat : la campagne reste dans son etat courant et le code est retourne a l'appelant.
  • ATTESTATION_FAILED est emis quand la signature HSM echoue ; la campagne en VERIFIED est alors transitionee vers ROLLED_BACK.
  • PRECHECK_EXPIRED est emis quand le TTL du precheck est depasse ; la campagne en PRECHECK_PASSED est retournee a DRAFT.

5.3 Bornes numeriques contractuelles

Parametre Defaut Min Max Unite Contexte Percentile Hors bornes
precheck_ttl 72 1 168 heures campagne migration n/a Rejet autorisation cutover, code metier PRECHECK_EXPIRED, retour DRAFT
cutover_window 2 0.25 24 heures fenetre bascule n/a Rejet planification
rollback_target 4 0.5 8 heures runbook rollback P95 KO readiness gate
readability_sample_count 1000 100 10000 objets lot migration P95 temps traitement Clamp au max + trace
readability_pass_threshold 1.0000 0.9900 1.0000 ratio lot migration n/a Migration KO
verification_timeout 7200 300 14400 secondes job CI P95 Echec job
lock_ttl 600 60 3600 secondes lock distribue campagne n/a Rejet execution
idempotency_ttl 168 1 720 heures dedup jobs n/a Rejet demarrage
reconciliation_interval 5 1 60 minutes cron reconciliation n/a Rejet config
orphan_threshold 15 5 180 minutes detection jobs orphelins n/a Rejet config
notify_sla 5 1 30 minutes alerte DevOps P95 Incident observabilite
clearing_cycles 3 2 10 cycles clearing conditionnel n/a Clearing refuse

5.4 SLA temporels (transitions a deadline)

Transition / SLA Defaut Min Max Configurabilite Comportement expiration
Precheck valide avant cutover (PRECHECK_PASSED valide) 72h 1h 7j Parametre pipeline Code metier PRECHECK_EXPIRED, retour DRAFT obligatoire
Emission attestation apres VERIFIED 15min 1min 4h Parametre service attestation Code metier ATTESTATION_FAILED, rollback obligatoire
Delai max resolution rollback 4h 30min 8h Runbook Incident majeur + etat terminal ROLLED_BACK

5.5 Flux F1 — Preparation

  1. Initialiser campagne migration_id en etat DRAFT avec protocol_version (ex: 1.0.0).
  2. Verifier preconditions : acces source/cible, acces registre ancrage, acces HSM, runbook rollback present.
  3. Verifier que la campagne n'est pas dupliquee (idempotence sur migration_id).

Resultat attendu : passage a PRECHECK_RUNNING.

5.6 Flux F2 — Precheck et manifest de reference

  1. Generer manifest.json avec chaque objet (doc_id, sha3, merkle_root, s3_object_key, size, timestamp, tx_hash) et le champ protocol_version.
  2. Calculer manifest_hash = SHA3-256 du contenu serialise du manifest (JSON canonique, cles triees).
  3. Signer le manifest via HSM pour produire manifest_signature.
  4. Stocker manifest_hash et manifest_signature dans un registre independant du manifest lui-meme.
  5. Enregistrer source_object_count = nombre total d'objets dans le manifest.
  6. Calculer hash_global_before (GRH, voir §3 pour l'ordre de tri).
  7. Executer controles precheck (integrite, Merkle, ancrage, lisibilite echantillon selon les 3 criteres de §3).
  8. Produire rapport precheck JSON.

Resultat attendu :

  • si 100% invariants satisfaits -> PRECHECK_PASSED,
  • sinon -> PRECHECK_FAILED.

5.7 Flux F3 — Postcheck apres migration

  1. Charger manifest de reference immuable.
  2. Revalidation du manifest : recalculer SHA3-256 du manifest charge et comparer avec manifest_hash stocke. Verifier manifest_signature via HSM. Si divergence -> campagne KO immediate.
  3. Revalidation du nombre d'objets source : verifier que le nombre d'objets actuellement en source correspond a source_object_count enregistre au precheck. Si divergence (ajout ou suppression d'objets entre precheck et postcheck) -> campagne KO, code metier MANIFEST_STALE.
  4. Relever dataset cible post-migration.
  5. Calculer hash_global_after (meme algorithme de tri que §3).
  6. Comparer pre/post :
  7. objet par objet (sha3, merkle_root, size, presence),
  8. verification ancrages blockchain,
  9. lisibilite echantillon post-migration (3 criteres de §3).
  10. Produire rapport differentiel JSON.

Resultat attendu :

  • si invariants OK -> VERIFIED,
  • sinon -> ROLLED_BACK (ou RECONCILIATION_FAILED si recuperation impossible).

5.8 Flux F4 — Attestation probatoire

  1. Generer migration_attestation.json avec les champs contractuels, incluant protocol_version.
  2. Signer attestation via HSM.
  3. Archiver attestation comme objet probatoire.
  4. Notifier DevOps/TechLead/PO du verdict.

Resultat attendu : ATTESTED (terminal succes).

5.9 Flux F5 — Dry-run

  1. Executer F1, F2 et simulation F3 sans aucune operation de migration.
  2. En mode dry-run, le postcheck (F3) compare le dataset source avec lui-meme (source/source) pour valider que la chaine de verification fonctionne correctement, sans verifier une migration effective.
  3. Interdire toute transition vers execution effective.
  4. Produire rapport dry-run avec code retour CI explicite.

Resultat attendu : readiness GO ou NO_GO sans modification de donnees.

5.10 Mecanismes de protection distribuee

  • Lock distribue
  • Scope : par migration_id.
  • Cle : migration:lock:{migration_id}.
  • TTL : voir lock_ttl.
  • Si lock non acquis : execution refusee, code d'erreur metier CONCURRENT_EXECUTION_DENIED (aucune transition d'etat).
  • Idempotence
  • Cle : migration_id + phase (precheck, postcheck, attestation).
  • Duree dedup : idempotency_ttl.
  • Replay : doit retourner le meme verdict et les memes artefacts logiques (verdict, ecarts, hash objets — hors timestamps et metadonnees d'execution).
  • Reconciliation
  • Cron : reconciliation_interval.
  • Orphelin : campagne non terminale sans activite > orphan_threshold.
  • Strategie : reprise de phase idempotente, sinon RECONCILIATION_FAILED.
  • Rate-limiting
  • Granularite : par migration_id et par initiateur (voir §3).
  • Quota : max 3 lancements/heure/initiateur.
  • Depassement : rejet explicite.
  • Clearing conditionnel
  • Retour a statut operationnel normal seulement apres clearing_cycles (defaut 3) cycles de reconciliation conformes consecutifs.
  • Compteur persiste par migration_id.
  • Un seul cycle non conforme remet le compteur a zero.

5.11 Atomicite multi-composant

Aucune ecriture transactionnelle DB suivie d'un append-only journal/queue n'est definie dans le perimetre de PD-254.
Aucune exigence d'atomicite multi-composant additionnelle applicable dans cette story.

5.12 Strategie migration DDL

La story exclut explicitement toute migration de schema DB.
Aucune strategie de migration DDL applicable dans PD-254.

5.13 Contraintes inter-modules

Le protocole depend de donnees issues des modules archives, preuves Merkle, ancrage blockchain et attestation HSM.

  • Donnees inter-modules requises :
  • identite objet (doc_id, s3_object_key, size, timestamp),
  • empreintes (sha3, merkle_root),
  • ancrages (tx_hash),
  • signature (signature_hsm).
  • Regle : absence d'une donnee obligatoire d'un module tiers => campagne KO.
  • Aucun guard de route cross-module n'est dans le perimetre de cette story (composant protocolaire/offline).

5.14 Schemas JSON contractuels

Schema du manifest (manifest.json)

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "PD-254 Migration Manifest",
  "type": "object",
  "required": ["migration_id", "protocol_version", "created_at", "source_object_count", "objects", "hash_global_before"],
  "properties": {
    "migration_id": {
      "type": "string",
      "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
    },
    "protocol_version": {
      "type": "string",
      "pattern": "^\\d+\\.\\d+\\.\\d+$"
    },
    "created_at": {
      "type": "string",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,6})?Z$"
    },
    "source_object_count": {
      "type": "integer",
      "minimum": 1
    },
    "hash_global_before": {
      "type": "string",
      "pattern": "^[a-f0-9]{64}$"
    },
    "objects": {
      "type": "array",
      "minItems": 1,
      "items": {
        "type": "object",
        "required": ["doc_id", "sha3", "merkle_root", "s3_object_key", "size", "timestamp", "tx_hash"],
        "properties": {
          "doc_id": { "type": "string", "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" },
          "sha3": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
          "merkle_root": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
          "s3_object_key": { "type": "string", "minLength": 1, "maxLength": 1024 },
          "size": { "type": "integer", "minimum": 1, "maximum": 5497558138880 },
          "timestamp": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,6})?Z$" },
          "tx_hash": { "type": "string", "pattern": "^0x[a-f0-9]{64}$" }
        },
        "additionalProperties": false
      }
    }
  },
  "additionalProperties": false
}

Schema de l'attestation (migration_attestation.json)

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "PD-254 Migration Attestation",
  "type": "object",
  "required": ["migration_id", "protocol_version", "attested_at", "hash_global_before", "hash_global_after", "total_objects", "readability_sample_count", "readability_pass_rate", "anchor_verification_rate", "manifest_hash", "verdict", "signature_hsm"],
  "properties": {
    "migration_id": { "type": "string", "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" },
    "protocol_version": { "type": "string", "pattern": "^\\d+\\.\\d+\\.\\d+$" },
    "attested_at": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,6})?Z$" },
    "hash_global_before": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
    "hash_global_after": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
    "total_objects": { "type": "integer", "minimum": 1 },
    "readability_sample_count": { "type": "integer", "minimum": 100, "maximum": 10000 },
    "readability_pass_rate": { "type": "string", "pattern": "^(0(\\.\\d{1,4})?|1(\\.0{1,4})?)$" },
    "anchor_verification_rate": { "type": "string", "pattern": "^1\\.0000$" },
    "manifest_hash": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
    "verdict": { "type": "string", "enum": ["GO", "KO"] },
    "signature_hsm": { "type": "string", "pattern": "^[A-Za-z0-9+/=]+$", "minLength": 88, "maxLength": 684 }
  },
  "additionalProperties": false
}

5bis. Diagrammes

5bis.1 Machine d'etats (state diagram)

stateDiagram-v2
    [*] --> DRAFT

    DRAFT --> PRECHECK_RUNNING : Preconditions OK

    PRECHECK_RUNNING --> PRECHECK_PASSED : Tous controles OK
    PRECHECK_RUNNING --> PRECHECK_FAILED : >= 1 controle KO

    PRECHECK_PASSED --> CUTOVER_AUTHORIZED : TTL precheck valide (<= 168h)
    PRECHECK_PASSED --> PRECHECK_RUNNING : Revalidation explicite
    PRECHECK_PASSED --> DRAFT : TTL expire (PRECHECK_EXPIRED)

    PRECHECK_FAILED --> PRECHECK_RUNNING : Correction puis relance

    CUTOVER_AUTHORIZED --> POSTCHECK_RUNNING : Migration executee
    CUTOVER_AUTHORIZED --> PRECHECK_PASSED : Retour avant execution
    CUTOVER_AUTHORIZED --> ROLLED_BACK : Annulation operationnelle

    POSTCHECK_RUNNING --> VERIFIED : Tous controles OK
    POSTCHECK_RUNNING --> ROLLED_BACK : Echec postcheck
    POSTCHECK_RUNNING --> RECONCILIATION_FAILED : Timeouts / retries epuises

    VERIFIED --> ATTESTED : Attestation signee HSM dans SLA
    VERIFIED --> ROLLED_BACK : Attestation impossible (ATTESTATION_FAILED)

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

    note right of ATTESTED : Terminal succes\nAucune transition sortante
    note right of ROLLED_BACK : Terminal echec maitrise\nPreuves conservees
    note right of RECONCILIATION_FAILED : Terminal echec technique\nResolution manuelle

Transitions interdites notables (INV-254-10) :

  • PRECHECK_RUNNING -> DRAFT (etat d'execution, pas de retour)
  • PRECHECK_FAILED -> CUTOVER_AUTHORIZED (gate integrite non valide)
  • ATTESTED -> * (terminal, immuable)
  • ROLLED_BACK -> * (terminal)
  • RECONCILIATION_FAILED -> * (terminal)

Codes d'erreur metier (distincts des etats, aucune transition d'etat) :

  • INVALID_INPUT : rejet validation format, campagne reste dans son etat courant
  • INVALID_MANIFEST : manifest incomplet, campagne reste dans son etat courant
  • CONCURRENT_EXECUTION_DENIED : lock actif, campagne reste dans son etat courant

5bis.2 Flux nominal multi-service (sequence diagram)

sequenceDiagram
    participant CI as Pipeline CI/CD
    participant PS as ProtocolService
    participant S3src as S3 Source
    participant S3dst as S3 Cible
    participant BC as Blockchain Registry
    participant HSM as HSM (PKCS#11)
    participant DB as PostgreSQL

    rect rgb(230, 245, 255)
        note over CI,DB: F1 — Preparation (DRAFT)
        CI->>PS: initCampaign(migration_id, protocol_version)
        PS->>DB: INSERT campaign (DRAFT)
        PS->>S3src: Verify access
        PS->>S3dst: Verify access
        PS->>BC: Verify access
        PS->>HSM: Verify access
        PS-->>CI: DRAFT confirmed
    end

    rect rgb(230, 255, 230)
        note over CI,DB: F2 — Precheck & Manifest (DRAFT -> PRECHECK_RUNNING -> PRECHECK_PASSED)
        CI->>PS: runPrecheck(migration_id)
        PS->>DB: UPDATE state = PRECHECK_RUNNING
        PS->>S3src: List objects (inventory)
        S3src-->>PS: objects[]

        loop Pour chaque objet
            PS->>S3src: GET object
            S3src-->>PS: object data
            note right of PS: Calcul SHA3-256(object)
        end

        PS->>BC: Verify tx_hash pour chaque objet
        BC-->>PS: anchor confirmations

        note right of PS: Echantillon lisibilite :<br/>I/O + MIME + decodage 1024 octets

        note right of PS: Tri lexicographique merkle_root hex lowercase<br/>Concatenation -> SHA3-256 = GRH (hash_global_before)

        PS->>PS: Generer manifest.json (JSON canonique)
        note right of PS: manifest_hash = SHA3-256(manifest)

        PS->>HSM: Sign(manifest_hash)
        HSM-->>PS: manifest_signature

        PS->>DB: Store manifest_hash, manifest_signature, source_object_count
        PS->>DB: UPDATE state = PRECHECK_PASSED
        PS-->>CI: Precheck report JSON
    end

    rect rgb(255, 245, 230)
        note over CI,DB: F3 — Postcheck (CUTOVER_AUTHORIZED -> POSTCHECK_RUNNING -> VERIFIED)
        CI->>PS: runPostcheck(migration_id)
        PS->>DB: UPDATE state = POSTCHECK_RUNNING

        PS->>DB: Load manifest_hash, manifest_signature
        PS->>PS: Recalcul SHA3-256(manifest)
        note right of PS: Comparaison manifest_hash<br/>(INV-254-12)
        PS->>HSM: Verify(manifest_signature)
        HSM-->>PS: signature OK

        PS->>S3src: Count objects
        note right of PS: Comparaison source_object_count<br/>(ERR-12 MANIFEST_STALE)

        PS->>S3dst: List objects (inventory cible)
        S3dst-->>PS: objects[]

        loop Pour chaque objet
            PS->>S3dst: GET object
            S3dst-->>PS: object data
            note right of PS: sha3_after = SHA3-256(object)<br/>Compare sha3_before (INV-254-01)
        end

        note right of PS: merkle_root_before == merkle_root_after (INV-254-02)

        PS->>BC: Verify tx_hash cible
        BC-->>PS: anchor OK (INV-254-04)

        note right of PS: Lisibilite echantillon post (INV-254-05)

        note right of PS: hash_global_after = GRH cible<br/>Compare hash_global_before (INV-254-03)

        PS->>DB: UPDATE state = VERIFIED
        PS-->>CI: Differential report JSON
    end

    rect rgb(245, 230, 255)
        note over CI,DB: F4 — Attestation (VERIFIED -> ATTESTED)
        CI->>PS: generateAttestation(migration_id)
        PS->>PS: Build migration_attestation.json
        PS->>HSM: Sign(attestation)
        HSM-->>PS: signature_hsm

        PS->>S3dst: Archive attestation (objet probatoire)
        PS->>DB: UPDATE state = ATTESTED
        PS-->>CI: ATTESTED — attestation archivee
        CI->>CI: Notify DevOps/TechLead/PO
    end

6. Cas d'erreur

ID Cas Detection Reponse attendue
ERR-01 Manifest incomplet Champ obligatoire absent Echec immediate, code metier INVALID_MANIFEST, pas de cutover, aucune transition d'etat
ERR-02 Hash objet mismatch sha3_before != sha3_after KO migration, rollback obligatoire
ERR-03 Merkle mismatch merkle_root_before != merkle_root_after KO migration, rollback obligatoire
ERR-04 Ancrage blockchain incoherent tx_hash introuvable/invalide KO migration, rollback obligatoire
ERR-05 Lisibilite sous seuil pass_rate < readability_pass_threshold KO migration, rollback obligatoire
ERR-06 Lock non acquis lock actif existant Rejet lancement, code metier CONCURRENT_EXECUTION_DENIED, aucune transition d'etat
ERR-07 Time-out verification depassement verification_timeout Echec job, entree reconciliation
ERR-08 Signature HSM indisponible/invalide signature absente ou regex invalide Code metier ATTESTATION_FAILED, rollback ou incident majeur
ERR-09 WORM violation detectee tentative suppression objet retenu Arret immediate, incident securite, evenement WORM_VIOLATION_BLOCKED emis
ERR-10 Donnee inter-module manquante reference orpheline KO migration, correction prealable requise
ERR-11 Manifest altere manifest_hash recalcule != manifest_hash stocke ou manifest_signature invalide Campagne KO immediate, rollback obligatoire
ERR-12 Manifest perime (TOCTOU) source_object_count actuel != source_object_count enregistre Campagne KO, code metier MANIFEST_STALE, retour DRAFT

7. Criteres d'acceptation (testables)

ID Critere Observable
CA-01 Le protocole de migration est publie en Markdown avec les 3 types de migration Fichier present + sections obligatoires
CA-02 Un manifest pre-migration est genere avec schema contractuel complet, incluant protocol_version manifest.json valide contre schema §5.14
CA-03 Le precheck compare SHA-3 objet avec reference et bloque a la moindre divergence Rapport precheck + verdict KO si mismatch
CA-04 Le postcheck valide egalite des merkle_root pre/post Rapport postcheck sans ecart
CA-05 Le Global Root Hash est calcule par tri lexicographique hex lowercase des merkle_root avant/apres et doit etre identique hash_global_before == hash_global_after
CA-06 Les ancrages blockchain sont verifies pour 100% des objets du lot Rapport inclut taux verification = 100%
CA-07 Le controle de lisibilite applique les 3 criteres (I/O, MIME, decodage 1024 octets) sur readability_sample_count objets avec seuil contractuel Rapport inclut sample_count et pass_rate
CA-08 Le mode --dry-run execute les controles en comparant source/source sans migration effective Logs/rapport sans operation de migration
CA-09 Une attestation signee HSM est generee apres verification reussie, incluant protocol_version et manifest_hash migration_attestation.json signe, valide contre schema §5.14 et archive
CA-10 Le pipeline CI bloque la campagne sur tout invariant KO Job status = failed si invariant viole
CA-11 Le protocole inclut un rollback cible <= rollback_target Runbook + mesure de delai en exercice
CA-12 Les mecanismes lock/idempotence/reconciliation sont actifs et testables Tests de concurrence/replay/orphelin passes
CA-13 Toutes transitions d'etat autorisees/interdites sont documentees, codes d'erreur metier distincts des etats Table transitions complete + table codes metier
CA-14 Les etats terminaux interdisent explicitement toute transition sortante Presence -> * : INTERDITE pour etats terminaux
CA-15 La conformite NF Z42-013 §12.1 est demontree par artefacts de campagne Dossier audit (manifest + rapports + attestation) complet
CA-16 Le manifest est protege par hash SHA-3 et signature HSM, verifies au debut du postcheck manifest_hash et manifest_signature presents et valides
CA-17 Le postcheck revalide que le nombre d'objets source n'a pas change depuis le precheck Comparaison source_object_count actuel vs enregistre
CA-18 Le clearing conditionnel exige N cycles conformes consecutifs avant retour normal Compteur persiste et verifie

8. Scenarios de test (Given / When / Then)

  • SCN-01 Nominal complet
  • Given une campagne DRAFT avec acces source/cible/registre/HSM valides
  • When precheck puis postcheck sont executes avec zero ecart
  • Then la campagne atteint ATTESTED et l'attestation signee est archivee avec protocol_version et manifest_hash

  • SCN-02 Mismatch SHA-3

  • Given une campagne en POSTCHECK_RUNNING
  • When un objet presente sha3_after != sha3_before
  • Then la campagne est KO et termine en ROLLED_BACK

  • SCN-03 Dry-run

  • Given une campagne prete
  • When execution en mode --dry-run
  • Then le postcheck compare source/source, aucun transfert/migration n'est effectue et un verdict readiness est produit

  • SCN-04 Concurrence

  • Given un lock actif sur migration_id
  • When une seconde execution est lancee
  • Then la seconde execution est rejetee avec code metier CONCURRENT_EXECUTION_DENIED, aucune transition d'etat

  • SCN-05 Attestation impossible

  • Given campagne VERIFIED
  • When HSM indisponible au-dela du SLA d'attestation
  • Then code metier ATTESTATION_FAILED puis rollback obligatoire

  • SCN-06 Reconciliation orphelin

  • Given campagne non terminale sans activite > orphan_threshold
  • When le cron de reconciliation s'execute
  • Then reprise idempotente ou etat terminal RECONCILIATION_FAILED

  • SCN-07 WORM

  • Given un objet en retention WORM active
  • When une suppression est tentee pendant la campagne
  • Then l'operation est refusee, evenement WORM_VIOLATION_BLOCKED emis et la campagne est stoppee

  • SCN-08 Transition interdite

  • Given campagne ATTESTED
  • When une transition sortante est demandee
  • Then la transition est refusee car etat terminal

  • SCN-09 Manifest altere

  • Given un manifest dont le contenu a ete modifie apres le precheck
  • When le postcheck demarre et recalcule le hash du manifest
  • Then manifest_hash diverge, la campagne est KO immediate

  • SCN-10 Clearing conditionnel

  • Given une campagne en reconciliation
  • When 1 cycle conforme suivi d'1 cycle non conforme puis 3 cycles conformes consecutifs
  • Then le clearing est refuse apres le 1er cycle, refuse apres le cycle non conforme (compteur remis a zero), autorise apres les 3 cycles conformes

9. Hypotheses explicites

ID Hypothese Impact si faux
H-01 Les metadonnees de reference (sha3, merkle_root, tx_hash) existent pour 100% des objets cibles de la campagne Impossible de prouver l'integrite de bout en bout
H-02 L'acces en lecture au registre d'ancrage blockchain est disponible pendant precheck/postcheck Verification ancrage impossible -> campagne KO
H-03 Une cle HSM dediee a la signature d'attestation et de manifest est provisionnee Attestation et protection manifest non produisibles -> non-conformite
H-04 Les environnements source/cible exposent des inventaires coherents (pas de filtrage implicite) Faux ecarts et faux positifs
H-05 Le perimetre objets de migration est freeze pendant la campagne (valide par revalidation source_object_count au postcheck) Comparaison pre/post non deterministe
H-06 Le protocole est versionne via protocol_version (semver) dans le manifest et l'attestation Evolution non maitrisee et auditabilite degradee

10. Points a clarifier (et contraintes techniques)

10.1 Points a clarifier

ID Point Donnee manquante / Decision requise
Q-01 Reference epique exacte Nom/ID epique non fourni
Q-02 Seuil lisibilite final Confirmer 1.0000 strict vs seuil inferieur tolere
Q-03 Registre blockchain de reference Endpoint(s) et politique de disponibilite
Q-04 Politique de sampling Tirage uniforme global ou stratifie par type MIME/taille
Q-05 Canal de notification DevOps Email, Slack, PagerDuty (et destinataires)
Q-06 Codification officielle des codes retour CI Mapping numerique attendu par pipeline
Q-07 Localisation d'archivage attestation Bucket/prefix probatoire cible contractuel
Q-08 Exigence de double signature attestation Signature unique HSM ou co-signature operationnelle

10.2 Contraintes techniques (stack projet cible)

Projet cible retenu : ProbatioVault-backend

  • Stack principale : NestJS + TypeORM + PostgreSQL
  • Ecosysteme associe au besoin PD-254 :
  • stockage objet compatible S3 (WORM Object Lock),
  • verification outillee par script Python (outil de controle, non substitution stack backend),
  • pipeline GitLab CI pour quality gate.
  • Contraintes :
  • aucune hypothese Swift/SwiftUI, Spring Boot ou autre stack non presente dans le projet cible,
  • compatibilite stricte avec les artefacts backend existants de preuve et d'audit.

Aucune transition temporelle ou borne numerique supplementaire hors celles listees en §5.3 et §5.4.


References

  • Epic : PD-217 — LEGAL & COMPLIANCE
  • JIRA : PD-254
  • Repos concernes : ProbatioVault-backend, ProbatioVault-infra, ProbatioVault-doc
  • Documents associes :
  • NF Z42-013:2020 §12.1
  • ISO 14641
  • PD-244 (audit GAP-FINAL-018)
  • PD-44 (WORM Object Lock)
  • Learnings : PD-264, PD-278, PD-282, PD-98