Aller au contenu

API Reference — ProbatioVault Backend

Version: 1.0.0 Base URL: http://localhost:3000 (développement) API Prefix: /v1 (sauf health check) Documentation Swagger: http://localhost:3000/api/docs (développement uniquement)


Table des matières

  1. Authentification
  2. Health & Monitoring
  3. Authentification SRP-6a
  4. Purge des comptes
  5. Documents sécurisés
  6. Upload multipart
  7. Cryptographie - Key Envelopes
  8. Audit & Preuves probatoires
  9. Jobs asynchrones
  10. Synchronisation multi-device
  11. Codes d'erreur

Authentification

La plupart des endpoints nécessitent un token JWT obtenu via le flow d'authentification SRP-6a.

Header requis

Authorization: Bearer <access_token>

Exceptions (endpoints publics)

  • /health
  • /
  • /v1/auth/srp-params
  • /v1/auth/register
  • /v1/auth/validate-email
  • /v1/auth/login/challenge
  • /v1/auth/login/verify

Health & Monitoring

GET /health

Health check pour monitoring infrastructure.

Authentication: Aucune

Response: 200 OK

{
  "status": "ok",
  "timestamp": "2025-01-02T10:30:00.000Z"
}

GET /

Informations sur l'API.

Authentication: Aucune

Response: 200 OK

{
  "name": "ProbatioVault Backend API",
  "version": "1.0.0",
  "description": "API REST pour coffre-fort numérique probatoire"
}

GET /v1/health/redis

Vérification santé Redis (connexion, TLS, AUTH, mémoire).

Authentication: Aucune

Response: 200 OK

{
  "status": "healthy",
  "connected": true,
  "tlsEnabled": true,
  "authEnabled": true,
  "memoryUsage": {
    "used": "10MB",
    "peak": "15MB"
  }
}

Référence: PD-21 - Utilisé par Flow Prefect redis_health_flow.py


GET /v1/health/redis/status

Statut Redis en cache (sans ping réseau).

Authentication: Aucune

Response: 200 OK


Authentification SRP-6a

Architecture Zero-Knowledge : le mot de passe n'est jamais envoyé au serveur.

Référence: PD-23, PD-24


GET /v1/auth/srp-params

Récupère les paramètres publics SRP-6a (N, g) nécessaires pour les calculs client.

Authentication: Aucune

Response: 200 OK

{
  "N": "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1...",
  "g": "2"
}

Notes: - N: Prime 3072 bits (groupe RFC 5054) - g: Générateur = 2


POST /v1/auth/register

Inscription utilisateur Zero-Knowledge.

Authentication: Aucune Guards: ForbidPasswordGuard, RateLimitGuard

Request Body:

{
  "email": "user@example.com",
  "salt": "a1b2c3d4e5f6...",
  "verifier": "f6e5d4c3b2a1..."
}

Response: 200 OK

{
  "status": "OK"
}

Important: - Le client génère salt et verifier localement - Formule: verifier = g^x mod Nx = SHA3-256(salt || K_auth) - K_auth = Argon2id(password, salt, "ProbatioVault_SRP_Auth_v1") - Anti-énumération: Réponse identique si email existe déjà (INV-8)

Erreurs: - 400: Payload invalide ou champ password présent (rejeté par guard) - 429: Rate limit dépassé


POST /v1/auth/validate-email

Validation email après inscription (lien reçu par email).

Authentication: Aucune

Request Body:

{
  "token": "550e8400-e29b-41d4-a716-446655440000"
}

Response: 200 OK

{
  "status": "OK"
}

Notes: - Token UUID v4 valide 1 heure (INV-10) - Transition: PENDING_VALIDATIONACTIVE - Anti-énumération: Réponse identique si token invalide


POST /v1/auth/login/challenge

Phase 1, Étape 1 : Initialisation du challenge SRP-6a.

Authentication: Aucune

Request Body:

{
  "email": "user@example.com",
  "A": "client_public_ephemeral_key_hex"
}

Response: 200 OK

{
  "salt": "a1b2c3d4e5f6...",
  "B": "server_public_ephemeral_key_hex"
}

Notes: - Le client génère (a, A)A = g^a mod N - Le serveur valide A mod N ≠ 0 - Le serveur génère (b, B) et retourne salt + B

Erreurs: - 400: Paramètre A invalide - 404: Utilisateur non trouvé


POST /v1/auth/login/verify

Phase 1, Étape 2 : Vérification de la preuve client et obtention du JWT.

Authentication: Aucune

Request Body:

{
  "email": "user@example.com",
  "M1": "client_proof_hex"
}

Response: 200 OK

{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "M2": "server_proof_hex"
}

Notes: - M1 = SHA3-256(A || B || K) (preuve client) - M2 = SHA3-256(A || M1 || K) (preuve serveur) - Le client doit vérifier M2 pour authentifier le serveur

Erreurs: - 401: Session SRP expirée ou preuve invalide


Purge des comptes

Endpoints internes pour la purge automatique des comptes non validés.

Référence: PD-23 INV-10, INV-12


POST /v1/auth/purge/expired

Purge des comptes en état PENDING_VALIDATION dont le token a expiré.

Authentication: X-Internal-Api-Key (InternalApiGuard)

Response: 200 OK

{
  "purged": 5,
  "auditEventsPurged": 12,
  "status": "OK"
}

Notes: - Appelé automatiquement par Flow Prefect (cron horaire) - Fallback manuel possible par administrateur - Purge également les traces d'audit (conformité RGPD) - Supprime les emails non envoyés de l'outbox

Erreurs: - 401: Non authentifié - 403: Non autorisé (admin requis)


GET /v1/auth/purge/status

Statut du système de purge.

Authentication: X-Internal-Api-Key

Response: 200 OK

{
  "pendingPurgeCount": 3,
  "oldestExpiredAt": "2024-01-15T10:30:00.000Z"
}

Usage: - Monitoring healthcheck Prefect - Alerting si comptes en attente de purge


Documents sécurisés

Gestion des documents probatoires avec métadonnées chiffrées côté client.

Référence: PD-16 Architecture: RLS PostgreSQL pour isolation utilisateur


POST /v1/documents

Créer un nouveau document sécurisé (status=PENDING).

Authentication: JWT Bearer

Request Body:

{
  "encryptedMetadata": "base64_encoded_encrypted_metadata",
  "keywordDeterministic": ["hash1", "hash2"],
  "ovhPath": "550e8400-e29b-41d4-a716-446655440000/doc.enc"
}

Response: 201 Created

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "userId": "user_id",
  "encryptedMetadata": "...",
  "fileHash": "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a",
  "status": "PENDING",
  "createdAt": "2025-01-02T10:30:00.000Z"
}

Notes: - Métadonnées chiffrées AES-256-GCM côté client - PD-38 E-01: Le hash SHA3-256 est calculé côté serveur depuis le fichier S3 (garantie probatoire) - Le hash est vérifié pour détecter les doublons par utilisateur - RLS filtre automatiquement par user_id

Erreurs: - 400: Données invalides, contexte utilisateur manquant, ou fichier S3 introuvable - 409: Document avec ce hash déjà existant


GET /v1/documents

Lister tous les documents de l'utilisateur courant.

Authentication: JWT Bearer

Response: 200 OK

[
  {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "encryptedMetadata": "...",
    "status": "SEALED",
    "createdAt": "2025-01-02T10:30:00.000Z"
  }
]

Notes: - RLS filtre automatiquement par user_id


GET /v1/documents/:id

Récupérer un document par son ID.

Authentication: JWT Bearer

Parameters: - id (UUID): ID du document

Response: 200 OK

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "userId": "user_id",
  "encryptedMetadata": "...",
  "fileHash": "...",
  "status": "SEALED",
  "createdAt": "2025-01-02T10:30:00.000Z"
}

Erreurs: - 404: Document non trouvé ou non accessible (RLS)


GET /v1/documents/:id/hash

Récupérer le hash probatoire SHA3-256 d'un document (PD-38).

Authentication: JWT Bearer

Parameters: - id (UUID): ID du document

Response: 200 OK

{
  "hash": "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a",
  "algorithm": "SHA3-256"
}

Notes: - Hash calculé sur le fichier chiffré (garantie probatoire) - Format: 64 caractères hexadécimaux (256 bits) - Utilisable pour vérification d'intégrité côté client

Erreurs: - 404: Document non trouvé ou non accessible (RLS)

Référence: PD-38


DELETE /v1/documents/:id

Supprimer un document.

Authentication: JWT Bearer

Parameters: - id (UUID): ID du document

Response: 204 No Content

Règles RLS: - PENDING: suppression autorisée par propriétaire - EXPIRED: suppression autorisée par propriétaire - SEALED: suppression interdite (sauf probatio_admin)

Erreurs: - 400: Contexte utilisateur manquant - 404: Document non trouvé


Upload multipart

Gestion des uploads multipart avec streaming direct vers S3.

Référence: PD-XX (Upload Service) Architecture: Streaming sans buffering (INV-2)


GET /v1/uploads/config

Récupérer la configuration upload (quotas, limites).

Authentication: JWT Bearer

Response: 200 OK

{
  "maxFileSize": 5368709120,
  "minPartSize": 5242880,
  "maxPartSize": 104857600,
  "maxParts": 1000,
  "sessionTTL": 86400,
  "allowedMimeTypes": ["application/pdf", "image/jpeg", "image/png"]
}

Notes: - Utilisé pour validation côté client - Audit CA12, CA14


POST /v1/uploads

Initialiser une nouvelle session d'upload multipart.

Authentication: JWT Bearer

Request Body:

{
  "fileName": "document.pdf",
  "fileSize": 10485760,
  "mimeType": "application/pdf",
  "totalParts": 2
}

Response: 201 Created

{
  "sessionId": "550e8400-e29b-41d4-a716-446655440000",
  "uploadId": "aws_multipart_upload_id",
  "expiresAt": "2025-01-03T10:30:00.000Z"
}

Erreurs: - 400: Données invalides - 429: Quota dépassé


PUT /v1/uploads/:sessionId/parts/:partNumber

Upload d'une partie (streaming direct vers S3).

Authentication: JWT Bearer Guards: TenantIsolationGuard, SessionOwnerGuard

Parameters: - sessionId (UUID): ID de la session - partNumber (integer): Numéro de la partie (1-indexed)

Headers: - Content-Length: Taille de la partie en bytes

Body: Binary data (streaming)

Response: 200 OK

{
  "partNumber": 1,
  "etag": "aws_etag",
  "uploadedBytes": 5242880
}

Erreurs: - 400: Taille ou numéro de partie invalide - 404: Session non trouvée - 409: Conflit partie (contenu différent) - 410: Session expirée


POST /v1/uploads/:sessionId/complete

Finaliser l'upload multipart.

Authentication: JWT Bearer Guards: TenantIsolationGuard, SessionOwnerGuard

Parameters: - sessionId (UUID): ID de la session

Response: 200 OK

{
  "sessionId": "550e8400-e29b-41d4-a716-446655440000",
  "fileKey": "s3://bucket/path/to/file",
  "fileSize": 10485760,
  "completedAt": "2025-01-02T10:35:00.000Z"
}

Erreurs: - 400: Parties manquantes ou invalides - 404: Session non trouvée - 409: Session en état terminal - 410: Session expirée


DELETE /v1/uploads/:sessionId

Annuler l'upload.

Authentication: JWT Bearer Guards: TenantIsolationGuard, SessionOwnerGuard

Parameters: - sessionId (UUID): ID de la session

Response: 200 OK

{
  "sessionId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "ABORTED",
  "abortedAt": "2025-01-02T10:35:00.000Z"
}

Erreurs: - 404: Session non trouvée - 409: Session déjà complétée


GET /v1/uploads/:sessionId

Récupérer le statut d'une session d'upload.

Authentication: JWT Bearer Guards: TenantIsolationGuard, SessionOwnerGuard

Parameters: - sessionId (UUID): ID de la session

Response: 200 OK

{
  "sessionId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "IN_PROGRESS",
  "uploadedParts": 1,
  "totalParts": 2,
  "uploadedBytes": 5242880,
  "totalBytes": 10485760,
  "expiresAt": "2025-01-03T10:30:00.000Z"
}

Erreurs: - 404: Session non trouvée


Cryptographie - Key Envelopes

Gestion des enveloppes de clés (Master, Device, Recovery).

Référence: PD-35 ⚠️ IMPORTANT: JWT authentication temporairement désactivé pour développement. À activer avant production !


POST /v1/crypto/keys/master-envelope

Créer Master Envelope lors de la création de compte.

Authentication: ⚠️ Désactivé (dev) - JWT requis en production

Request Body:

{
  "kEncryptionBase64": "base64_encoded_32_bytes",
  "kMasterUserBase64": "base64_encoded_32_bytes"
}

Response: 201 Created

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "encryptedEnvelope": "base64_encoded_40_bytes",
  "algorithm": "AES-256-KW",
  "version": 1
}

Erreurs: - 400: Clés invalides (longueur incorrecte) - 409: Master Envelope existe déjà


POST /v1/crypto/keys/device-envelope

Créer Device Envelope lors de l'ajout d'un nouveau device.

Authentication: ⚠️ Désactivé (dev) - JWT requis en production

Request Body:

{
  "deviceId": "550e8400-e29b-41d4-a716-446655440000",
  "kDeviceBase64": "base64_encoded_32_bytes",
  "kMasterUserBase64": "base64_encoded_32_bytes"
}

Response: 201 Created

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "deviceId": "device_uuid",
  "encryptedEnvelope": "base64_encoded_40_bytes",
  "algorithm": "AES-256-KW",
  "version": 1
}

Erreurs: - 400: Clés invalides - 403: Device blacklisté - 409: Device Envelope existe déjà


POST /v1/crypto/keys/recovery-envelope

Créer Recovery Envelope (BIP39).

Authentication: ⚠️ Désactivé (dev) - JWT requis en production

Request Body:

{
  "kRecoveryBase64": "base64_encoded_32_bytes",
  "kMasterUserBase64": "base64_encoded_32_bytes"
}

Response: 201 Created

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "encryptedEnvelope": "base64_encoded_40_bytes",
  "algorithm": "AES-256-KW",
  "version": 1
}

Erreurs: - 400: Clés invalides - 409: Recovery Envelope existe déjà


GET /v1/crypto/keys/master-envelope

Récupérer Master Envelope (pour changement password).

Authentication: ⚠️ Désactivé (dev) - JWT requis en production

Response: 200 OK

{
  "encryptedEnvelope": "base64_encoded_40_bytes",
  "algorithm": "AES-256-KW",
  "version": 1
}

Erreurs: - 404: Master Envelope non trouvée


GET /v1/crypto/keys/device-envelope/:deviceId

Récupérer Device Envelope (pour accès document).

Authentication: ⚠️ Désactivé (dev) - JWT requis en production

Parameters: - deviceId (UUID): ID du device

Response: 200 OK

{
  "encryptedEnvelope": "base64_encoded_40_bytes",
  "algorithm": "AES-256-KW",
  "lastUsedAt": "2025-01-02T10:30:00.000Z"
}

Erreurs: - 403: Device blacklisté - 404: Device Envelope non trouvée


GET /v1/crypto/keys/devices

Lister tous les devices autorisés de l'utilisateur.

Authentication: ⚠️ Désactivé (dev) - JWT requis en production

Response: 200 OK

[
  {
    "deviceId": "550e8400-e29b-41d4-a716-446655440000",
    "lastUsedAt": "2025-01-02T10:30:00.000Z",
    "createdAt": "2025-01-01T08:00:00.000Z"
  }
]

DELETE /v1/crypto/keys/devices/:deviceId

Révoquer un device.

Authentication: ⚠️ Désactivé (dev) - JWT requis en production

Parameters: - deviceId (UUID): ID du device à révoquer

Request Body:

{
  "reason": "lost_device"
}

Response: 204 No Content

Erreurs: - 404: Device non trouvé


Audit & Preuves probatoires

Export et vérification de preuves probatoires pour audit indépendant.

Référence: PD-37 E-01 Compliance: NF Z42-013, ISO 14641


GET /v1/audit/proof/:id

Exporter une entrée d'audit comme preuve vérifiable.

Authentication: JWT Bearer (implicite)

Parameters: - id (UUID): ID de l'entrée audit

Response: 200 OK

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "entryCanonical": "{\"action\":\"CREATE\",\"timestamp\":\"2025-01-02T10:30:00.000Z\"}",
  "entryHash": "sha3_256_hex",
  "hsmSignature": "base64_signature",
  "hsmKeyId": "key_uuid",
  "publicKey": "base64_public_key"
}

Notes: - La preuve contient toutes les données pour vérification indépendante - Vérifiable sans accès HSM

Erreurs: - 404: Entrée audit non trouvée


GET /v1/audit/proof/:id/verify

Vérifier une entrée d'audit (HSM-independent).

Authentication: JWT Bearer

Parameters: - id (UUID): ID de l'entrée audit

Response: 200 OK

{
  "valid": true,
  "entryId": "550e8400-e29b-41d4-a716-446655440000",
  "hashValid": true,
  "signatureValid": true,
  "verifiedAt": "2025-01-02T10:30:00.000Z"
}

POST /v1/audit/proof/verify-batch

Vérifier plusieurs entrées d'audit en batch (max 1000).

Authentication: JWT Bearer

Request Body:

{
  "ids": [
    "550e8400-e29b-41d4-a716-446655440000",
    "660e9511-f39c-23e4-b827-557766551111"
  ]
}

Response: 200 OK

{
  "total": 2,
  "valid": 2,
  "invalid": 0,
  "results": {
    "550e8400-e29b-41d4-a716-446655440000": {
      "valid": true,
      "hashValid": true,
      "signatureValid": true
    },
    "660e9511-f39c-23e4-b827-557766551111": {
      "valid": true,
      "hashValid": true,
      "signatureValid": true
    }
  }
}

Erreurs: - 400: IDs manquants ou limite dépassée (max 1000)


POST /v1/audit/proof/verify-external

Vérifier une preuve externe (sans accès base de données).

Authentication: JWT Bearer

Request Body:

{
  "proof": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "entryCanonical": "{...}",
    "entryHash": "sha3_256_hex",
    "hsmSignature": "base64_signature",
    "hsmKeyId": "key_uuid",
    "publicKey": "base64_public_key"
  }
}

Response: 200 OK

{
  "valid": true,
  "hashValid": true,
  "signatureValid": true,
  "verifiedAt": "2025-01-02T10:30:00.000Z"
}

Notes: - Permet vérification par auditeurs tiers - Ne nécessite pas d'accès au système

Erreurs: - 400: Champs manquants dans la preuve


GET /v1/audit/proof/status

Statut de vérification pour entrées récentes.

Authentication: JWT Bearer

Query Parameters: - limit (integer, optional): Nombre d'entrées à vérifier (défaut 100, max 1000)

Response: 200 OK

{
  "checked": 0,
  "valid": 0,
  "invalid": 0,
  "message": "Use verify-batch endpoint for actual verification"
}

GET /v1/audit/proof/dlq/stats

Statistiques DLQ (Dead Letter Queue).

Authentication: JWT Bearer

Response: 200 OK

{
  "pending": 5,
  "retrying": 2,
  "resolved": 100,
  "abandoned": 3
}

Référence: PD-37 E-03


GET /v1/audit/proof/dlq/alerts

Vérifier alertes DLQ.

Authentication: JWT Bearer

Response: 200 OK

{
  "hasAlerts": true,
  "alerts": [
    "5 entries pending retry for >1 hour",
    "3 entries abandoned"
  ]
}

POST /v1/audit/proof/dlq/retry

Déclencher retry DLQ pour entrées en attente.

Authentication: JWT Bearer

Query Parameters: - batchSize (integer, optional): Nombre max d'entrées à traiter (défaut 50, max 200)

Response: 200 OK

{
  "processed": 10,
  "succeeded": 8,
  "failed": 2
}

Notes: - Appelé par Flow Prefect toutes les 15 minutes


POST /v1/audit/proof/dlq/:id/retry

Retry manuel d'une entrée DLQ spécifique.

Authentication: JWT Bearer

Parameters: - id (UUID): ID de l'entrée DLQ

Query Parameters: - by (string, optional): Qui déclenche le retry

Response: 200 OK

{
  "success": true,
  "auditLogId": "550e8400-e29b-41d4-a716-446655440000"
}

POST /v1/audit/proof/dlq/:id/abandon

Abandonner une entrée DLQ (marquer comme irrécupérable).

Authentication: JWT Bearer

Parameters: - id (UUID): ID de l'entrée DLQ

Query Parameters: - reason (string, optional): Raison de l'abandon - by (string, optional): Qui abandonne

Response: 200 OK

{
  "success": true
}

Jobs asynchrones

Gestion des jobs asynchrones via BullMQ + Redis.

Référence: PD-21 Architecture: Orchestration Prefect


POST /v1/jobs

Soumettre un nouveau job pour traitement asynchrone.

Authentication: JWT Bearer

Request Body:

{
  "type": "EMAIL_VALIDATION",
  "payload": {
    "email": "user@example.com",
    "token": "validation_token"
  }
}

Response: 202 Accepted

{
  "jobId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "PENDING",
  "submittedAt": "2025-01-02T10:30:00.000Z"
}

Notes: - Retour immédiat sans attendre l'exécution (INV-1: Non-blocage) - Job enqueued pour traitement par worker

Erreurs: - 400: Type de job ou payload invalide - 503: Queue indisponible


GET /v1/jobs/:jobId

Récupérer le statut d'un job par ID.

Authentication: JWT Bearer

Parameters: - jobId (UUID): ID du job

Response: 200 OK

{
  "jobId": "550e8400-e29b-41d4-a716-446655440000",
  "type": "EMAIL_VALIDATION",
  "status": "COMPLETED",
  "submittedAt": "2025-01-02T10:30:00.000Z",
  "completedAt": "2025-01-02T10:30:05.000Z",
  "result": {
    "success": true
  }
}

Notes: - Implémente CA-3: Traçabilité consultable

Erreurs: - 404: Job non trouvé


GET /v1/jobs/failed

Lister les jobs échoués (pour Flow Prefect retry).

Authentication: JWT Bearer

Query Parameters: - retryable (boolean, optional): Filtrer par statut retryable - jobType (string, optional): Filtrer par type de job - since (date, optional): Jobs échoués depuis cette date - limit (integer, optional): Nombre max de jobs à retourner

Response: 200 OK

{
  "jobs": [
    {
      "jobId": "550e8400-e29b-41d4-a716-446655440000",
      "type": "EMAIL_VALIDATION",
      "status": "FAILED",
      "failedAt": "2025-01-02T10:30:00.000Z",
      "error": "SMTP connection timeout",
      "retryable": true
    }
  ],
  "total": 1
}

Notes: - Utilisé par jobs_dlq_retry_flow.py


POST /v1/jobs/:jobId/retry

Retry d'un job échoué.

Authentication: JWT Bearer

Parameters: - jobId (UUID): ID du job à retry

Response: 202 Accepted

{
  "jobId": "660e9511-f39c-23e4-b827-557766551111",
  "originalJobId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "PENDING",
  "retryCount": 1,
  "submittedAt": "2025-01-02T10:35:00.000Z"
}

Notes: - Crée un nouveau job avec compteur de retry incrémenté - Job original marqué RETRIED

Erreurs: - 404: Job non trouvé - 400: Job non retryable, max retries atteint, ou statut invalide


GET /v1/jobs/stats

Statistiques jobs pour monitoring.

Authentication: JWT Bearer

Response: 200 OK

{
  "pending": 10,
  "active": 5,
  "completed": 1000,
  "failed": 20,
  "retried": 15,
  "totalProcessed": 1035
}

Notes: - Utilisé par Prefect pour monitoring et alerting


GET /v1/jobs/purgeable

Lister les jobs éligibles pour purge.

Authentication: JWT Bearer

Query Parameters: - olderThan (string, optional): Seuil d'âge (ex: "30d", "7d")

Response: 200 OK

{
  "jobs": [
    {
      "jobId": "550e8400-e29b-41d4-a716-446655440000",
      "type": "EMAIL_VALIDATION",
      "status": "COMPLETED",
      "completedAt": "2024-12-01T10:30:00.000Z"
    }
  ],
  "total": 1
}

Notes: - Utilisé par jobs_purge_flow.py pour preview


DELETE /v1/jobs/purge

Purger les jobs anciens selon politique de rétention.

Authentication: JWT Bearer

Query Parameters: - target (enum: db | redis, optional): Cible de purge - olderThan (string, optional): Seuil d'âge (ex: "30d" pour DB, "7d" pour Redis) - dryRun (boolean, optional): Mode preview (affiche ce qui serait purgé)

Response: 200 OK

{
  "purged": 50,
  "target": "db",
  "olderThan": "30d",
  "dryRun": false
}

Notes: - Implémente I-5: Politique de rétention formelle


Synchronisation multi-device

Synchronisation d'objets avec détection et résolution de conflits.

Référence: PD-171 Architecture: Détection conflits, résolution automatique, journalisation probatoire


POST /v1/sync

Synchroniser un objet existant.

Authentication: JWT Bearer Guards: DeviceAuthGuard, SchemaVersionGuard Interceptors: SyncAuditInterceptor

Request Body:

{
  "objectId": "550e8400-e29b-41d4-a716-446655440000",
  "version": 2,
  "baseVersion": 1,
  "delta": {
    "field1": "new_value"
  }
}

Response: 200 OK (sans conflit)

{
  "status": "APPLIED",
  "objectId": "550e8400-e29b-41d4-a716-446655440000",
  "newVersion": 2
}

Response: 409 Conflict (avec conflit)

{
  "status": "CONFLICT",
  "objectId": "550e8400-e29b-41d4-a716-446655440000",
  "conflictType": "CONCURRENT_MODIFICATION",
  "serverVersion": 3,
  "clientVersion": 2
}

Notes: - Flux N1 (sans conflit) ou N2 (avec conflit) - Résolution automatique selon règles configurées

Erreurs: - 404: Objet non trouvé


POST /v1/sync/create

Créer un nouvel objet synchronisé.

Authentication: JWT Bearer Guards: DeviceAuthGuard, SchemaVersionGuard

Request Body:

{
  "objectId": "550e8400-e29b-41d4-a716-446655440000",
  "data": {
    "field1": "value1"
  }
}

Response: 201 Created

{
  "status": "CREATED",
  "objectId": "550e8400-e29b-41d4-a716-446655440000",
  "version": 1
}

Erreurs: - 409: Objet existe déjà


GET /v1/sync/:objectId

Récupérer l'état canonique d'un objet.

Authentication: JWT Bearer Guards: DeviceAuthGuard

Parameters: - objectId (UUID): ID de l'objet

Response: 200 OK

{
  "objectId": "550e8400-e29b-41d4-a716-446655440000",
  "version": 3,
  "data": {
    "field1": "value1"
  },
  "lastModifiedAt": "2025-01-02T10:30:00.000Z"
}

Erreurs: - 404: Objet non trouvé


GET /v1/sync

Lister les objets de l'utilisateur.

Authentication: JWT Bearer Guards: DeviceAuthGuard

Query Parameters: - includeDeleted (boolean, optional): Inclure objets supprimés

Response: 200 OK

{
  "objects": [
    {
      "objectId": "550e8400-e29b-41d4-a716-446655440000",
      "version": 3,
      "lastModifiedAt": "2025-01-02T10:30:00.000Z"
    }
  ],
  "total": 1
}

GET /v1/sync/:objectId/history

Récupérer l'historique complet des versions.

Authentication: JWT Bearer Guards: DeviceAuthGuard

Parameters: - objectId (UUID): ID de l'objet

Response: 200 OK

{
  "objectId": "550e8400-e29b-41d4-a716-446655440000",
  "history": [
    {
      "id": "version_id_1",
      "version": 1,
      "baseVersion": null,
      "status": "APPLIED",
      "conflictType": null,
      "ruleApplied": null,
      "syncRequestId": "sync_req_1",
      "deviceId": "device_1",
      "timestamp": "2025-01-02T10:00:00.000Z"
    },
    {
      "id": "version_id_2",
      "version": 2,
      "baseVersion": 1,
      "status": "APPLIED",
      "conflictType": null,
      "ruleApplied": null,
      "syncRequestId": "sync_req_2",
      "deviceId": "device_2",
      "timestamp": "2025-01-02T10:30:00.000Z"
    }
  ]
}

Notes: - Historique complet pour audit (§5.8)

Erreurs: - 404: Objet non trouvé


GET /v1/sync/:objectId/export

Exporter objet pour audit (format canonique RFC 8785).

Authentication: JWT Bearer Guards: DeviceAuthGuard

Parameters: - objectId (UUID): ID de l'objet

Response: 200 OK

{
  "object_id": "550e8400-e29b-41d4-a716-446655440000",
  "exported_at": "2025-01-02T10:30:00.000Z",
  "canonical_version": 3,
  "history": [
    {
      "version": 1,
      "timestamp": "2025-01-02T10:00:00.000Z",
      "device_id": "device_1"
    }
  ]
}

Notes: - Export en JSON canonique (§5.10) - Vérifiable indépendamment

Erreurs: - 404: Objet non trouvé


Codes d'erreur

Codes HTTP standards

Code Signification Usage
200 OK Requête réussie
201 Created Ressource créée avec succès
202 Accepted Requête acceptée (traitement asynchrone)
204 No Content Requête réussie, pas de contenu retourné
400 Bad Request Données invalides
401 Unauthorized Non authentifié
403 Forbidden Non autorisé (permissions insuffisantes)
404 Not Found Ressource non trouvée
409 Conflict Conflit (doublon, état invalide)
410 Gone Ressource expirée
429 Too Many Requests Rate limit dépassé
500 Internal Server Error Erreur serveur
503 Service Unavailable Service indisponible

Format des erreurs

{
  "statusCode": 400,
  "message": "Invalid email format",
  "error": "Bad Request",
  "timestamp": "2025-01-02T10:30:00.000Z",
  "path": "/v1/auth/register"
}

Notes de sécurité

Architecture Zero-Knowledge

  • Le mot de passe n'est jamais envoyé au serveur
  • Authentification SRP-6a avec salt + verifier
  • Métadonnées documents chiffrées AES-256-GCM côté client

Isolation des données

  • RLS (Row Level Security) PostgreSQL
  • Filtre automatique par user_id
  • Guards NestJS pour contrôle d'accès supplémentaire

Rate Limiting

  • Protection anti-automatisation sur endpoints sensibles
  • Quotas configurables par plan utilisateur

Audit & Compliance

  • Journalisation probatoire de toutes les actions critiques
  • Signatures HSM pour preuves vérifiables
  • Conformité NF Z42-013, ISO 14641, RGPD

Endpoints manquants (roadmap)

Selon EPIC PD-192, ces endpoints sont prévus mais non implémentés :

  • POST /v1/documents/upload (upload complet intégré)
  • GET /v1/documents/:id/download (download sécurisé)
  • PUT /v1/documents/:id/metadata (mise à jour métadonnées)
  • POST /v1/documents/search (recherche avancée)
  • GET /v1/documents/:id/proof (preuve probatoire spécifique document)
  • POST /v1/documents/:id/share (partage PRE/délégation)
  • Versioning documents (PD-69)

Ressources

  • Swagger UI: http://localhost:3000/api/docs (développement)
  • TypeDoc: /docs/reference/typedoc/backend/
  • EPIC Docs-API: /docs/epics/docs-api/PD-192-epic.md
  • Architecture: Architecture Executive ProbatioVault v4.x

Dernière mise à jour: 2026-01-05 Généré automatiquement par: Claude Code