API upload & download blob Décidé
Le videur rehache chaque colis à la porte — l'empoisonnement est impossible par construction. Deux familles d'endpoints par classe de segment, un batch-get piloté par le serveur qui réalise « les courses par rayon », et le protocole de données est complet.
Upload — le videur à la porte
PUT /v1/chunks/{addr}), le serveur rehache le corps et vérifie BLAKE3(corps) == addr avant d'indexer. Mismatch = rejet sec. Conséquence énorme : l'empoisonnement est impossible par construction — on ne peut pas ranger un mauvais contenu sous une bonne adresse, puisque l'adresse est le hash du contenu. Une machine compromise du tenant peut uploader des déchets (un problème de quota), jamais corrompre un chunk que les autres machines dédupliquent.
Les propriétés :
- Idempotent — re-uploader un chunk existant = succès no-op. Livrer deux fois le même carton n'est pas une erreur : c'est la reprise qui fonctionne.
- Durable avant ack — le contrat de durabilité, appliqué au protocole.
- Taille max ≈ 8 Mio + marge (le max du chunker) — au-delà, rejet.
- Parallélisme par multiplexing H2, fenêtre en vol bornée côté client (pipeline).
Deux familles d’endpoints, par classe. Le serveur doit savoir dans quelle caisse ranger (segments données vs métadonnées) : /v1/chunks/* → segments données (HDD) ; /v1/meta/* (manifestes, répertoires, racines) → segments métadonnées (SSD). Les manifestes — convergents — ont leur check d’existence (POST /v1/meta/exists) ; les répertoires et racines, jamais (toujours neufs par construction). Même videur, mêmes règles partout.
Download — la liste de courses remise au magasinier
POST /v1/chunks/batch-get, tableau d'adresses), et le magasinier fait le picking dans l'ordre de ses rayons — le serveur streame les blobs dans son ordre optimal (trié segment/offset), en flux framé [addr | len | payload]. C'est lui qui connaît l'entrepôt.
Les GET unitaires (GET /v1/chunks/{addr}, GET /v1/meta/{addr}) restent pour la navigation, l’arbre parent du scan (petits nœuds, SSD, cachés en RAM) et le debug.
Le point d’entrée des racines. Le client stateless doit trouver la racine du snapshot parent : GET /v1/snapshots — la liste des refs, servie depuis la DB de contrôle (l’équivalent de git branch). C’est le seul endpoint qui touche la DB de contrôle sur le chemin des données ; tout le reste est du CAS pur.
Le protocole de données, complet
| Endpoint | Rôle | Particularité |
|---|---|---|
POST /v1/sessions · …/commit | ouvrir / committer un backup | session-promesse, commit atomique |
POST /v1/chunks/exists · /v1/meta/exists | dédup par lots | bitmap, épinglage à la session |
PUT /v1/chunks/{addr} · /v1/meta/{addr} | upload | rehash à la porte, idempotent, durable avant ack |
POST /v1/chunks/batch-get | restauration | picking par rayon, flux framé |
GET /v1/chunks/{addr} · /v1/meta/{addr} | lecture unitaire | navigation, arbre parent, debug |
GET /v1/snapshots | refs des racines | seul contact avec la DB de contrôle |
GET /v1/capabilities | négociation | tailles de batch, features, versions (transport) |
Sept familles d’endpoints : toute la surface dont l’agent a besoin pour sauvegarder et restaurer. Config, heartbeat et jobs s’y ajoutent (même style, polling) ; l’authentification est le sujet suivant. Et remarque ce qui n’existe pas : aucun endpoint de suppression — le client n’a pas la gomme.