0001 - Rust microservices with axum + tonic for backend
- Status: Accepted
- Date: 2026-04-19
- Deciders: nexus core team
- Tags: language, framework, architecture, backend
- Consulted: Aegis core team (shared-crate owner; see
vortex-common-crates/contracts/shared-crates.md)
Context and Problem Statement
Section titled “Context and Problem Statement”This decision sets the backend language for every nexus service that compiles to a binary: gateway, control-plane, auth, policy, ingest, audit, query. The choice is effectively irreversible — changing it later means a rewrite. Constraints:
- Memory safety is non-negotiable. The gateway terminates TLS and proxies customer prompts; a memory-corruption bug is a cross-tenant data leak.
- FIPS posture is required (see the forthcoming ADR-0009 for details). Rust is compatible with FIPS-validated crypto modules via
aws-lc-rs. - The shared-crate contract with Aegis names Rust crates (
vortex-types,vortex-classification,vortex-audit-core,vortex-auth-core,vortex-policy-core,vortex-telemetry,vortex-proto). Seevortex-common-crates/contracts/shared-crates.md. Choosing a different language for nexus would force re-authoring or FFI-bridging every shared crate. - The gateway hot path must be predictable under load; no GC pauses, no runtime warm-up penalty.
- The Bifrost reference implementation is Go; the Helicone reference is TypeScript on a Cloudflare Worker. Both shape our informed opinion of what Rust buys us here.
Decision Drivers
Section titled “Decision Drivers”- Memory safety without GC.
- FIPS-capable crypto stack.
- Shared-crate alignment with Aegis.
- Mature async HTTP (
axum) and gRPC (tonic) ecosystems. - Predictable latency for the gateway hot path.
- Strong compile-time checks on wire contracts (
tonic-prost-build,sqlx::query!,serde).
Considered Options
Section titled “Considered Options”- Rust +
axum(HTTP) +tonic(gRPC) — chosen. - Go +
net/http+grpc-go— Bifrost’s path. Pros: fast compile times, simple onboarding. Cons: GC pauses on the gateway hot path, no compile-time wire checks, shared-crate story requires CGo or parallel implementations. - TypeScript on Node — Helicone’s valhalla/jawn path. Pros: shared types with
web/. Cons: no memory-safety guarantee for bugs in native modules, GC pauses, FIPS story is immature. - TypeScript on Cloudflare Workers — Helicone’s
worker/path. Explicitly rejected because it assumes edge-hosted SaaS, hostile to air-gap deployment (see the Helicone lessons-learned, “patterns to improve on” item 1). - Java / Kotlin on JVM — Pros: strong enterprise story. Cons: warm-up latency unacceptable for the gateway, memory footprint heavy for the number of services we run.
- C++ — No.
Decision Outcome
Section titled “Decision Outcome”Chosen option: Rust with axum for HTTP services and tonic for internal gRPC.
Concrete constraints:
- MSRV is pinned in the workspace
Cargo.tomland updated deliberately (one stable release behind current at any given time). - Every service crate lives in
services/(shared library crates incrates/) and depends on the appropriatevortex-*shared crates. - Async runtime:
tokiowith the multi-thread scheduler. Noasync-std, no mixed runtimes. - HTTP server:
axum(tower middleware ecosystem). - gRPC:
tonicserver +tonic-prost-buildfor code generation from.protounderproto/. - Database access:
sqlxwith compile-time query checking against a checked-in schema snapshot (macroquery!, notquery_as!for raw strings). - TLS:
rustlswith theaws-lc-rsprovider (FIPS-capable). Noopensslcrate dependency on new code. - Error handling:
thiserrorfor library errors,anyhowfor binaries only;Result<T, E>everywhere. - Serialization:
serdewithserde_jsonfor external JSON;prostfor protobuf (viatonic-prost-build). - Observability:
tracing+ thevortex-telemetryshared crate for OTLP wiring (seevortex-common-crates/contracts/telemetry.md).
What this decision does not cover
Section titled “What this decision does not cover”- ADRs listed in the backlog ship with the feature that first needs them: NATS JetStream (ADR-0010), ClickHouse (ADR-0003), OTLP (ADR-0004), BYOK/PTB routing (ADR-0006), audit chain (ADR-0007), classification header (ADR-0008), FIPS build profile (ADR-0009), Next.js console (ADR-0002).
Consequences
Section titled “Consequences”- A Rust learning tax on contributors not fluent in
tokioand lifetimes. We accept this; the onboarding guide links the canonical references. tonic-prost-buildandsqlx::query!enforce wire and query correctness at compile time — the inverse of Helicone’s TSOA-generated types (helicone lessons-learned, “patterns to improve on” item 7).aws-lc-rsgives a clear upgrade path to FIPS-validated crypto without swapping TLS stacks per build (to be formalized by ADR-0009).- Cross-service contracts live in
proto/and generate both Rust servers/clients (tonic-prost-build) and a TypeScript client used by the Next.js console (bufwith thebufbuild/esplugin; see ADR-0002 for the console itself). - Build times are non-trivial. We mitigate with
sccachein CI and workspace-level incremental builds.
