PRODSovereign European BaaS platformOpen Dashboard →

Engineering · 15 min read

Aurabase’s Rust core: a unified Backend-as-a-Service

Affane Daylami · Fondateur · August 16, 2026

Back to blog

Aurabase is a Backend-as-a-Service written in Rust — not just for a few peripheral services, but for its entire application core: gateway, authentication, database, real-time, storage, notifications, AI, provisioning. The Cargo workspace at the root of the repository lists 18 crates compiled together by a single cargo build --workspace, with no third-party language hidden behind the product logic.

This English text was generated automatically from the French original and has not been reviewed yet.

This file documents this architecture as it actually exists in the code, verified file by file as of August 23, 2026 — diagrams, figures and extracts included. This is the pillar page of the “Rust Engineering” cluster: it gives the overview, and links to the in-depth technical analyzes (Axum, Cargo workspace, gateway, PostgREST, pg_graphql, and the rest of the file) published or in the process of being published. For the complete product comparison against a competing BaaS, see our comparison Aurabase vs Supabase.

The essentials

  • Single Workspace Cargo: 18 crates (11 business services, the auraCLI, 5 shared libraries, the Rust SDK) compiled together by a single cargo build --workspace.
  • The business code weighs approximately 274,000 lines of Rust (measured via find + wc -l, August 23, 2026), distributed over these 18 crates.
  • The gateway (aura-gateway) separates two planes — data (SDK, port 8080) and management (Studio, port 8090) — each with its own middleware stack and authentication.
  • Aurabase does not rewrite PostgREST: the real upstream binary (v12.2.8) runs per tenant, orchestrated by Rust services — the added value is around it, not instead.
  • The only notable departure from pure Rust: the default runtime of Edge Functions (denomode) is a dedicated TypeScript service based on V8 Isolates; a second path, native and in Rust via Wasmtime, exists for wasmmode.
18
WORKSPACE CRATES
11 services + CLI + 5 libs + Rust SDK
~274k
RUST LINES
find + wc -l, August 23, 2026
2
GATEWAY PLANS
data: 8080 · management: 8090
10/11
SERVICES ON NATS
async-nats in direct dependency
#
Positioning

What sets Aurabase apart from a BaaS assembled service by service

Most open source Postgres BaaS packages their services in multiple languages. This is not a value judgment — it is an architectural fact that has concrete consequences: as many compilation chains, error conventions and auth logics to keep in synchronization as there are languages in play.

At Aurabase, the product layer that we write and maintain ourselves — gateway, auth, database, real-time, storage, notifications, AI, provisioning, account management — is a single Cargo workspace, a single language, a single build chain. This is the choice that this file documents.

Necessary precision

“Unified core” does not mean that everything running in production is Rust. Like any Postgres BaaS, Aurabase also relies on open source building blocks that it did not write: PostgreSQL itself, PostgREST, NATS. The structural difference with a heterogeneous stack does not relate to these shared building blocks — it concerns the product layer which orchestrates them. The following sections detail where exactly this boundary passes, including the only real exception we found when auditing the code (Edge Functions, section 09).

#
The workspace

18 crates, one compilation chain

The root Cargo.toml declares a Cargo workspace in resolver v2 with 18 members: 11 business services, the aura-cliCLI, 5 shared libraries, and the aurabase-rsSDK. Here is the actual list, as it appears in the repository.

Cargo.tomltoml
[workspace]
resolver = "2"

members = [
    # Services (11)
    "services/aura-gateway", "services/aura-auth", "services/aura-db",
    "services/aura-provisioner", "services/aura-realtime", "services/aura-storage",
    "services/aura-functions", "services/aura-notifications", "services/aura-ai",
    "services/aura-migrator", "services/aura-control",
    # Tools
    "aura-cli",
    # Libs (5)
    "libs/aura-core", "libs/aura-crypto", "libs/aura-db-adapters",
    "libs/aura-migrations", "libs/aura-telemetry",
    "aurabase-rs",
]

Shared dependencies live in [workspace.dependencies]: Axum 0.8 (with WebSockets, multipart, macros), Tokio, Tower/Tower-HTTP, SQLx 0.8 (Postgres + MongoDB via mongodb for secondary NoSQL engine), async-nats 0.47, sqlparser 0.53 (SQL validation of NL2SQL), oauth2 5, jsonwebtoken 10, and two performance libs found in almost every service: mimalloc as a global allocator and moka/dashmap for the in-memory cache.

The release profile documents an assumed choice: panic = "unwind" rather than "abort". The file comment is self-explanatory — a panic in an Axum/Tokio handler is isolated by the runtime (the affected request returns 500) instead of bringing down the entire process and cutting off competing requests. The performance gain ofabort (approximately 1 to 2% of RPS) is not worth the loss of insulation, according to this same note. This is a reliability-vs-speed trade-off documented in the code, not a marketing claim.

A single cargo build --workspace compiles the whole thing. A single cargo test --workspace runs the entire test suite. A single cargo clippy --workspace --all-targets -- -D warnings lints the entire product with the same rules. The details of this structure - inheritance of dependencies, internal graph between libs and services, pitfalls that we encountered while growing it - are the subject of a dedicated article: Cargo workspace architecture, how to structure a multi-service Rust backend.

Rust lines by service (find services -name '*.rs' | xargs wc -l, August 23, 2026):

aura-control

36 024

aura-db

30 255

aura-auth

28 431

aura-provisioner

28 271

aura-notifications

18 498

will have

18 305

aura-gateway

16 938

aura-realtime

16 799

aura-storage

13 747

aura-functions

11 619

aura-migrator

538

Excluding shared libs (aura-db-adapters: 24,725 lines, aura-core: 7,259, aura-migrations: 3,641, aura-crypto: 2,718, aura-telemetry: 201), the CLI (aura-cli: 9,845) and the Rust SDK (aurabase-rs: 6,363). aura-migrator, a single-use migration job and not a long-lived HTTP server, deliberately remains the smallest service in the workspace.

#
Services

11 business services, each a standalone Axum server

Each service is an independent Axum/Tokio binary, with its own configuration and port. Ten of the eleven expose /health and /metrics and declare mimalloc as the global allocator — the only exception, aura-migrator, is a single-purpose job rather than a continuously running server.

aura-gatewayDual-plane gateway (data:8080, management:8090): proxy to all other services.
aura-authAuthentication: JWT, 15 named OAuth providers + generic OIDC per project, sessions, MFA.
aura-dbDatabase API: PostgREST management/reload per tenant, Postgres and MongoDB adapters, CDC.
aura-provisionerProject life cycle: dedicated or shared CNPG clusters, roles, PostgREST per tenant.
aura-realtimeWebSocket and SSE, CDC broadcast, cross-instance presence via NATS JetStream KV.
aura-storageS3 compatible objects (MinIO), RLS policies transferable from Postgres.
aura-functionsEdge Functions: deployment, jobs, cron, native Wasmtime runtime (see section 09).
aura-notificationsEmail, push, outgoing webhooks.
will haveNL2SQL, RAG and LLM gateway (native OpenAI, Anthropic, Gemini + any OpenAI compatible endpoint).
aura-migratorMigration engine: single source of the holding schema replayed at each provisioning.
aura-controlManagement plan: developer accounts, organizations, billing, Studio API.

The aura CLI (≈ 9,800 lines) talks to the same APIs as the SDKs — it has no privileged path. The full reference for each service lives in the architecture documentation and the CLI reference.

#
Shared libraries

5 crates that avoid inter-service drift

In a polyglot stack, a security rule — error format, SSRF protection, rate limiting — must be reimplemented in each language, and almost always drifts over the months. Aurabase codes it once, in a workspace lib, consumed by all the services concerned.

aura-core7 259 l.Shared primitives: errors, API response envelope, JWT claims, internal service-to-service auth, NATS helpers, rate limiting, SSRF protected HTTP client, circuit breaker, tenant resolution, metering.
aura-db-adapters24 725 l.Trait to adapt unified database, Postgres and MongoDB implementations used by aura-db.
aura-crypto2 718 l.Password hashing, token generation, JWT signing/validation, field-level encryption.
aura-migrations3 641 l.Migration engine — single source of the tenant schema, replayed by the provisioner (not the migrations/specific folders for each service).
aura-telemetry201 l.OpenTelemetry + tracing configuration, shared by the 11 services.

Direct consequence: a security patch in aura-core — SSRF protection, for example — propagates to each consumer service in the next cargo build, not through five separate patches in five languages.

#
The gateway

Dual plan: SDK traffic versus Studio traffic

aura-gateway separates two surfaces that have neither the same clients nor the same auth model. The data plane (port 8080, variable GATEWAY_PORT) receives SDK/app traffic, authenticated by API key (apikey, X-API-Key or ?apikey=). The management plane (port 8090, MANAGEMENT_PORT) receives Studio/admin traffic, authenticated by a JWT console (Authorization: Bearer).

gateway/routes.rsrust
// Data plane — API key
.route("/v1/auth/{*path}", any(auth_proxy))
.route("/v1/db/{*path}", any(db_proxy))
.route("/v1/realtime/ws", get(realtime_ws_proxy))
.route("/v1/realtime/sse", get(realtime_sse_proxy))
.route("/v1/storage/{*path}", any(storage_proxy))
.route("/v1/notifications/{*path}", any(notifications_proxy))
.route("/v1/ai/{*path}", any(ai_dispatcher))
.route("/v1/functions/{*path}", any(functions_proxy))

// Management plan — JWT console
.route("/v1/control/{*path}", any(control_proxy))

Each plane carries its own stack of middleware — query identification, access log, rate limit, authentication (plane-specific), circuit breaker, then proxy — implemented in separate modules (middleware/rate_limit.rs, middleware/circuit_breaker.rs, middleware/auth_data_plane.rs, middleware/auth_management_plane.rs) rather than a single chain accidentally shared between two surfaces that should not trust each other in the same way. way.

The exact order of the middlewares, the rate limiting per target and the NATS/HTTP proxy are the subject of a dedicated article: dual-plane gateway, design a data-plane/management-plane API gateway in Rust.

#
The data

Why Aurabase does not reimplement PostgREST in Rust

The self-generated REST API that the SDK consumes is not an in-house Rust module: it is the real upstream binary PostgREST (postgrest/postgrest:v12.2.8), deployed in 2 replicas per dedicated project (deploy/cnpg/tenant-postgrest.yaml), co-located with the tenant's CNPG instance. aura-provisioner creates this deployment, and aura-db probes its endpoint OPTIONS after each schema reload to confirm that PostgREST has taken the DDL change into account.

This is an assumed architectural choice, not a shortcut: PostgREST is a mature, widely adopted project, whose behavior rewriting bit by bit in Rust would bring nothing. Aurabase's Rust work is focused around — multi-tenant routing, provisioning, network isolation by NetworkPolicy, shared auth with the gateway, orchestrated schema reload — not inside the query engine itself. What this compatibility really covers, and where it stops, is the subject of a separate article: PostgREST, actual compatibility andalternatives.

Same logic on the GraphQL side: the Postgres extension pg_graphql (precompiled .deb package, v1.6.1) is installed in the dedicated CNPG image and activated on demand per project via the POST /v1/control/projects/{project_id}/graphql/enable endpoint — not a rewritten GraphQL engine either. Details and honest comparison with Hasura and PostGraphile: Native GraphQL API on Postgres with pg_graphql.

#
Insulation

RLS and authenticator role, not an application layer

Multi-tenant isolation does not rely on an WHERE tenant_id = ? filter added by an application ORM: each request goes through the aura_authenticatorrole, which executes a SET LOCAL ROLE tenant_<uuid> transaction-scoped before executing the request — exactly the model that PostgREST itself expects. Row-Level Security does the rest, at the engine level, not at the business code level.

Not all projects share the same Postgres topology. The aura-provisioner code exposes a ProjectInstanceKind type with at least two real variants: FullyDedicated (CNPG instance entirely dedicated to the project) and SharedClusterDedicated (schema isolated by RLS on a shared CNPG cluster). The subscribed plan determines the topology — it is not a uniform promise of a “dedicated base for everyone”.

Astuce

The angle “why a dedicated base per project rather than purely application isolation” is the subject of a dedicated article: RLS and dedicated base per project. The Row-Level Security documentation covers practical implementation.

#
Messaging

NATS core, not JetStream: asynchronous messaging between services

Ten of the eleven business services declare async-nats as a direct dependency in their Cargo.toml — only aura-migrator does without it. What this crate name doesn't say: these services almost everywhere use NATS'core pub/sub API (Client::publish / publish_with_headers, at-most-once delivery without persistence or replay), not JetStream. This is the case verified in the code for the three uses that matter the most: the distribution of the PostgreSQL CDC to aura-realtime, the notification of Edge Functions jobs in aura-functions (the DLQ itself lives in Postgres, not in NATS), and provisioning events between aura-provisioner and the rest of the fleet.

JetStream — NATS' persistence and named streams mode — only appears in one verified location in the monorepo: the cross-instance presence KV Store in aura-realtime (bucket aura_presence, memory store, 60-second max_age), which synchronizes who is connected to which channel across multiple instances of ws-front — a cross-instance shared state, not a stream of events to replay. No persistent JetStream streams were found elsewhere in the workspace. The details of the CDC pipeline - wal2json, election of a unique cdc-worker by Lease Kubernetes, core NATS fan-out to the ws-frontreplicas, RLS re-verification by subscriber - are the subject of a dedicated article: broadcast the PostgreSQL CDC with NATS. The Realtime documentation covers usage on the SDK side.

#
The accepted exception

Edge Functions: two runtimes for two needs

This is the most important nuance of this file, and the only real departure from pure Rust in the Aurabase product code. Each function carries a runtime field which is worth "wasm" or "deno". The invocation handler chooses the execution path accordingly — actual snippet, commented out in the code itself:

functions/dispatch.rsrust
// Dispatch according to runtime: WASM (wasmtime) or Deno (V8 Isolates via edge-runtime)
let resp = if func.runtime == "wasm" {
    state.runtime.invoke(&wasm_bytes, &func.code_hash, req).await?
} else {
    // Proxy to aura-edge-runtime — V8 Isolates (supabase/edge-runtime, MIT)
    proxy_to_edge_runtime(&state.config.edge_runtime_url, …).await?
};

The wasm mode is native: aura-functions depends directly on Wasmtime (version 43, features async and cranelift) and executes the module in the same Rust process, with fuel counting, interruption by epoch and memory bounding via StoreLimits. The deno mode — the one used by default in the Studio editor, for almost direct Deno.serve() compatibility with existing Supabase code — delegates execution to aura-edge-runtime, a separate TypeScript service of about 550 lines, explicitly modeled — the source file header comment itself cites it — on supabase/edge-runtime (MIT license), which isolates each invocation into its own V8 Isolate.

Why is it honest to say it like that?

Control — deployment, permissions, jobs, cron, quotas — remains entirely in Rust in aura-functions. Only executing user code in deno mode exits the Rust binary. This is a defensible engineering compromise (V8 Isolates are what Deno natively provides for this level of sandboxing), not an omission we prefer to keep quiet. The Wasmtime vs Wasmer comparison and the WebAssembly cold start analysis, coming soon in this file, will go further on this subject.

For the how-to doc of both deployment paths, see Edge Functions. For the Deno migration playbook from Supabase, see Migrate a Supabase project to Aurabase, which already documented this distinction before this file.

#
In practice

What this unified heart concretely changes for you

If you just call the API through an SDK, this architecture is invisible — that's the goal. It mainly matters to three audiences: those who evaluate the operational reliability of a multi-tenant backend before migrating production data to it, those who plan to contribute to the repository (MIT, single monorepo), and those who want to understand why a security patch on the Aurabase side spreads quickly rather than slowly.

Concretely: a single IC (cargo build --workspace, cargo test --workspace, cargo clippy --workspace --all-targets -- -D warnings) covers 90% or more of the product surface. A code review on aura-core potentially affects ten services at once — for better (a fix doesn't get lost along the way) and for worse (a poorly isolated change spreads just as quickly). It's a compromise, not a magic solution — and that's precisely why we document the actual architecture rather than a marketing summary.

#
Folder Hub

The rest of the Rust Engineering file

This pillar page links to the technical analyzes of the cluster, as they are published. Actual status at time of publication of this page — links become active as soon as the corresponding article is online.

Cluster A — Framework & architecture

Axum vs Actix-web: which framework for a Rust backend in production?Published

Cargo workspace architecture: how to structure a multi-service Rust backendPublished

Dual-plane gateway: designing a data-plane/management-plane API gateway in RustPublished

Cluster B — Self-generated API on Postgres

PostgREST: what compatibility really covers, and what alternatives existPublished

Native GraphQL API on Postgres with pg_graphql: what Hasura and PostGraphile don't do the samePublished

RLS and dedicated base per project: the choice of multi-tenant insulation from AurabasePublished

Cluster C — Real time & messaging

Distribute PostgreSQL CDC with NATS: real-time architecturePublished

Cluster D — Edge Functions WebAssembly

Wasmtime vs Wasmer: which WebAssembly runtime for Edge Functions in productionComing soon
Edge Functions in Rust/WASM versus Cloudflare Workers and Vercel EdgeComing soon
Cold start WebAssembly: what the benchmarks really say (and what we can't yet say)Coming soon

Cluster E — Migration & alternatives

Migrate a Supabase project to Aurabase without rewriting your RLS policiesPublished

Sovereign self-hosting: positioning Aurabase against Rust-native BaaSComing soon

#
Frequently asked questions

FAQs

Is Aurabase code open source?+
The repository is a single monorepo: the 18 Rust crates of the workspace, the client SDKs (JavaScript, Rust, Python, Dart) and the Next.js Studio live there together, published under the MIT license on GitHub.
Can Aurabase be self-hosted?+
Yes. The local k3d bench (./start.sh) and the official Helm chart (deploy/helm/aurabase/) allow you to deploy the complete stack. Aurabase Cloud remains the managed option if you prefer not to operate the infrastructure yourself.
Does the JavaScript SDK use the same syntax as Supabase?+
On the essentials, yes: createClient(), the chained query builder .from().select().eq(), the authentication flows and the RLS policies aim for almost direct compatibility — this is what makes a Supabase to Aurabase migration feasible without a complete rewrite.
Do you need to know Rust to use Aurabase?+
No. Rust is the backend language, not the one you write on a daily basis: client SDKs exist in JavaScript/TypeScript, Rust, Python and Dart, and Edge Functions are written by default in JavaScript/TypeScript (Deno runtime). Rust only comes into play if you explicitly choose the native WASM execution mode.
Do Aurabase Edge Functions really run in WebAssembly?+
It depends on the mode chosen. The default mode (deno) runs your JavaScript/TypeScript code in V8 Isolates via a dedicated service, not in a WASM engine. A second mode (wasm) exists and runs natively in the Rust aura-functions service via Wasmtime — but this is not the default path.

READY TO DEPLOY?

Your backend in five minutes.

No credit card required · 500 MB free · 50,000 MAU