Stack backend : Rust + Axum
source : docs/adr/0001-backend-stack.md · versionné · MADR-lite
ADR-0001 — Stack backend : Rust + Axum
- Statut : Accepted
- Date : 2026-04-24
- Décideurs : Core team OmbrysWeb
- Issue GitHub : #1
- Références cahier des charges : § 5.2
Contexte
OmbrysWeb doit exposer plusieurs micro-services (auth, blog, messaging, storage, audit, vault-proxy, admin) manipulant :
- du trafic non authentifié à fort volume (blog public),
- de la cryptographie sensible (WebAuthn, dérivations, liboqs),
- des données utilisateur jamais lisibles en clair par le serveur (§ 5.3 Zero-Knowledge),
- une communication interne mTLS/gRPC avec rotation de certificats courts.
Le modèle de menace (§ 1.3) liste explicitement comme vecteurs critiques :
- corruption mémoire (buffer overflow, UAF, double-free) → classe de CVE historique dominante,
- défaut d'isolation entre workloads ou entre requêtes,
- chaîne d'approvisionnement (supply chain) compromise.
Le choix du langage backend et de son framework web structure toute la suite (CI, observabilité, format binaire, empreinte mémoire des conteneurs distroless § 5.7).
Options considérées
Option A — Rust + Axum (retenue)
- Langage memory-safe par design (sauf
unsafeexplicite, interdit sauf bindings FFI documentés). - Écosystème async mature via Tokio (dont Axum est issu).
- Axum : framework maintenu par le core Tokio, API fondée sur
tower(middlewares composables), intégration nativehyper/rustls. - Bindings FFI clairs pour liboqs (Kyber/Dilithium) et ring / RustCrypto.
- Binaires statiques compatibles
scratch/ distroless (< 30 MB typique). - Outillage :
cargo-audit,cargo-deny,cargo-fuzz,miri(UB detection),cargo-llvm-cov.
Option B — Go + Chi / Echo
- Memory-safe (GC), concurrence
goroutinessimple, compile-time rapide. - Écosystème crypto solide (
crypto/*,golang.org/x/crypto). - Bindings CGO pour liboqs fonctionnels mais augmentent la surface d'attaque (CGO désactive certaines protections du runtime Go, et complique les builds reproductibles statiques).
- Perf très correctes mais overhead GC mesurable sur charges crypto lourdes et latences tail.
- Moins de garanties statiques (typage moins strict, absence d'ADT/sum types ergonomique).
Option C — Node.js / TypeScript (Fastify, Hono)
- Partage de code potentiel avec le frontend TS.
- Not memory-safe pour les natifs (fuites courantes via modules C++).
- Performances et empreinte mémoire défavorables sur un parc de 7 services.
- Écosystème npm = surface supply chain extrêmement large → incompatible avec le durcissement visé (§ 5.5).
- Rejeté pour tout code backend. Reste acceptable côté frontend uniquement (via Next.js, § 5.1).
Option D — Zig / C / C++
- Performances maximales possibles.
- Non memory-safe → incompatible avec l'objectif d'élimination de la classe CWE-119 et apparentées.
- Rejeté.
Option E — Rust + Actix-web
- Même socle langage que l'option A.
- Actix-web historiquement plus performant sur benchmarks synthétiques, mais :
- base actor (
actix) non indispensable, - historique de
unsafemieux maîtrisé désormais mais plus dense qu'Axum, - ergonomie middleware et extraction un cran en dessous de
tower. - Conservé en plan B si un service a des besoins spécifiques (ex : WebSocket très haute charge).
Décision
Rust stable comme langage backend unique pour tous les micro-services (auth-svc, blog-svc, messaging-svc, storage-svc, audit-svc, vault-svc, admin-svc).
Axum (dernière stable, piste axum = "0.7" ou supérieure selon l'état à M1) comme framework HTTP par défaut. Tonic pour gRPC interne (mTLS, même écosystème Tokio).
Contraintes :
#![forbid(unsafe_code)]au niveau crate pour tout code applicatif.unsafeautorisé uniquement dans des crates dédiées FFI clairement nommées (ombrys-liboqs-sys, etc.), revues ligne à ligne.- Rust stable pin (ex :
rust-toolchain.tomlversionné) ; bump manuel après validation CI. cargo-denyconfiguré (licences autorisées, bans, advisories RustSec bloquantes).cargo-auditen CI bloquant surcriticalethigh.cargo-fuzzsur parsers et handlers sensibles (WebAuthn, désérialisation, gRPC).- Builds reproductibles :
--locked,SOURCE_DATE_EPOCHfixé, toolchain pinnée (§ 5.7.1).
Conséquences
Positives
- Élimine par construction la classe dominante de CVE (§ 1.3, ~70 % des CVE mémoire éliminées).
- Binaires statiques compatibles distroless/scratch → images < 50 MB par service (objectif § 5.7.1 atteint).
- Outillage sécurité (fuzz, audit, deny, miri) natif et mûr.
- Axum/Tonic/hyper = même runtime Tokio sur tous les services → cohérence opérationnelle et modèle mental unique.
- Compatible objectif SLSA niveau 3 (§ 5.5) et builds reproductibles.
Négatives
- Courbe d'apprentissage Rust plus raide que Go ou TS → onboarding contributeurs rallongé. Mitigation : pairing, revues appuyées en M1-M2, templates de micro-service.
- Temps de compilation plus long. Mitigation :
sccache, cache mount Docker, workspace partagé. - Écosystème crypto post-quantique (liboqs) encore jeune côté bindings Rust. Mitigation : crate FFI interne auditée, pas de dépendance directe sur un wrapper tiers non maintenu.
- Certains intégrateurs (SSO, SDK tiers) peuvent n'avoir de SDK qu'en Go ou Node. Mitigation : wrappers HTTP/gRPC isolés si nécessaire, jamais dans le chemin critique d'auth ou de crypto.
Neutres
- Observabilité :
tracing+opentelemetry-rustcouvrent les besoins, alignés sur Loki/Grafana (§ 5.5). - Testing :
cargo test+insta(snapshots) +criterion(bench) +proptestcouvrent le besoin.
Suivi
- Les ADR qui en dépendent directement : ADR-0002 (crypto, usage des crates RustCrypto), ADR-0003 (ZK, modèle de stockage), et toute ADR future concernant runtime, DB driver, observability.
- Une ADR de révision pourra être produite si :
- un service a un besoin où Axum est démontré insuffisant → option Actix-web locale avec justification ;
- l'état de l'écosystème PQ évolue (ex : Dilithium/Kyber inclus dans
ringupstream) → simplification possible des FFI.
Changelog
- 2026-04-24 : rédaction initiale, statut Accepted.