Aller au contenu

PD-72 — Plan d'implementation


Navigation User Story | Document | | | ---------- | -- | | [Expression de besoin](PD-72-besoin.md) | | | [Specification](PD-72-specification.md) | | | **Plan d'implementation** | *(ce document)* | | [Tests](PD-72-tests.md) | | [<- Retour a crypto-proof](../PD-189-epic.md) . [Index User Story](index.md)

1. Decoupe en composants

Architecture cible

src/modules/document-transfer/
├── document-transfer.module.ts            # Module NestJS, enregistrement BullMQ
├── entities/
│   └── document-transfer.entity.ts        # Entite TypeORM DocumentTransfer
├── enums/
│   ├── transfer-status.enum.ts            # PENDING, READY_FOR_TRANSFER, ...
│   ├── transfer-policy.enum.ts            # copy | transfer
│   └── transfer-error-code.enum.ts        # PRE-01..PRE-08
├── types/
│   └── branded-ids.ts                     # DocumentTransferId, VaultId, ProofId (branded)
├── interfaces/
│   ├── transfer-job.interface.ts          # Payload job BullMQ
│   └── transfer-events.interface.ts       # Contrat evenement TRANSFER_EMPLOYEE
├── services/
│   ├── document-transfer.service.ts       # Service principal (creation, queries)
│   ├── transfer-state-machine.service.ts  # Machine d'etats fermee (INV-08)
│   ├── transfer-orchestrator.service.ts   # Orchestration flux nominal (etapes 1-11)
│   ├── transfer-preconditions.service.ts  # Gardes preconditions (compte, cle, consentement)
│   ├── transfer-proof.service.ts          # Emission evenement, acte de remise, Merkle/TSA
│   ├── transfer-revocation.service.ts     # Revocation acces entreprise (policy transfer)
│   └── transfer-notification.service.ts   # Notification salarie (in-app + email neutre)
├── processors/
│   └── pre-transfer.processor.ts          # Worker BullMQ @Processor('pv:jobs:transfer')
├── listeners/
│   └── anchor-confirmation.listener.ts    # Ecoute confirmation batch -> ANCHOR_CONFIRMED
├── constants/
│   └── transfer.constants.ts              # MAX_RETRIES, BASE_DELAY, ANCHOR_TIMEOUT_HOURS
├── dto/
│   └── transfer-job.dto.ts                # Validation payload job
├── migrations/
│   └── XXXXXX-create-document-transfer.ts # Migration DDL
└── __tests__/
    ├── transfer-state-machine.spec.ts     # Tests unitaires machine d'etats
    ├── transfer-orchestrator.spec.ts      # Tests unitaires orchestration
    ├── transfer-preconditions.spec.ts     # Tests unitaires preconditions
    ├── transfer-proof.spec.ts             # Tests unitaires proof/audit
    ├── transfer-revocation.spec.ts        # Tests unitaires revocation
    ├── pre-transfer.processor.spec.ts     # Tests unitaires processor
    ├── pre-transfer.integration.spec.ts   # Tests integration (DB + queue)
    └── fixtures/
        └── transfer-fixtures.ts           # Factories et fixtures

Composants et responsabilites

Composant Responsabilite
DocumentTransferModule Module NestJS, enregistrement queue BullMQ pv:jobs:transfer, export services, imports modules dependants
DocumentTransfer (entity) Entite TypeORM avec machine d'etats, UUIDs branded, contraintes DB
TransferStateMachineService Transitions autorisees, gardes, interdictions explicites (INV-08). Aucune logique metier.
TransferOrchestratorService Orchestration sequentielle du flux nominal (etapes 1-11 de la spec). Point d'entree unique du processor.
TransferPreconditionsService Verification compte actif, cle publique certifiee, consentement, autorisation emetteur (source_vault_id)
TransferProofService Emission TRANSFER_EMPLOYEE, signature HSM, inclusion Merkle, horodatage TSA, planification ancrage
TransferRevocationService Revocation acces entreprise si policy=transfer (INV-06)
TransferNotificationService Notification salarie in-app + email neutre (CA-10)
PreTransferProcessor Worker BullMQ, idempotence, retry backoff exponentiel, delegation a l'orchestrateur
AnchorConfirmationListener Ecoute confirmation batch blockchain, transition PROOF_PENDING_ANCHOR -> ANCHOR_CONFIRMED
DocumentTransferService CRUD DocumentTransfer, requetes, creation initiale a reception DOCUMENT_SEALED
Migration DDL Creation table vault_secure.document_transfers avec contraintes, index, enum types

2. Flux techniques

Flux N1 — Nominal (copy)

DocumentSealingService          PreTransferProcessor       TransferOrchestratorService
       |                               |                            |
       |-- DOCUMENT_SEALED event ------>|                            |
       |   (audit log source)          |                            |
       |                               |-- process(job) ----------->|
       |                               |                            |
       |                               |   TransferPreconditionsService
       |                               |            |
       |                               |            |<-- checkAll() -->|
       |                               |            |  compte actif?   |
       |                               |            |  cle certifiee?  |
       |                               |            |  consentement?   |
       |                               |            |  vault_id match? |
       |                               |            |                  |
       |                               |   TransferStateMachineService
       |                               |            |
       |                               |            |-- PENDING -> READY_FOR_TRANSFER
       |                               |            |-- READY_FOR_TRANSFER -> TRANSFER_IN_PROGRESS
       |                               |            |
       |                               |   PreService (PD-41)
       |                               |            |
       |                               |            |-- reEncrypt(wrap_Entreprise, rk)
       |                               |            |-- zeroize(rk) immediate
       |                               |            |
       |                               |            |-- TRANSFER_IN_PROGRESS -> TRANSFERRED
       |                               |            |
       |                               |   TransferProofService
       |                               |            |
       |                               |            |-- createEvent(TRANSFER_EMPLOYEE)
       |                               |            |-- signHSM(event)
       |                               |            |-- includeMerkle(event)
       |                               |            |-- timestampTSA(event)
       |                               |            |-- scheduleAnchor(batch)
       |                               |            |-- generateActeRemise()
       |                               |            |
       |                               |            |-- TRANSFERRED -> PROOF_PENDING_ANCHOR
       |                               |            |
       |                               |   TransferNotificationService
       |                               |            |
       |                               |            |-- notifySalarie(in-app + email neutre)
       |                               |            |
       |                               |<-- OK (PROOF_PENDING_ANCHOR) ---|

Flux N2 — Confirmation ancrage (asynchrone)

AnchorBatchProcessor (PD-55)    AnchorConfirmationListener    TransferStateMachineService
       |                               |                            |
       |-- batch FINALIZED ----------->|                            |
       |   (event/callback)            |                            |
       |                               |-- findByProofId() -------->|
       |                               |                            |
       |                               |-- transition() ----------->|
       |                               |   PROOF_PENDING_ANCHOR     |
       |                               |   -> ANCHOR_CONFIRMED      |
       |                               |                            |
       |                               |-- setCompletedAt() ------->|

Flux E1 — Erreur recuperable (HSM/TSA)

PreTransferProcessor            TransferOrchestratorService
       |                               |
       |-- process(job) -------------->|
       |                               |-- ... erreur HSM/TSA ...
       |                               |-- transition -> FAILED
       |                               |-- retry_count < 3 ?
       |                               |   OUI: throw pour retry BullMQ
       |                               |   NON: FAILED terminal, escalade
       |<-- throw (BullMQ retry) ------|
       |                               |
       |-- backoff: 30s * 2^count ---->|
       |-- process(job) retry -------->|

Flux E2 — Replay idempotent

PreTransferProcessor            TransferOrchestratorService
       |                               |
       |-- process(job) -------------->|
       |                               |-- findByDocumentId()
       |                               |-- status in terminal?
       |                               |   OUI: rejet PRE-06 (log replay + code retour)
       |                               |   NON: reprise etat courant
       |<-- OK (idempotent) -----------|

2bis. Diagrammes Mermaid

Graphe de dependances inter-composants

graph TD
    subgraph "PD-72 — DocumentTransferModule"
        PROC[PreTransferProcessor]
        ORCH[TransferOrchestratorService]
        SM[TransferStateMachineService]
        PREC[TransferPreconditionsService]
        PROOF[TransferProofService]
        REVOC[TransferRevocationService]
        NOTIF[TransferNotificationService]
        LISTEN[AnchorConfirmationListener]
        SVC[DocumentTransferService]
        ENTITY[(DocumentTransfer entity)]
    end

    subgraph "Modules externes"
        PRE["PreService (PD-41)"]
        AUDIT["AuditService (PD-31)"]
        HSM["HsmService (PD-37)"]
        TSA["TsaService (PD-39)"]
        MERKLE["MerkleService (PD-54)"]
        ANCHOR["AnchorService (PD-55)"]
        NOTIF_EXT["NotificationService (PD-105)"]
        DOC["DocumentSecure entity"]
    end

    PROC -->|"delegue process(job)"| ORCH
    ORCH --> SM
    ORCH --> PREC
    ORCH --> PROOF
    ORCH -->|"si policy=transfer"| REVOC
    ORCH --> NOTIF
    ORCH -->|"reEncrypt()"| PRE
    ORCH --> SVC
    SVC --> ENTITY
    PREC --> DOC

    PROOF --> AUDIT
    PROOF --> HSM
    PROOF --> TSA
    PROOF --> MERKLE
    PROOF --> ANCHOR

    NOTIF --> NOTIF_EXT

    LISTEN -->|"batch FINALIZED"| SM
    LISTEN --> SVC

    style PROC fill:#4a86c8,color:#fff
    style ORCH fill:#4a86c8,color:#fff
    style SM fill:#e8a735,color:#fff
    style PROOF fill:#d64045,color:#fff
    style ENTITY fill:#6c757d,color:#fff

Diagramme de sequence — Flux nominal (copy)

sequenceDiagram
    participant Seal as DocumentSealingService
    participant Queue as BullMQ pv:jobs:transfer
    participant Proc as PreTransferProcessor
    participant Orch as TransferOrchestratorService
    participant SM as TransferStateMachineService
    participant Prec as TransferPreconditionsService
    participant PRE as PreService (PD-41)
    participant Proof as TransferProofService
    participant HSM as HsmService (PD-37)
    participant TSA as TsaService (PD-39)
    participant Merkle as MerkleService (PD-54)
    participant Anchor as AnchorService (PD-55)
    participant Notif as TransferNotificationService

    Seal->>Queue: DOCUMENT_SEALED event
    Queue->>Proc: process(job)
    Proc->>Orch: execute(job.data)

    rect rgb(230, 245, 255)
        Note over Orch,Prec: Phase 1 — Preconditions
        Orch->>Prec: checkAll(payload)
        Prec-->>Orch: OK (compte actif, cle certifiee, consentement, vault_id)
    end

    rect rgb(230, 255, 230)
        Note over Orch,PRE: Phase 2 — Re-chiffrement PRE
        Orch->>SM: transition(PENDING → READY_FOR_TRANSFER)
        Orch->>SM: transition(READY_FOR_TRANSFER → TRANSFER_IN_PROGRESS)
        Orch->>PRE: reEncrypt(wrap_Entreprise, rk)
        PRE-->>Orch: wrap_Salarie
        Note over Orch: zeroize(rk) dans finally
        Orch->>SM: transition(TRANSFER_IN_PROGRESS → TRANSFERRED)
    end

    rect rgb(255, 240, 230)
        Note over Orch,Anchor: Phase 3 — Preuve probatoire
        Orch->>Proof: createTransferProof()
        Proof->>HSM: sign(event_hash)
        HSM-->>Proof: signature
        Proof->>Merkle: include(event)
        Merkle-->>Proof: merkle_proof
        Proof->>TSA: timestamp(event)
        TSA-->>Proof: tsa_token
        Proof->>Anchor: schedule(batch)
        Proof-->>Orch: proof_id
        Orch->>SM: transition(TRANSFERRED → PROOF_PENDING_ANCHOR)
    end

    rect rgb(245, 230, 255)
        Note over Orch,Notif: Phase 4 — Notification
        Orch->>Notif: notifySalarie(transfer_ref)
        Notif-->>Orch: OK
    end

    Orch-->>Proc: OK (PROOF_PENDING_ANCHOR)

Diagramme de sequence — Confirmation ancrage (asynchrone)

sequenceDiagram
    participant Batch as AnchorBatchProcessor (PD-55)
    participant Listen as AnchorConfirmationListener
    participant SM as TransferStateMachineService
    participant SVC as DocumentTransferService

    Batch->>Listen: batch FINALIZED (tx_id)
    Listen->>SVC: findByProofId(proof_id)
    SVC-->>Listen: DocumentTransfer (PROOF_PENDING_ANCHOR)
    Listen->>SM: transition(PROOF_PENDING_ANCHOR → ANCHOR_CONFIRMED)
    Listen->>SVC: setCompletedAt(now)
    Note over Listen: Transfert termine

Machine d'etats — Transitions autorisees

stateDiagram-v2
    [*] --> PENDING: DOCUMENT_SEALED recu

    PENDING --> READY_FOR_TRANSFER: Preconditions OK
    PENDING --> BLOCKED_WAITING_CONSENT: Compte absent (PRE-01)
    PENDING --> FAILED: Cle invalide (PRE-02)

    BLOCKED_WAITING_CONSENT --> READY_FOR_TRANSFER: Consentement valide

    READY_FOR_TRANSFER --> TRANSFER_IN_PROGRESS: Debut re-chiffrement

    TRANSFER_IN_PROGRESS --> TRANSFERRED: Re-chiffrement OK
    TRANSFER_IN_PROGRESS --> FAILED: Erreur (retry < 3)

    TRANSFERRED --> PROOF_PENDING_ANCHOR: Preuve emise

    PROOF_PENDING_ANCHOR --> ANCHOR_CONFIRMED: Batch finalise
    PROOF_PENDING_ANCHOR --> FAILED: Timeout ancrage

    FAILED --> PENDING: Retry (count < MAX)

    ANCHOR_CONFIRMED --> [*]

3. Mapping invariants -> mecanismes

Invariant ID Exigence Mecanisme Composant Observable Risque
INV-01 K_docEntreprise jamais modifiee L'orchestrateur n'accede jamais a K_docEntreprise. Le PRE engine (PD-41) recoit wrap_Entreprise et produit wrap_Salarie sans toucher la cle source. Assertion : empreinte K_doc identique avant/apres. TransferOrchestratorService, PreService (PD-41) Hash K_doc avant == apres dans test TC-INV-01 Faible — PD-41 deja valide
INV-02 Worker ne dechiffre jamais Aucun appel a decrypt() ou unwrap() dans le module document-transfer. Seul reEncrypt() aveugle est appele. Audit traces d'appels. TransferOrchestratorService Grep sur traces d'appels : absence de decrypt/unwrap (TC-INV-02) Faible — architecture PRE by design
INV-03 rk detruit immediatement apres re-chiffrement Appel zeroize() sur buffer rk dans un bloc finally apres reEncrypt(). Buffer remplace par zeros. Variable mise a null. TransferOrchestratorService TC-INV-03 : buffer non recuperable post-traitement Moyen — necessite pattern zeroize explicite
INV-04 Aucun secret temporaire persiste en clair rk jamais ecrit en DB/log/queue. Si persistance intermediaire necessaire : envelope AES-256-GCM via HSM. Scan stockage/logs/queues. TransferOrchestratorService, PreTransferProcessor TC-INV-04 : scan DB + logs + queue payload = 0 secret clair Moyen — attention aux logs debug
INV-05 Trace append-only, signee HSM, Merkle ancre TransferProofService cree TRANSFER_EMPLOYEE via AuditService.logEvent() (append-only PD-31), signe HSM via HsmService.sign() (PD-37). Cote verification : utiliser crypto.verify(null, hash, key, sig) — JAMAIS createVerify() (learning PD-282), inclut Merkle (PD-54), horodatage TSA (PD-39), planifie ancrage (PD-55). TransferProofService TC-INV-05 : chaine complete event -> signature -> merkle -> tx_id Moyen — dependance 4 modules externes
INV-06 Revocation acces entreprise si policy=transfer TransferRevocationService appele uniquement si transfer_policy === 'transfer' ET etat PROOF_PENDING_ANCHOR atteint. Revocation via modification droits acces coffre entreprise. TransferRevocationService TC-NOM-02 : acces entreprise refuse apres finalisation Moyen — irreversibilite a tester
INV-07 Idempotence (pas de double signature/acte/ancrage) Garde idempotence par DocumentTransfer.id : si etat terminal, rejet explicite PRE-06 avec log securite + code retour observable. Contrainte UNIQUE sur (document_id, target_vault_id) en DB. TransferOrchestratorService, DocumentTransfer entity TC-ERR-05, TC-NR-01 : compteurs artefacts stables Faible — contrainte DB + garde code
INV-08 Machine d'etats fermee TransferStateMachineService : map explicite des transitions autorisees. Toute transition non listee leve TransferForbiddenTransitionError + log securite. TransferStateMachineService TC-INV-08 : toute transition non listee rejetee + log Faible — exhaustivite testable
INV-09 Auteur != validateur Metadata preuve : author_id (worker service account) != validator_id (HSM signer). Assertion dans TransferProofService. TransferProofService TC-INV-09 : author_id != validator_id dans dossier preuve Faible — separation structurelle

4. Mapping criteres d'acceptation -> mecanismes

Critere ID Mecanisme(s) Composant Observable Risque
CA-01 Re-chiffrement PRE aveugle produit wrap_Salarie dechiffrable par cle salarie. Test end-to-end : salarie dechiffre avec sa cle privee. TransferOrchestratorService, PreService Lecture valide cote salarie Faible
CA-02 TransferRevocationService supprime/revoque l'acces entreprise. Tentative acces post-revocation -> refus. TransferRevocationService Acces entreprise refuse (HTTP 403) Moyen
CA-03 Contrainte UNIQUE (document_id, target_vault_id) + garde idempotence dans orchestrateur. Un seul TRANSFER_EMPLOYEE par DocumentTransfer.id. TransferOrchestratorService, entity DB Unicite event + signature verifiable Faible
CA-04 TransferProofService enchaine : event -> HSM sign -> Merkle include -> TSA timestamp -> anchor schedule. proof_id resolu + tx_id a confirmation. TransferProofService proof_id + tx_id presents Moyen
CA-05 Garde idempotence : si DocumentTransfer existe en etat terminal, rejet explicite PRE-06 (etat inchange, log replay detecte, code retour observable). Compteurs artefacts identiques. TransferOrchestratorService Compteurs stables au replay Faible
CA-06 rk zeroize dans finally. Scan stockage : aucun secret clair. Payload job BullMQ ne contient jamais rk. TransferOrchestratorService, PreTransferProcessor Audit stockage = 0 secret clair Moyen
CA-07 Traitement synchrone (hors batch) <= 3s P95. Mesure latence dans processor avec metriques. PreTransferProcessor P95 <= 3000ms Moyen — dependant SLA modules
CA-08 TransferStateMachineService rejette toute transition non listee. Log securite emis. TransferStateMachineService Erreur metier + log transition interdite Faible
CA-09 retry_count >= MAX_RETRIES -> FAILED terminal. Processor ne re-throw pas. PreTransferProcessor, TransferStateMachineService retry_count=3, FAILED terminal, 0 relance Faible
CA-10 TransferNotificationService envoie message neutre : reference transfert, pas de contenu/cle/hash. Template valide par test. TransferNotificationService Contenu notification sans payload documentaire Faible

5. Mapping tests (TC-*) -> mecanismes + observables

Test ID Reference spec Mecanisme(s) Point(s) d'observation Niveau de test vise
TC-NOM-01 CA-01, CA-03, CA-04, INV-01..05 Flux complet copy : PENDING -> ANCHOR_CONFIRMED Etat final, acces salarie, event unique, proof_id + tx_id, acces entreprise conserve Integration
TC-NOM-02 CA-02, INV-06 Flux transfer + revocation entreprise Acces entreprise refuse apres ANCHOR_CONFIRMED Integration
TC-NOM-03 PRE-05 Etat PROOF_PENDING_ANCHOR sans confirmation batch Pas de rollback, acces salarie OK Unit
TC-NOM-04 CA-10 Template notification neutre Contenu sans document/cle/hash Unit
TC-ERR-01 PRE-01 Precondition : compte absent -> BLOCKED_WAITING_CONSENT Etat cible, 0 artefact probatoire Unit
TC-ERR-02 PRE-02 Precondition : cle invalide -> FAILED + alerte Etat FAILED, log securite Unit
TC-ERR-03 PRE-03, CA-09 HSM persistant indisponible, 3 retries -> FAILED terminal retry_count=3, 0 4e relance Unit
TC-ERR-04 PRE-04 TSA indisponible temporaire, reprise avant retry 3 Reprise reussie, 0 doublon event Integration
TC-ERR-05 PRE-06, INV-07, CA-03, CA-05 Replay sur transfert finalise -> rejet idempotent PRE-06 (log + code retour explicite) Etat inchange, 0 nouvel artefact Unit
TC-ERR-06 PRE-07 Format invalide (hash_doc hors regex) -> rejet strict Log securite, refus creation Unit
TC-ERR-07 PRE-08, CA-09 retry_count=3, relance -> refus immediat FAILED terminal, escalade tracee Unit
TC-ERR-08 SLA ancrage, ANCHOR_TIMEOUT_HOURS Timeout ancrage -> FAILED + alerte ops Transition FAILED, alerte ops Unit
TC-ERR-09 BLOCKED -> READY Consentement valide -> reprise flux Transition autorisee, notification RH Unit
TC-ERR-10 Autorisation emetteur source_vault_id mismatch -> rejet + alerte 0 DocumentTransfer cree, log securite Unit
TC-ERR-11 Backoff exponentiel retry_count=1 -> delay >= 60s Delay >= base_delay * 2^count Unit
TC-INV-01 INV-01 Hash K_doc avant/apres job Empreinte identique Unit
TC-INV-02 INV-02 Audit traces : 0 decrypt/unwrap Absence d'operation de dechiffrement Unit
TC-INV-03 INV-03 Zeroize buffer rk post re-chiffrement Buffer non recuperable Unit
TC-INV-04 INV-04 Scan DB/logs/queue : 0 secret clair Aucune valeur secrete en clair Integration
TC-INV-05 INV-05 Chaine append-only -> HSM -> Merkle -> TSA -> anchor Chaine complete Integration
TC-INV-08 INV-08 Transitions non listees rejetees Erreur + log pour chaque transition interdite Unit
TC-INV-09 INV-09 author_id != validator_id Metadata preuve Unit
TC-NR-01 INV-07 Relance meme job -> 0 artefact supplementaire Compteurs stables Unit
TC-NR-02 Interdictions retour TRANSFERRED->TRANSFER_IN_PROGRESS etc. rejetes Rejets explicites Unit
TC-NR-03 CA-07 Latence P95 hors batch P95 <= 3000ms Perf
TC-NR-04 Verrou policy Downgrade transfer->copy apres TRANSFER_IN_PROGRESS Rejet Unit
TC-NR-05 CA-09 0 relance auto au-dela de 3 retry_count=3, FAILED terminal Unit
TC-NEG-01 Format UUID UUID non v4 -> rejet strict Erreur validation + log securite Unit
TC-NEG-02 Format rk_id rk_id hors charset -> rejet + alerte 0 traitement PRE lance Unit
TC-NEG-03 Transition interdite ANCHOR_CONFIRMED -> tout -> rejet Log transition interdite Unit
TC-NEG-04 Replay concurrent N messages identiques simultanes 1 seul resultat logique Integration
TC-NEG-05 Secret temporaire Payload malveillant persistance clair -> rejet Absence secret clair au repos Unit/Sec

6. Gestion des erreurs

Code Cas Mecanisme technique Composant responsable Observable
PRE-01 Salarie sans compte actif TransferPreconditionsService.checkAccountActive() -> false -> transition BLOCKED_WAITING_CONSENT TransferPreconditionsService, TransferStateMachineService Etat BLOCKED, notification RH (1 email, dedup par transfer.id)
PRE-02 Cle publique invalide/non certifiee TransferPreconditionsService.checkPublicKey() -> validation echec -> transition FAILED (non recuperable) TransferPreconditionsService Etat FAILED, alerte securite, 0 retry
PRE-03 HSM indisponible TransferProofService.signHSM() throw -> catch dans orchestrateur -> transition FAILED -> retry si count < 3 TransferProofService, TransferOrchestratorService Etat FAILED, retry_count incremente, backoff exponentiel
PRE-04 TSA indisponible TransferProofService.timestampTSA() throw -> meme pattern que PRE-03 TransferProofService, TransferOrchestratorService Retry avec backoff
PRE-05 Batch blockchain differe Non bloquant : etat PROOF_PENDING_ANCHOR valide sans tx_id TransferProofService Etat PROOF_PENDING_ANCHOR, ancrage asynchrone
PRE-06 Replay detecte Garde idempotence : findByDocumentId() + check etat terminal -> rejet explicite PRE-06 (code + log securite) TransferOrchestratorService Etat inchange, 0 nouvel artefact
PRE-07 Format invalide Validation DTO (class-validator) + regex contractuels sur chaque champ (section 5.1 spec) transfer-job.dto.ts, validators Rejet strict, log securite
PRE-08 Retry max atteint Garde retry_count >= MAX_RETRIES dans processor -> ne pas re-throw -> FAILED terminal PreTransferProcessor FAILED terminal, escalade manuelle tracee
Timeout ancrage ANCHOR_TIMEOUT_HOURS depasse AnchorConfirmationListener ou cron : si created_at + timeout < now et etat PROOF_PENDING_ANCHOR -> FAILED AnchorConfirmationListener FAILED, alerte ops
Crash pre-commit Rollback transaction TypeORM transaction wrapping : aucun artefact persiste TransferOrchestratorService DB coherente, reprise worker
Crash post-commit Etat DB = source de verite Worker reprend avec etat courant de DocumentTransfer PreTransferProcessor Reprise idempotente

Strategie de retry (backoff exponentiel)

// Dans PreTransferProcessor
const backoffDelay = TRANSFER_CONSTANTS.BASE_DELAY_MS * Math.pow(2, transfer.retry_count);
// BASE_DELAY_MS = 30_000 (30s)
// retry 0: 30s, retry 1: 60s, retry 2: 120s

Erreurs non recuperables (PRE-02, PRE-07) : FAILED terminal immediat, pas de retry.


7. Impacts securite

Risque Mitigation Composant Journalisation
Exposition rk en memoire Zeroization immediate dans finally (buffer.fill(0) + null). Pas de log du contenu rk. TransferOrchestratorService Log de zeroization (sans valeur)
Replay/double traitement Garde idempotence par DocumentTransfer.id + UNIQUE constraint DB TransferOrchestratorService, entity Log PRE-06
Injection via payload job Validation DTO stricte (class-validator + regex contractuels). Rejet format invalide. transfer-job.dto.ts Log PRE-07 + alerte securite
source_vault_id forge Verification autorisation : source_vault_id du job == vault_id du document en base TransferPreconditionsService Log TC-ERR-10 + alerte securite
Timing attack sur tokens timingSafeEqual() pour toute comparaison de tokens/secrets (learning PD-238) Services de verification n/a
Double-hash signature HSM Signature : HsmService.sign(hash) (appel PKCS#11 CKM_ECDSA). Verification : crypto.verify(null, hash, key, sig) — JAMAIS createVerify() (learning PD-282). Test roundtrip sign-verify OBLIGATOIRE (learning PD-282). TransferProofService n/a
Revocation incomplete (policy transfer) Transaction atomique : revocation + log audit dans meme TX TransferRevocationService Audit WORM
Log sensible rk, wrap_Entreprise, secrets temporaires JAMAIS loggues. Seuls les IDs de correlation. Tous Politique de log
Concurrent replay storm Verrouillage pessimiste (SELECT FOR UPDATE) sur DocumentTransfer par document_id TransferOrchestratorService TC-NEG-04

IDs aleatoires

Tous les UUIDs generes via crypto.randomUUID() (learning PD-63). Typage branded :

type DocumentTransferId = string & { readonly __brand: 'DocumentTransferId' };
type VaultId = string & { readonly __brand: 'VaultId' };
type ProofId = string & { readonly __brand: 'ProofId' };

8. Hypotheses techniques

ID Hypothese Impact si faux Mitigation
H-01 Modules PD-41 (PRE), PD-37 (HSM signature), PD-39 (TSA), PD-54 (Merkle), PD-55 (anchoring), PD-31 (audit) disponibles et fonctionnels. Blocage flux nominal. Interfaces mockees pour tests unitaires. Stubs documentes avec story destination.
H-02 employee_consent est fourni par source fiable (module auth/user). Transfert non autorise ou blocage abusif. TransferPreconditionsService valide le flag ; si absent/faux -> BLOCKED_WAITING_CONSENT.
H-03 La verification d'identite coffres source/cible est assuree par modules existants (vault, auth). Transfert vers mauvais coffre. Garde source_vault_id match dans TransferPreconditionsService.
H-04 Les IDs suivent UUID v4 (crypto.randomUUID()). Non-conformite format. Validation regex sur tous les UUIDs entrants.
H-05 Queue pv:jobs:transfer est deja declaree dans queue-names.ts (PD-21). Blocage enregistrement processor. Ajout conditionnel si absente.
H-06 Le module notifications (PD-105) expose un service injectable pour email + in-app. Notification impossible. // STUB: PD-105 — injection conditionnelle, fallback log.
H-07 Le consentement peut etre mis a jour apres blocage (event ou polling). BLOCKED_WAITING_CONSENT permanent. Listener/cron pour re-evaluer les transferts bloques.

9. Points de vigilance (risques, dette, pieges)

  1. Zeroization fiable : JavaScript n'offre pas de garantie formelle de zeroization memoire (GC, optimisations V8). Le pattern buffer.fill(0) + null est le meilleur effort. Pour une garantie HSM-grade, les secrets doivent rester dans le HSM (c'est le cas de K_doc). Le rk est le seul secret temporaire manipule cote Node.js.

  2. Couplage modules externes : L'orchestrateur depend de 6+ modules (PRE, HSM, TSA, Merkle, anchoring, audit). Chaque module est injecte via interface NestJS. Les tests unitaires mockent chaque dependance. Les tests d'integration valident les interactions reelles.

  3. Machine d'etats exhaustive : La spec definit 8 etats et ~10 transitions autorisees. Le service doit couvrir les 8x8=64 combinaisons possibles (10 autorisees, 54 interdites). Les tests TC-INV-08 et TC-NR-02 couvrent les interdictions.

  4. Timeout ancrage configurable : ANCHOR_TIMEOUT_HOURS (defaut 24h, min 1h, max 72h) via variable d'environnement. Le cron/listener de timeout doit etre resilient aux redemarrages (verification basee sur created_at en DB, pas sur timer en memoire).

  5. Notification anti-spam : Deduplication par DocumentTransfer.id pour la notification RH (BLOCKED_WAITING_CONSENT). Un seul email par transfert bloque.

  6. Verrouillage pessimiste : Utiliser SELECT FOR UPDATE pour proteger contre les replays concurrents (TC-NEG-04). Attention au deadlock si plusieurs transferts pour le meme document.

  7. Migration DDL : Nouvelle table vault_secure.document_transfers. Aucune modification de table existante. Les enum types PostgreSQL (transfer_status, transfer_policy) sont crees dans la migration.

  8. ALTER TYPE ADD VALUE : Si les enums sont ajoutes a un type existant (peu probable ici — types nouveaux), respecter le pattern commitTransaction() avant utilisation (learning PD-282/PD-279).


10. Perimetre de test

Niveau de test In scope Hors scope (justification)
Unitaire Tous les composants : state machine, orchestrateur, preconditions, proof, revocation, notification, processor, listener --
Integration DB + queue BullMQ : flux complet PENDING->ANCHOR_CONFIRMED, replay concurrent (TC-NEG-04), scan secrets (TC-INV-04), chaine proof complete (TC-INV-05) --
E2E Hors scope : necessite modules PD-41/PD-37/PD-39/PD-54/PD-55 deployes et fonctionnels. Les tests d'integration avec mocks valident les contrats d'interface. Infrastructure multi-module non disponible en CI unitaire. Couvert par integration avec mocks des interfaces.
Performance TC-NR-03 : benchmark P95 latence hors batch sur jeu de charge contractuel --
Securite TC-INV-03/04, TC-NEG-02/05 : zeroization, scan secrets, injection payload --

Tous les niveaux de test intra-module sont couverts. L'E2E cross-module est reporte aux tests d'integration avec mocks des interfaces PD-41/PD-37/PD-39/PD-54/PD-55.

Couverture minimale attendue : >= 80% sur le perimetre in scope.

Framework de test : Jest (configuré dans le projet ProbatioVault-backend existant, jest.config.ts). Runner CJS-compatible. Les modules ESM-only (si presents) sont transformes via ts-jest.

Ancre temporelle timeout ancrage : Le timeout ANCHOR_TIMEOUT_HOURS est calcule depuis le timestamp de transition vers PROOF_PENDING_ANCHOR (champ updated_at de DocumentTransfer), pas depuis created_at.


11. Hors perimetre

  • Implementation interne des primitives cryptographiques (PRE/HSM/TSA/Merkle/blockchain) — modules existants.
  • UX de consentement salarie — suppose employee_consent = TRUE au moment nominal.
  • Format interne du proof_bundle.json (ProofEnvelope canonical existant).
  • Validation juridique externe de l'opposabilite probatoire.
  • Interface REST/HTTP pour declencher un transfert — le declenchement est exclusivement par evenement interne DOCUMENT_SEALED.
  • Modification des routes d'autres modules : aucune modification d'autres modules. PD-72 est un flux asynchrone interne (worker BullMQ) sans route HTTP cross-module a proteger.

12. Mecanismes cross-module

Aucune modification d'autres modules. PD-72 consomme les services existants via injection de dependances NestJS :

Module consomme Service/Interface Usage dans PD-72
crypto/pre (PD-41) PreService.reEncrypt() Re-chiffrement aveugle
audit (PD-31) AuditService.logEvent() Journal append-only WORM
crypto/hsm (PD-37) HsmService.sign() Signature evenement probatoire
tsa (PD-39) TsaService.timestamp() Horodatage RFC 3161
merkle (PD-54) MerkleService.include() Inclusion Merkle batch
anchor (PD-55) AnchorService.schedule() Planification ancrage blockchain
notifications (PD-105) NotificationService.send() Notification salarie. // STUB: PD-105 si non disponible
documents DocumentSecure entity (read-only) Verification document SEALED + vault_id