Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

En clair — un binaire, des casquettes Regarde notre chemin le plus fréquent : 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 :

# PUT /v1/chunks/{addr} — le chemin d'écriture complet, vu du serveur
TLS (hybride ML-KEM) → tokensessionquota # 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 indexack # 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

En clair — le sharding gratuit Comment répartir l'index sur plusieurs nœuds sans créer de points chauds ? Le cadeau de l'adressage par hash : les adresses sont uniformément aléatoires par construction. On découpe donc l'espace d'adresses en tranches par préfixe — chaque nœud de stockage possède une tranche (son LSM + son bloom, co-résidents sur son NVMe). Équilibrage parfait, gratuit, sans rebalancing savant : des hashes ne font pas de favoritisme. Un lot d'existence de 1 000 adresses ? Le front l'éclate par propriétaire (fan-out batché), refusionne la bitmap. Note l'indépendance voulue : l'index se place par préfixe, les shards EC par domaines de panne — chacun sa logique.

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 (front ne peut pas appeler les API d'administration des nœuds storage, un worker jobs ne termine pas de sessions). Le moindre privilège entre nos propres casquettes.