PD-55 — Worker ancrage blockchain périodique¶
1. Objectif¶
Spécifier, de manière contractuelle, le comportement d'un mécanisme automatique et périodique d'ancrage blockchain des événements probatoires internes ProbatioVault, afin de garantir : - l'intégrité cryptographique des lots d'événements, - la traçabilité bidirectionnelle entre événements internes et transaction publique, - la continuité temporelle de couverture probatoire, - la vérifiabilité externe sans coopération active de ProbatioVault.
La présente spécification est normative : toute exigence formulée avec "DOIT" est obligatoire.
2. Périmètre / Hors périmètre¶
Inclus¶
- Agrégation périodique des événements probatoires non encore ancrés.
- Construction d'un lot d'ancrage avec racine cryptographique unique (Merkle root).
- Publication de la racine sur blockchain publique via le contrat d'ancrage disponible.
- Enregistrement de la référence de transaction publique (
tx_id) et du lien aux événements couverts. - Gestion des états d'ancrage des événements et du lot (cycle de vie traçable).
- Détection et traitement des erreurs d'ancrage (sans perte d'événement).
- Auditabilité externe des ancrages (preuves vérifiables hors système).
Exclu¶
- Interface utilisateur de visualisation des ancrages.
- Multi-chain simultané.
- Fallback automatique vers une autre blockchain.
- Changement de design cryptographique du Merkle persistence existant (PD-237).
- Toute règle juridique interprétative non traduisible en vérification technique automatisable (hors périmètre).
3. Définitions¶
- Événement probatoire : enregistrement métier produisant une trace devant être intégrée à une preuve composite.
- Événement éligible : événement probatoire validé, non ancré, appartenant à la fenêtre d'agrégation active.
- Types d'événements probatoires éligibles (liste fermée V1) :
DOCUMENT_SIGNED,DOCUMENT_CERTIFIED,AUDIT_LOG_ENTRY. - Fenêtre d'ancrage : intervalle temporel de collecte des événements à agréger pour un cycle donné.
- Cycle d'ancrage : exécution périodique complète : sélection → agrégation → publication → rattachement.
- Lot d'ancrage : ensemble immuable d'événements couverts par une même racine.
- Ordre canonique des événements d'un lot : tri strict par
(created_at ASC, event_id ASC); en cas d'égalité decreated_at, comparaison lexicographique ASCII deevent_id. - Format temporel
created_at: timestamp ISO 8601 UTC avec précision milliseconde (ex:2026-02-21T10:30:00.123Z). Tous les horodatages du système DOIVENT être normalisés en UTC avant comparaison. - Canonicalisation JSON : sérialisation canonique normative selon RFC 8785 (JSON Canonicalization Scheme) avant calcul des hash utilisés pour l'arbre de Merkle.
- Merkle root : empreinte racine représentant l'ensemble ordonné des événements d'un lot.
tx_id: identifiant de transaction blockchain de publication de la racine.- Finalité : état où la transaction est considérée confirmée lorsque le seuil de finalité est atteint, défini comme
>= 30 confirmationsou>= 5 minutesaprès publication avec statut transaction valide. - Append-only : aucune suppression ni modification destructive d'un événement ou d'un lien d'ancrage déjà persisté.
- Traçabilité bidirectionnelle : capacité de retrouver, depuis un événement, son lot/racine/transaction, et depuis une transaction, la liste des événements couverts.
4. Invariants (non négociables)¶
| ID | Règle | Justification |
|---|---|---|
| INV-55-01 | Le contenu métier brut ne DOIT jamais être publié on-chain ; seules des empreintes cryptographiques DOIVENT l'être. | Protection des données, confidentialité probatoire. |
| INV-55-02 | Un événement ne DOIT être rattaché qu'à au plus un lot d'ancrage finalisé. | Idempotence et absence de double ancrage involontaire. |
| INV-55-03 | Tout événement ancré DOIT être rattachable à un lot, une racine, un tx_id, un horodatage d'ancrage et un état de finalité. | Traçabilité complète et auditabilité. |
| INV-55-04 | Un lot d'ancrage finalisé DOIT être immutable (append-only) : ni modification de périmètre, ni réécriture de racine, ni réaffectation de tx_id. | Intégrité de preuve et non-répudiation. |
| INV-55-05 | Le calcul de racine d'un lot DOIT être déterministe pour un même ensemble d'événements ordonné selon l'ordre canonique (INV-55-13) et canonicalisé selon RFC 8785 avant hash. | Reproductibilité et vérification indépendante. |
| INV-55-06 | Aucun événement éligible ne DOIT être perdu en cas d'échec partiel du cycle ; il DOIT rester ré-ancrable. | Robustesse opérationnelle et continuité probatoire. |
| INV-55-07 | Les fenêtres temporelles successives ne DOIVENT laisser aucun "trou" de couverture pour les événements éligibles. | Continuité temporelle exigée. |
| INV-55-08 | La preuve externe DOIT être vérifiable sans accès privilégié aux systèmes internes (à partir d'artefacts exportables et données publiques). | Auditabilité externe. |
| INV-55-09 | Toute entrée externe au worker (identifiants, statuts transaction, références de lot) DOIT être validée strictement avant usage. | Réduction des surfaces d'injection/corruption (learning PD-44). |
| INV-55-10 | Toute transition d'état d'un lot ou d'un événement DOIT être journalisée de façon horodatée et corrélable. | Exigence d'audit et forensics. |
| INV-55-11 | Si aucune donnée éligible n'existe sur une fenêtre, le cycle DOIT produire une trace explicite "no-op" auditée. | Continuité d'exploitation et preuve d'exécution périodique. |
| INV-55-12 | Une stratégie de continuité en cas d'indisponibilité prolongée de la chaîne cible DOIT être documentée et testée au niveau procédure (même sans fallback automatique). | Évite dépendance exclusive non maîtrisée. |
| INV-55-13 | L'ordre canonique d'un lot DOIT être strictement appliqué avant construction Merkle : tri (created_at ASC, event_id ASC) avec comparaison lexicographique ASCII sur event_id comme tie-breaker. | Supprime l'ambiguïté d'ordonnancement et garantit l'unicité du résultat de racine. |
5. Flux nominaux¶
- Le cycle démarre périodiquement selon la cadence cible (10 minutes) et la politique de tolérance temporelle configurée.
- Les événements éligibles non ancrés sont sélectionnés dans la fenêtre.
- Un lot d'ancrage est constitué avec identifiant unique et périmètre figé.
- La racine Merkle du lot est produite de manière déterministe.
- La racine est publiée sur la blockchain cible via le contrat d'ancrage.
- Le
tx_idretourné est enregistré avec le lot. - Après atteinte de la finalité, le lot passe à l'état finalisé.
- Les événements du lot passent à l'état ancré, avec lien explicite vers lot/racine/
tx_id. - Les journaux d'audit du cycle (horodatages, volumes, statuts) sont persistés.
5bis. Diagrammes¶
5bis.1 Diagramme d'états — Cycle de vie d'un lot d'ancrage¶
Le lot d'ancrage traverse les états suivants. Toute transition est journalisée (INV-55-10). Un lot finalisé est immutable (INV-55-04).
stateDiagram-v2
[*] --> CREATED : Cycle démarre,\névénements éligibles sélectionnés
CREATED --> MERKLE_COMPUTED : Racine Merkle calculée\n(déterministe, INV-55-05/INV-55-13)
CREATED --> FAILED : ERR-55-02 Échec calcul racine
MERKLE_COMPUTED --> TX_SUBMITTED : Racine publiée on-chain\n(empreinte seule, INV-55-01)
MERKLE_COMPUTED --> FAILED : ERR-55-03 Rejet transaction
TX_SUBMITTED --> PENDING_FINALITY : tx_id enregistré,\nattente confirmations
TX_SUBMITTED --> FAILED : ERR-55-04 tx_id invalide/absent
PENDING_FINALITY --> FINALIZED : Finalité atteinte\n(≥30 confirmations OU ≥5min)
PENDING_FINALITY --> ESCALATED : ERR-55-05 Timeout 1h\n→ alerte WARNING
PENDING_FINALITY --> FAILED : ERR-55-03 Rejet détecté\naprès soumission
ESCALATED --> FINALIZED : Finalité atteinte\naprès escalade
ESCALATED --> FAILED : Timeout 2h → CRITICAL
FINALIZED --> [*] : Lot immutable (INV-55-04),\névénements marqués ancrés (INV-55-02)
FAILED --> [*] : Événements restent\nrééligibles (INV-55-06) 5bis.2 Diagramme d'états — Cycle de vie d'un événement probatoire¶
Un événement ne peut être rattaché qu'à un seul lot finalisé (INV-55-02). Toute tentative de double rattachement est rejetée (ERR-55-06).
stateDiagram-v2
[*] --> ELIGIBLE : Événement validé,\nnon encore ancré
ELIGIBLE --> ASSIGNED : Rattaché à un lot\n(périmètre figé)
ELIGIBLE --> ELIGIBLE : Cycle no-op (INV-55-11),\nreste éligible
ASSIGNED --> ANCHORED : Lot finalisé,\nlien lot/racine/tx_id (INV-55-03)
ASSIGNED --> ELIGIBLE : Lot échoué (INV-55-06),\nretour éligible
ANCHORED --> [*] : Traçabilité complète\n(INV-55-03, INV-55-08)
note right of ANCHORED
Immutable — append-only (INV-55-04)
Double rattachement interdit (INV-55-02)
end note 5bis.3 Diagramme de séquence — Cycle d'ancrage nominal¶
Ce diagramme illustre les interactions entre services lors d'un cycle complet réussi. La canonicalisation RFC 8785 (INV-55-05) et l'ordre canonique (INV-55-13) sont appliqués avant le calcul Merkle.
sequenceDiagram
autonumber
participant Scheduler as Scheduler<br/>(BullMQ)
participant Worker as Anchor Worker
participant DB as PostgreSQL<br/>(append-only)
participant Merkle as Merkle Service<br/>(PD-237)
participant Contract as Smart Contract<br/>(PD-53)
participant Chain as Blockchain L2<br/>(PD-52)
Scheduler->>Worker: Déclenchement cycle (cadence 10min ±2min)
Worker->>DB: SELECT événements éligibles dans fenêtre<br/>(INV-55-07 continuité temporelle)
alt Aucun événement éligible
Worker->>DB: INSERT trace no-op auditée (INV-55-11)
else Événements présents
Worker->>DB: INSERT lot CREATED + rattachement événements<br/>(périmètre figé, max 10K par lot)
Worker->>Worker: Tri canonique (created_at ASC, event_id ASC)<br/>(INV-55-13)
Worker->>Merkle: Calcul racine Merkle<br/>(RFC 8785 + SHA-256, INV-55-05)
Merkle-->>Worker: merkle_root (hex64)
Worker->>DB: UPDATE lot → MERKLE_COMPUTED
Worker->>Contract: anchorRoot(merkle_root)<br/>(empreinte seule, INV-55-01)
Contract->>Chain: Transaction on-chain
Chain-->>Contract: tx_id (0x + 64 hex)
Contract-->>Worker: tx_id + block_number
Worker->>DB: UPDATE lot → TX_SUBMITTED (tx_id, chain_id)
loop Attente finalité (≥30 confirmations OU ≥5min)
Worker->>Chain: Vérification confirmations
Chain-->>Worker: Nombre de confirmations
end
Worker->>DB: UPDATE lot → FINALIZED<br/>(INV-55-04 immutable)
Worker->>DB: UPDATE événements → ANCHORED<br/>(lien lot/racine/tx_id, INV-55-03)
Worker->>DB: INSERT journal audit cycle<br/>(horodatages, volumes, statuts, INV-55-10)
end 5bis.4 Diagramme de séquence — Vérification externe¶
La preuve est vérifiable sans accès aux systèmes internes (INV-55-08), à partir de l'artefact JSON exporté et des données publiques blockchain.
sequenceDiagram
autonumber
participant Auditor as Auditeur externe
participant Artifact as Artefact JSON<br/>(§8.1)
participant Chain as Blockchain publique
participant Verify as Recalcul local
Auditor->>Artifact: Lecture artefact exporté<br/>(lot_id, merkle_root, tx_id, events[])
Auditor->>Chain: GET transaction par tx_id<br/>(données publiques)
Chain-->>Auditor: Payload on-chain (merkle_root, block, status)
Auditor->>Auditor: Comparer merkle_root artefact vs on-chain<br/>(doivent être identiques)
loop Pour chaque événement
Auditor->>Verify: Recalcul Merkle proof<br/>(event_hash + merkle_proof → racine)
Verify-->>Auditor: Racine recalculée
Auditor->>Auditor: Vérifier racine recalculée == merkle_root<br/>(INV-55-05, INV-55-13)
end
Note over Auditor: Vérification complète sans accès<br/>aux systèmes internes (INV-55-08) 6. Cas d'erreur¶
- ERR-55-01 — Aucun événement éligible : cycle en no-op ; journal d'audit obligatoire ; aucun échec.
- ERR-55-02 — Échec de calcul racine : lot non finalisé ; aucun événement marqué ancré ; ré-exécution possible sans perte.
- ERR-55-03 — Rejet transaction on-chain : lot en échec ; événements conservés non ancrés ; cause traçable.
- ERR-55-04 —
tx_idinvalide ou absent : lot non finalisable ; contrôle d'intégrité échoue ; alerte d'audit. - ERR-55-05 — Finalité non atteinte avant timeout contractuel : lot en état intermédiaire explicite ; pas de marquage ancré final ; durée maximale de l'état intermédiaire = 1 heure avant escalade opérationnelle obligatoire. Mécanisme d'escalade : le compteur démarre à la soumission on-chain (
tx_submitted_at) ; à T+1h sans finalité, alerte automatique PagerDuty niveau WARNING avec contexte (lot_id,tx_id,chain_id,block_number, durée attente) ; si T+2h sans finalité, escalade CRITICAL avec notification équipe SRE. - ERR-55-06 — Tentative de double rattachement d'un événement : rejet explicite ; invariant INV-55-02 prioritaire.
- ERR-55-07 — Indisponibilité blockchain prolongée : accumulation contrôlée des événements éligibles ; application de la stratégie de continuité documentée ; aucune suppression.
- ERR-55-08 — Écart de fenêtre temporelle (trou détecté) : alerte critique et journal d'anomalie ; lot concerné non validé tant que non résolu.
- ERR-55-09 — Échec de persistance d'un lien événement↔lot : transaction logique du cycle considérée non conforme ; pas de finalisation partielle silencieuse.
- ERR-55-10 — Entrée non conforme (format/range/état) : rejet avant traitement ; journal d'audit de validation.
7. Critères d'acceptation (testables)¶
| ID | Critère | Observable |
|---|---|---|
| CA-55-01 | Un cycle périodique est déclenché selon la cadence définie avec une tolérance explicite de ±2 minutes (cycle entre 8 et 12 minutes). | Horodatages successifs de cycles dans les logs d'audit. |
| CA-55-02 | Un événement éligible apparaît dans exactement un lot finalisé. | Requête de traçabilité : cardinalité = 1. |
| CA-55-03 | Aucun contenu métier en clair n'est publié on-chain. | Inspection payload transaction : empreinte seule. |
| CA-55-04 | Pour un lot finalisé, les champs lot/racine/tx_id/horodatage/finalité sont tous présents. | Contrôle de complétude des enregistrements. |
| CA-55-05 | Deux recalculs de racine sur le même lot ordonné produisent la même valeur. | Test de déterminisme (égalité stricte). |
| CA-55-06 | En cas d'échec publication, les événements restent non ancrés et rééligibles. | État post-erreur + réexécution réussie. |
| CA-55-07 | En absence d'événement éligible, un enregistrement no-op est produit. | Log d'audit no-op horodaté. |
| CA-55-08 | Depuis un tx_id finalisé, on peut retrouver tous les événements couverts. | Requête inverse transaction → événements. |
| CA-55-09 | Toute tentative de double ancrage d'un même événement est rejetée. | Erreur explicite + absence de second rattachement. |
| CA-55-10 | Aucune fenêtre éligible n'est laissée non couverte sans alerte. | Rapport de continuité temporelle sans trou silencieux. |
| CA-55-11 | Les validations d'entrée rejettent toute valeur hors format attendu (tx_id, états, IDs). | Cas de test négatifs avec rejet explicite. |
| CA-55-12 | La stratégie de continuité chaîne est documentée et exécutable en test de procédure. | Preuve documentaire + résultat d'exercice. |
8. Modèle de données et formats¶
8.1 Format contractuel de l'artefact de preuve externe (JSON)¶
L'artefact exportable DOIT être sérialisé en UTF-8 et respecter le schéma minimal suivant :
{
"version": "1.0",
"lot_id": "uuid",
"merkle_root": "hex64",
"tx_id": "0x...",
"chain_id": 137,
"block_number": 12345678,
"events": [
{
"event_id": "uuid",
"event_hash": "hex64",
"merkle_index": 0,
"merkle_proof": ["hex64", "hex64"]
}
]
}
8.2 Contraintes de format¶
version: chaîne, valeur normative initiale1.0.lot_id: UUID v4 canonical (8-4-4-4-12, hex lowercase).merkle_root:hex64lowercase sans préfixe0x(64 caractères hex).tx_id: hash de transaction EVM, format0x+ 64 hex lowercase (66 caractères).chain_id: entier positif (EIP-155).block_number: entier positif (>= 0).events: tableau non vide pour un lot non no-op.event_id: UUID canonical.event_hash:hex64lowercase sans préfixe0x.merkle_index: entier>= 0, position de la feuille dans l'ordre canonique du lot.merkle_proof: tableau ordonné de siblings (hex64) du niveau feuille vers racine ; chaque élément représente le hash sibling au niveau correspondant.
8.3 Règles de vérification externe¶
La vérification externe DOIT pouvoir être exécutée avec : 1. l'artefact JSON exporté, 2. les données publiques blockchain (tx_id, chain_id, block_number), 3. le recalcul Merkle déterministe conforme à l'ordre canonique (INV-55-13) et à la canonicalisation RFC 8785 (INV-55-05).
9. Contraintes techniques¶
- Taille maximale d'un lot : 10 000 événements.
- Backlog maximal acceptable : 100 000 événements non ancrés.
- SLA de résorption backlog : retour sous backlog nominal en 24 heures maximum après restauration de la chaîne cible.
9.1 Conduite au dépassement des seuils¶
| Seuil | Condition | Action |
|---|---|---|
| Lot > 10 000 événements | Fenêtre contient plus de 10K événements éligibles | Découpage automatique en sous-lots de 10K max ; chaque sous-lot est ancré séparément avec traçabilité du lot parent. |
| Backlog > 100 000 événements | Accumulation dépassant le seuil acceptable | Alerte critique (PagerDuty niveau CRITICAL) ; passage en mode dégradé : priorisation des événements les plus anciens ; blocage de l'ingestion de nouveaux événements si backlog > 150K (circuit breaker). |
| SLA 24h dépassé | Backlog non résorbé après 24h post-restauration chaîne | Escalade opérationnelle obligatoire ; rapport d'incident ; intervention manuelle requise pour purge ou rattrapage accéléré. |
10. Scénarios de test (Given / When / Then)¶
- Nominal avec événements
- Given un ensemble d'événements éligibles non ancrés dans la fenêtre
- When le cycle périodique s'exécute et la transaction atteint la finalité
-
Then un lot finalisé unique est créé, chaque événement est marqué ancré, et
tx_idest traçable -
Nominal sans événements (no-op)
- Given aucune donnée éligible dans la fenêtre
- When le cycle s'exécute
-
Then aucun lot finalisé n'est créé et une trace no-op est journalisée
-
Idempotence événement
- Given un événement déjà rattaché à un lot finalisé
- When un nouveau cycle tente de le reprendre
-
Then le rattachement est refusé et aucun second ancrage n'est produit
-
Échec publication blockchain
- Given un lot constitué et une publication rejetée par la chaîne
- When le cycle termine en erreur
-
Then le lot n'est pas finalisé, les événements restent rééligibles, et l'erreur est auditée
-
Finalité retardée
- Given une transaction soumise avec
tx_idvalide mais finalité non atteinte - When le délai maximal de confirmation est dépassé
-
Then le lot reste en état intermédiaire explicite sans marquage final des événements
-
Déterminisme racine
- Given un lot figé et ordonné
- When la racine est recalculée deux fois
-
Then la valeur de racine est strictement identique
-
Traçabilité inverse
- Given un
tx_idd'un lot finalisé - When une vérification d'audit externe est demandée
-
Then la liste des événements couverts est retrouvable et cohérente avec la racine
-
Validation stricte des entrées
- Given des entrées malformées (ID, statut,
tx_id) - When elles sont soumises au worker
-
Then elles sont rejetées avant traitement et consignées comme invalides
-
Continuité temporelle
- Given plusieurs fenêtres successives avec flux continu d'événements
- When les cycles s'enchaînent sur une période prolongée
-
Then aucun trou de couverture n'est détecté sans alerte explicite
-
Indisponibilité chaîne prolongée
- Given la chaîne cible indisponible sur une durée étendue
- When les cycles continuent de s'exécuter
- Then les événements s'accumulent sans perte et la procédure de continuité est déclenchée/auditée
11. Hypothèses explicites¶
| ID | Hypothèse | Impact si faux |
|---|---|---|
| HYP-55-01 | Les dépendances PD-52/PD-53/PD-237 sont opérationnelles et conformes à leurs contrats. | Blocage fonctionnel : ancrage impossible ou non fiable. |
| HYP-55-02 | Une horloge de référence cohérente est disponible pour dater fenêtres et cycles. | Risque de trous/superpositions temporelles. |
| HYP-55-03 | Le statut de finalité blockchain est observable de manière fiable. | Impossibilité de conclure l'état final des lots. |
| HYP-55-04 | Le schéma de persistance supporte les liens événement↔lot↔transaction en append-only. | Traçabilité incomplète ou mutable. |
| HYP-55-06 | La politique de rétention des journaux d'audit couvre les besoins de preuve externe. | Auditabilité affaiblie. |
12. Points à clarifier¶
- Chaîne cible normative de la V1 : réseau exact (ex. Polygon Amoy/Mainnet), niveau d'environnement, règles de bascule manuelle.
- Politique de lot vide : no-op uniquement ou ancrage "heartbeat" autorisé/interdit.
- Stratégie de continuité non-automatique : déclencheurs, rôles, délais, critères de reprise.
- Seuils d'alerte sécurité/ops : délais, taux d'échec, criticité.
Références¶
- Epic : PD-187 (BLOCKCHAIN)
- JIRA : PD-55
- Repos concernés : ProbatioVault-backend
- Documents associés :
- PD-55 — Expression de besoin
- PD-52 — Ethereum L2 setup (DONE)
- PD-53 — Smart contract Merkle anchor (DONE)
- PD-237 — Merkle persistence (DONE)
- Learnings :
- PD-44 (validation stricte des entrées / sécurité)
- PD-40 (append-only, invariants numérotés, canonicalisation)
- BATCH-RETRO (WORM/Object Lock : irréversibilité et traçabilité)