Anatomie du serveur srv Décidé
Un seul binaire cairn-server à casquettes — pas de microservices sur le chemin chaud. L'index se sharde gratuitement par préfixe d'adresse, la coordination de dédup se dissout dans l'existant, et l'isolation multi-tenant gagne son troisième verrou : le système de types.
Décision — le monolithe modulaire à rôles
check d'existence → bloom → LSM — 500 000 fois par backup. En microservices, ce trajet devient un aller-retour réseau au milieu du chemin chaud ; et pour le revendeur auto-hébergé, douze services = douze pannes possibles. Le miroir exact de l'agent s'impose : un binaire cairn-server, des casquettes activables — tout-en-un sur une machine (le revendeur FS/S3), ou déployé par rôles front / storage / jobs sur les nœuds du design 3 DC. Le même binaire partout : un artefact à signer, un à mettre à jour, et les appels internes chauds restent des appels de fonction.
Seuls les vrais flux traversent le réseau (front → nœud de stockage, nœud → nœud pour la réplication), en mTLS interne sur PKI privée — la boucle promise par la page auth (« mTLS excellent entre nos services ») se referme : le sceau de cire fonctionne très bien quand personne ne rouvre le courrier.
L’ingest, de bout en bout
Chaque étape a été décidée dans sa page ; les voici assemblées en un seul récit :
TLS (hybride ML-KEM) → token → session → quota # auth, épinglage, limites
→ rehash BLAKE3 # le videur : mismatch = rejet, empoisonnement impossible
→ routage vers le segment ouvert du (tenant, classe) # nœud propriétaire, carte de placement
→ append répliqué 2× → fsync groupé → insertion index → ack # durable avant visible
Avec la contre-pression de bout en bout : files bornées à chaque étage — un disque qui ralentit se propage en refus polis (429) jusqu’au client, dont le backoff fait le reste. Jamais de file infinie qui masque un problème en le laissant grossir.
L’index shardé par préfixe d’adresse
Le service métadonnées n’est pas un service à part : les mêmes nœuds servent la classe méta (NVMe, réplication 3×, cache RAM agressif), plus les refs de snapshots dans Postgres. Et la « coordination de dédup côté serveur » se dissout : ce n’est pas un composant, c’est une propriété émergente de l’API d’existence + les sessions + l’index. Il n’y a rien à construire de plus — le meilleur service est celui qu’on n’écrit pas.
L’isolation multi-tenant — le troisième verrou, dans les types
Deux verrous existent déjà : la crypto (des CK différentes — les tenants ne peuvent pas nommer les données les uns des autres) et le protocole (le scoping par token). Le troisième : le code lui-même, par la leçon du typestate :
// Le tenant n'est pas un paramètre qu'on peut oublier — c'est un type
// qui enveloppe tout ce qui touche aux données.
pub struct TenantScoped<T> { tenant: TenantId, inner: T }
// Les fonctions de la couche stockage N'ACCEPTENT QUE du scopé :
pub fn index_lookup(q: TenantScoped<AddrBatch>) -> TenantScoped<Bitmap>;
pub fn read_segment_entry(r: TenantScoped<EntryRef>) -> TenantScoped<Bytes>;
// Une requête « nue », un log sans tenant, un accès cross-tenant :
// ça ne compile pas. L'oubli d'isolation n'est pas un bug possible —
// c'est un programme impossible.
S’y ajoutent l’équité des ressources (jobs par tenant, quotas et rate limits accrochés aux claims) et la télémétrie taguée tenant partout — l’exigence de cloisonnement des certifications, prouvable à l’audit.
L’autorisation — les trois populations, fermées
- Machines — paire de clés + tokens courts, scopes dans les claims (décidé). Pas de scope de suppression : l'endpoint n'existe pas.
- Humains — SSO/OIDC + MFA + RBAC sur le dashboard (sections H/I, à venir — la frontière est posée).
- Services internes — mTLS sur PKI privée, identité par rôle (
frontne peut pas appeler les API d'administration des nœudsstorage, un workerjobsne termine pas de sessions). Le moindre privilège entre nos propres casquettes.