Aller au contenu

PD-279 — Restitution ISO 14641 d’un document archivé (état RESTITUTED)

1. Objectif

Spécifier contractuellement l’ajout du statut RESTITUTED et des transitions de restitution d’un document archivé, afin d’aligner le backend avec ISO 14641 §11.3 et de satisfaire l’ASSUME TLA+ AnchorAssume_States, sans ambiguïté fonctionnelle, temporelle, ni de traçabilité.

2. Périmètre / Hors périmètre

Inclus

  • Ajout du statut RESTITUTED au cycle de vie documentaire.
  • Transition SEALED -> RESTITUTED via POST /documents/:id/restitute.
  • Transition retour RESTITUTED -> SEALED via POST /documents/:id/return-from-restitution.
  • Vérification des gardes métier: ownership, legal_lock=false, geo_copy_count >= 2, état source valide.
  • Journalisation obligatoire des transitions dans lifecycle_log et journal d’audit.
  • SLA de restitution: durée max configurable, calcul de deadline, alerte à 80%, escalade à 100%, événement RESTITUTION_OVERDUE après dépassement.
  • Interdiction de destruction d’un document RESTITUTED (rejet HTTP 409 côté module destruction, contrôle à l’inclusion et à l’exécution des batches).
  • Migration BD réversible (up/down), incluant extension d’enum + champs temporels.

Exclu

  • Formalisation complète de l’état DISPOSED (STUB planifié en PD-280, hors de cette story).
  • NF Z42-013 DIP (PD-278).
  • Restitution multi-destinataire.
  • Refonte des flux hors restitution (capture, sealing, expiration), sauf impacts explicitement listés ici.

3. Définitions

  • DocumentStatus: statut métier du document (PENDING, SEALED, RESTITUTED, EXPIRED).
  • RESTITUTED: document archivé restitué temporairement à son originateur.
  • Originateur / Owner: utilisateur propriétaire du document.
  • legal_lock: verrou juridique interdisant les actions de restitution.
  • geo_copy_count: nombre de copies géo-distribuées validées.
  • lifecycle_log: registre d’attestations de transitions d’état.
  • JournalEventType: type d’événement d’audit applicatif.
  • SLA restitution: durée maximale autorisée entre restitution et retour en archivage.
  • Deadline restitution: restituted_at + restitution_max_duration.
  • État terminal (dans ce périmètre): état sans transition sortante autorisée par cette spécification.

4. Invariants (non négociables)

ID Règle Justification
INV-279-01-state-model Le statut RESTITUTED DOIT exister dans DocumentStatus et être persistant. Alignement modèle TLA+ / implémentation.
INV-279-02-restitute-guards SEALED -> RESTITUTED autorisée uniquement si: owner, legal_lock=false, geo_copy_count >= 2, état courant SEALED. Cette contrainte geo_copy_count >= 2 est une GARDE de l’entrée en restitution uniquement. ISO 14641 §11.3 et §5.5.8.
INV-279-03-return-guards RESTITUTED -> SEALED autorisée uniquement si owner et état courant RESTITUTED; aucun contrôle geo_copy_count n’est requis pour ce retour. Le document n’a pas quitté le stockage d’archive pendant la restitution.
INV-279-04-traceability Chaque transition de restitution DOIT créer une attestation lifecycle_log horodatée UTC + acteur + type d’événement + security_level, et un log d’audit. Exigence de traçabilité complète.
INV-279-05-sla Tout document RESTITUTED DOIT porter restituted_at et restitution_deadline; alerte à 80%, escalade à 100%, puis événement RESTITUTION_OVERDUE si deadline dépassée. Gouvernance temporelle et conformité.
INV-279-06-no-destruction-while-restituted Toute tentative de destruction d’un document RESTITUTED DOIT être rejetée avec HTTP 409 Conflict et error_code=DOCUMENT_RESTITUTED_DESTRUCTION_FORBIDDEN. Cohérence cycle de vie ISO.
INV-279-07-transition-matrix Les transitions sortantes de chaque état du périmètre DOIVENT être explicitement autorisées ou interdites; aucune transition implicite. Exigence anti-ambiguïté de machine à états.
INV-279-08-ddl-reversible La migration de schéma DOIT être réversible sans perte de cohérence structurelle (up/down). Robustesse opérationnelle.
INV-279-09-atomicity Une transition d’état et l’attestation associée DOIVENT être atomiques (même transaction DB). Les traitements asynchrones SLA sont post-commit, idempotents et rattrapables. Cohérence en cas d’incident.
INV-279-10-cross-module-guard Le module destruction DOIT appliquer une contrainte cross-module interdisant la destruction d’un document en état RESTITUTED (soft-delete PD-250 inclus). Contrainte inter-modules explicite.
INV-279-11-destruction-checkpoint-uniqueness Le module destruction DOIT vérifier le statut du document AVANT toute inclusion dans un batch (POST /destruction/batches) et AVANT exécution (POST /destruction/batches/:id/execute). Un document RESTITUTED DOIT être rejeté à l’inclusion avec HTTP 409. Empêche tout contournement par chemin batch.
INV-279-12-expired-terminal EXPIRED est un état terminal strict dans ce périmètre: aucune transition sortante autorisée. Levée d’ambiguïté de machine à états.

5. Flux nominaux

5.1 Modèle de données (formats et contraintes contractuelles)

Définitions centralisées; les sections 5.2, 6, 7, 8 y font référence sans redéfinition.

Donnée Format / encodage Taille / longueur Jeu caractères Sensibilité casse Validation Comportement si invalide
document_id (path :id) UUID v4 canonique texte 36 caractères [0-9a-f-] case-insensitive sur hex, format canonical recommandé ^[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}$ HTTP 400
status Enum string 1 valeur parmi 4 A-Z_ case-sensitive appartenance enum stricte HTTP 409 si transition interdite
legal_lock Booléen 1 bit logique n/a n/a {true,false} HTTP 423 si true pour restitution
geo_copy_count Entier non signé 0..2147483647 chiffres n/a entier >= 0 HTTP 409 si <2
restituted_at Timestamp UTC RFC3339/ISO8601 précision seconde min UTF-8 n/a datetime valide UTC rejet transaction (400/500 selon source)
restitution_deadline Timestamp UTC RFC3339/ISO8601 précision seconde min UTF-8 n/a deadline = restituted_at + SLA rejet transaction si non calculable
lifecycle_log.type Enum string valeurs: RESTITUTION, RETURN_FROM_RESTITUTION A-Z_ case-sensitive appartenance enum stricte rejet transaction
actor_id UUID v4 canonique texte 36 caractères [0-9a-f-] idem document_id regex UUID v4 rejet transaction
security_level Enum de sécurité existant (contrat module documents) selon contrat existant selon contrat existant selon contrat existant validation par contrat existant rejet transaction

Bornes numériques obligatoires

Paramètre Défaut Min Max Unité Percentile / Référence Hors bornes
geo_copy_count pour restitution n/a (mesure observée) 2 (pour autoriser) 2147483647 copies n/a <2 => HTTP 409 + INSUFFICIENT_GEO_COPIES
restitution_max_duration 30 1 30 jours n/a config invalide => rejet au chargement config
Seuil alerte SLA 80 1 99 % n/a hors bornes => rejet config
Seuil escalade SLA 100 100 100 % n/a toute autre valeur interdite

5.2 Machine à états (transitions explicites, incluant retours)

Portée: modèle documentaire concerné par restitution (PENDING, SEALED, RESTITUTED, EXPIRED).

  • PENDING:
  • PENDING -> RESTITUTED: INTERDITE (HTTP 409 + INVALID_SOURCE_STATE).
  • PENDING -> *: INTERDITE dans ce périmètre (transitions hors restitution inchangées, hors périmètre de cette story).
  • SEALED:
  • SEALED -> RESTITUTED: AUTORISÉE si INV-279-02.
  • SEALED -> SEALED: INTERDITE (hors idempotence endpoint retour, cf. §5.4).
  • SEALED -> EXPIRED et autres: hors périmètre, inchangé par cette story.
  • RESTITUTED:
  • RESTITUTED -> SEALED: AUTORISÉE si INV-279-03.
  • RESTITUTED -> document détruit (soft-delete via module destruction PD-250): INTERDITE (INV-279-06). L’état DISPOSED est un STUB traité en PD-280, hors périmètre.
  • RESTITUTED -> RESTITUTED: AUTORISÉE UNIQUEMENT en sémantique idempotente API (retour HTTP 200 sans nouvelle attestation, cf. §5.3).
  • RESTITUTED -> PENDING/EXPIRED: INTERDITE dans ce périmètre.
  • EXPIRED (terminal strict):
  • EXPIRED -> *: INTERDITE (état terminal, aucune transition sortante autorisée).

Comportement downgrade/retour RESTITUTED -> SEALED: - Données de preuve (lifecycle_log, audit) conservées. - SLA restitution clos (plus d’alerte/escalade active sur la fenêtre terminée). - Protections d’archive SEALED réappliquées immédiatement.

5.3 Flux nominal A — Restitution (POST /documents/:id/restitute)

  1. Le système valide document_id.
  2. Le système applique l’ordre de gardes normatif (§6.1): authentification, ownership, état, legal lock, copies géo.
  3. Si le document est déjà RESTITUTED, le endpoint est idempotent: HTTP 200 avec le même body métier, sans nouvelle attestation.
  4. Si le document est SEALED et toutes gardes satisfaites, le système calcule restituted_at=now_utc et restitution_deadline=restituted_at + restitution_max_duration.
  5. Le système persiste atomiquement:
  6. status=RESTITUTED
  7. restituted_at
  8. restitution_deadline
  9. attestation lifecycle_log(type=RESTITUTION, actor_id, security_level, timestamp_utc).
  10. Le système émet l’événement d’audit correspondant.
  11. Réponse HTTP 200.

5.4 Flux nominal B — Retour de restitution (POST /documents/:id/return-from-restitution)

  1. Le système valide document_id.
  2. Le système applique l’ordre de gardes normatif (§6.1), puis vérifie ownership et état source.
  3. Si le document est déjà SEALED suite à un retour antérieur, le endpoint est idempotent: HTTP 200 avec le même body métier, sans nouvelle attestation.
  4. Si le document est RESTITUTED et l’appelant owner, le système persiste atomiquement:
  5. status=SEALED
  6. attestation lifecycle_log(type=RETURN_FROM_RESTITUTION, actor_id, security_level, timestamp_utc).
  7. Le système n’applique pas de garde geo_copy_count sur ce retour.
  8. Le système émet l’événement d’audit correspondant.
  9. Réponse HTTP 200.

5.5 Flux nominal C — Contrôle destruction cross-module

  1. Le module destruction reçoit une demande de destruction sur l’une des routes protégées:
  2. POST /destruction/batches (inclusion)
  3. POST /destruction/batches/:id/execute (exécution)
  4. DELETE /documents/:id (soft-delete direct)
  5. Le système lit le statut du document cible AVANT inclusion puis AVANT exécution.
  6. Si status=RESTITUTED, la destruction est refusée immédiatement.
  7. Réponse HTTP 409 avec error_code=DOCUMENT_RESTITUTED_DESTRUCTION_FORBIDDEN.
  8. Événement d’audit de refus enregistré.

5.6 SLA temporels (checklist complète)

Élément Valeur
Valeur par défaut restitution_max_duration = 30 jours
Borne minimale 1 jour
Borne maximale 30 jours
Configurabilité Oui, paramètre de configuration service (runtime)
Expiration à 80% Alerte opérationnelle obligatoire
Expiration à 100% Escalade obligatoire + événement audit
Comportement post-100% Émission obligatoire de l’événement RESTITUTION_OVERDUE; aucune transition automatique
Décision métier post-100% Intervention manuelle du PO requise; le document reste RESTITUTED jusqu’à action explicite

5.7 Stratégie de migration DDL (modification colonne/enum existant)

Élément Spécification
Type actuel / cible documents.status: enum existant (PENDING,SEALED,EXPIRED) -> enum étendu avec RESTITUTED
Colonnes ajoutées restituted_at (timestamp UTC nullable), restitution_deadline (timestamp UTC nullable)
Backfill Aucun backfill massif requis; documents déjà SEALED restent sans valeurs tant qu’aucune restitution n’a eu lieu
Impact triggers existants Doit rester compatible avec triggers de traçabilité/immutabilité; aucun trigger ne doit empêcher l’écriture atomique des attestations
Impact workers/services Workers SLA doivent supporter enregistrement tardif/retry; modules lisant status doivent accepter la nouvelle valeur RESTITUTED
Down migration Retrait des colonnes ajoutées + suppression de la valeur enum selon stratégie DB sûre et réversible contractuelle. Précondition obligatoire: tous les documents RESTITUTED DOIVENT avoir été retournés (SEALED) avant migration descendante.
Contraintes ajoutées restitution_deadline >= restituted_at quand les deux sont non null; cohérence transactionnelle obligatoire

5.8 Atomicité multi-composant

Scope Sync/Async Garantie
MAJ document + insertion lifecycle_log Synchrone (transaction DB) Atomicité ACID
Événement audit applicatif Post-commit At-least-once, idempotent
Alerte 80% / escalade 100% SLA / événement RESTITUTION_OVERDUE Async (job planifié) Retry-safe, rattrapage sur redémarrage
Crash pré-commit n/a rollback total, aucun artefact persistant
Crash post-commit n/a état DB valide, traitements async rattrapés

5.9 Contraintes inter-modules

Élément Spécification
Routes d’autre module à protéger POST /destruction/batches, POST /destruction/batches/:id/execute, DELETE /documents/:id (module destruction PD-250)
Mécanisme de protection Vérification du DocumentStatus avant inclusion dans batch et avant destruction
Données cross-module nécessaires document.status exposé au module destruction
Résolution FK/liaison Identifiant document métier (document_id) selon contrat module documents
Scope d’enregistrement Contrôle appliqué au niveau service/guard du module destruction (pas de règle globale transverse implicite)
Exceptions d’accès Aucune exception: même rôle privilégié ne peut détruire un RESTITUTED dans ce périmètre

6. Cas d’erreur

6.1 Ordre normatif d’évaluation des gardes

  1. Authentification -> 401 Unauthorized
  2. Autorisation/ownership -> 403 Forbidden
  3. État du document (source invalide) -> 409 Conflict + error_code=INVALID_SOURCE_STATE
  4. Legal lock actif -> 423 Locked + error_code=DOCUMENT_UNDER_LEGAL_LOCK
  5. Copies géo insuffisantes -> 409 Conflict + error_code=INSUFFICIENT_GEO_COPIES

Cet ordre est obligatoire et stable pour tous les endpoints de restitution/retour.

6.2 Mapping HTTP / error_code

  • 400 Bad Request: document_id invalide (format non conforme §5.1).
  • 401 Unauthorized: appelant non authentifié.
  • 403 Forbidden: appelant authentifié mais non owner.
  • 404 Not Found: document inexistant.
  • 409 Conflict + INVALID_SOURCE_STATE: état courant incompatible avec la transition demandée (PENDING/EXPIRED vers restitution, état non attendu pour retour).
  • 409 Conflict + INSUFFICIENT_GEO_COPIES: geo_copy_count < 2 lors de SEALED -> RESTITUTED.
  • 409 Conflict + DOCUMENT_RESTITUTED_DESTRUCTION_FORBIDDEN: tentative de destruction d’un document RESTITUTED.
  • 423 Locked + DOCUMENT_UNDER_LEGAL_LOCK: legal_lock=true lors de restitution.
  • 422 Unprocessable Entity ou échec de validation config: bornes SLA invalides (hors [1j,30j]).
  • 500 Internal Server Error: échec transactionnel non métier (avec rollback garanti).

7. Critères d’acceptation (testables)

ID Critère Observable
CA-279-01 DocumentStatus contient RESTITUTED. Enum persistée et lisible en runtime.
CA-279-02 Migration DB up/down est exécutable et réversible. Exécution CI de migration aller/retour sans erreur, avec précondition de retour des documents RESTITUTED avant down.
CA-279-03 POST /documents/:id/restitute renvoie 200 si toutes gardes OK. Réponse 200 + status final RESTITUTED.
CA-279-04 POST /documents/:id/restitute renvoie erreur normalisée sur garde violée. 409 INVALID_SOURCE_STATE, 423 DOCUMENT_UNDER_LEGAL_LOCK, 409 INSUFFICIENT_GEO_COPIES.
CA-279-05 POST /documents/:id/return-from-restitution renvoie 200 si gardes OK. Réponse 200 + status final SEALED.
CA-279-06 POST /documents/:id/return-from-restitution renvoie 409 INVALID_SOURCE_STATE si état incompatible. Réponse 409.
CA-279-07 Chaque transition crée une attestation lifecycle_log complète. Entrée avec timestamp UTC, actor_id, type, security_level.
CA-279-08 Module destruction rejette RESTITUTED avec 409 au point d’inclusion et d’exécution. Réponse 409 + audit de refus sur POST /destruction/batches et POST /destruction/batches/:id/execute.
CA-279-09 restitution_deadline est calculée exactement à partir de restituted_at + SLA. Écart nul à la seconde près.
CA-279-10 Alerte à 80%, escalade à 100% et RESTITUTION_OVERDUE post-deadline sont produites. Trois événements observables distincts.
CA-279-11 L’ASSUME TLA+ AnchorAssume_States passe. Exécution de vérification formelle: PASS.
CA-279-12 Utilisateur non propriétaire ne peut ni restituer ni retourner. Réponse 403 sur les deux endpoints.
CA-279-13 Idempotence des endpoints REST est respectée. Rejeu de POST /documents/:id/restitute sur document déjà RESTITUTED -> 200 même body sans nouvelle attestation; rejeu de POST /documents/:id/return-from-restitution sur document déjà SEALED (retour déjà effectué) -> 200 même body sans nouvelle attestation.

8. Scénarios de test (Given / When / Then)

  • ST-279-01 Restitution nominale
  • Given un document SEALED, owner authentifié, geo_copy_count=2, legal_lock=false
  • When POST /documents/:id/restitute
  • Then HTTP 200, statut RESTITUTED, restituted_at et restitution_deadline renseignés, attestation RESTITUTION créée.

  • ST-279-02 Restitution refusée copies insuffisantes

  • Given un document SEALED avec geo_copy_count=1
  • When POST /documents/:id/restitute
  • Then HTTP 409 + INSUFFICIENT_GEO_COPIES, statut inchangé, aucune attestation de transition.

  • ST-279-03 Restitution refusée legal lock

  • Given un document SEALED avec legal_lock=true
  • When POST /documents/:id/restitute
  • Then HTTP 423 + DOCUMENT_UNDER_LEGAL_LOCK, statut inchangé, audit de refus.

  • ST-279-04 Restitution refusée non owner

  • Given un document SEALED appartenant à un autre utilisateur
  • When POST /documents/:id/restitute
  • Then HTTP 403.

  • ST-279-05 Restitution refusée état source invalide

  • Given un document PENDING (ou EXPIRED)
  • When POST /documents/:id/restitute
  • Then HTTP 409 + INVALID_SOURCE_STATE.

  • ST-279-06 Retour nominal

  • Given un document RESTITUTED dont l’appelant est owner
  • When POST /documents/:id/return-from-restitution
  • Then HTTP 200, statut SEALED, attestation RETURN_FROM_RESTITUTION créée.

  • ST-279-07 Retour refusé mauvais état

  • Given un document PENDING ou EXPIRED
  • When POST /documents/:id/return-from-restitution
  • Then HTTP 409 + INVALID_SOURCE_STATE.

  • ST-279-08 Interdiction destruction à l’inclusion batch

  • Given un document RESTITUTED
  • When appel POST /destruction/batches pour inclusion
  • Then HTTP 409 + DOCUMENT_RESTITUTED_DESTRUCTION_FORBIDDEN, aucune inclusion effective.

  • ST-279-09 Interdiction destruction à l’exécution batch

  • Given un batch contenant une référence devenue RESTITUTED avant exécution
  • When appel POST /destruction/batches/:id/execute
  • Then HTTP 409 + DOCUMENT_RESTITUTED_DESTRUCTION_FORBIDDEN, aucune destruction effective.

  • ST-279-10 Interdiction destruction direct soft-delete

  • Given un document RESTITUTED
  • When appel DELETE /documents/:id
  • Then HTTP 409 + DOCUMENT_RESTITUTED_DESTRUCTION_FORBIDDEN.

  • ST-279-11 SLA événements

  • Given un document RESTITUTED avec SLA configuré à 30 jours
  • When le temps atteint 24 jours (80%), puis 30 jours (100%), puis dépasse la deadline
  • Then une alerte 80%, une escalade 100%, puis RESTITUTION_OVERDUE sont émises et tracées, sans transition automatique d’état.

  • ST-279-12 Idempotence restitution

  • Given un document déjà RESTITUTED
  • When POST /documents/:id/restitute est rejoué
  • Then HTTP 200 avec même body, aucune nouvelle attestation.

  • ST-279-13 Idempotence retour

  • Given un document déjà SEALED suite à un retour de restitution
  • When POST /documents/:id/return-from-restitution est rejoué
  • Then HTTP 200 avec même body, aucune nouvelle attestation.

  • ST-279-14 Vérification TLA+

  • Given code et modèle mis à jour
  • When exécution de la vérification AnchorAssume_States
  • Then résultat PASS.

9. Hypothèses explicites

ID Hypothèse Impact si faux
H-279-01 document_id est un UUID v4 (contrat API existant). Adapter regex/validation et tests d’API.
H-279-02 Le module destruction applique les contrôles de statut aux deux points (inclusion + exécution) et sur la route soft-delete direct. Si faux: risque de contournement, NON CONFORME à INV-279-11.
H-279-03 security_level est défini dans le contrat documentaire et disponible au moment du log, y compris pour RETURN_FROM_RESTITUTION. Si faux: compléter le modèle d’attestation avant livraison.
H-279-04 EXPIRED est terminal strict dans le périmètre restitution. Si faux: compléter la matrice de transitions et les tests retour.
H-279-05 Les jobs planifiés SLA supportent l’idempotence. Si faux: risque de doublons d’alertes/escalades/overdue.
H-279-06 Le STUB DISPOSED reste hors périmètre PD-279 et sera formalisé en PD-280. Si faux: revoir le modèle d’état global et les contrats cross-modules.

10. Contraintes techniques et points à clarifier

10.1 Contraintes techniques (stack réelle, obligatoire)

  • Projet cible: ProbatioVault-backend.
  • Stack contractuelle: NestJS + TypeORM + PostgreSQL.
  • Endpoints exposés via module documents backend.
  • Migration via mécanisme TypeORM du backend.
  • Aucune hypothèse frontend/mobile dans cette story.

10.2 Points à clarifier

ID Point à clarifier Donnée manquante / décision attendue
Q-279-01 Politique post-expiration SLA à 100% et post-deadline Décision figée en v2: escalade + RESTITUTION_OVERDUE, aucune transition automatique, action manuelle PO.
Q-279-02 Code d’erreur métier standardisé pour chaque 409/423 Décision figée en v2: INVALID_SOURCE_STATE, INSUFFICIENT_GEO_COPIES, DOCUMENT_RESTITUTED_DESTRUCTION_FORBIDDEN, DOCUMENT_UNDER_LEGAL_LOCK.
Q-279-03 Valeur/obligation du champ security_level pour RETURN_FROM_RESTITUTION Décision v2: obligatoire si contrat documents existant l’exige; même règle que RESTITUTION.
Q-279-04 Stratégie exacte de down migration enum PostgreSQL selon conventions repo Décision partielle: précondition métier obligatoire (aucun RESTITUTED restant) + séquence DDL conforme conventions DBA backend.
Q-279-05 Routes précises du module destruction concernées Décision figée en v2: POST /destruction/batches, POST /destruction/batches/:id/execute, DELETE /documents/:id.
Q-279-06 Test adversarial security_level hors contrat API Optionnel: classé test de robustesse non bloquant (MIN-02), hors contrat API nominal.

Références

  • Epic : PD-279-iso14641-restituted
  • JIRA : PD-279
  • Repos concernés : ProbatioVault-backend (principal), ProbatioVault-doc (normes/TLA+)
  • Documents associés :
  • ProbatioVault-doc/docs/normes/iso-14641/formal/ISO_14641.tla
  • ISO 14641 §11.3
  • ISO 14641 §5.5.8
  • PD-250 (destruction), PD-251 (intégrité), PD-81 (SLA temporels), PD-280 (STUB DISPOSED)