Skip to content

Architecture

AnvilBase is a set of services orchestrated by a central control plane. The control plane is the only component clients talk to; it authenticates requests, enforces per-project isolation, and routes to the right backing service.

Internet
┌──────────────────────────────────────────────────────────────┐
│ Traefik — TLS termination (Let's Encrypt), routing │
└───────────────────────────┬──────────────────────────────────┘
┌───────────────────────────▼──────────────────────────────────┐
│ Control Plane (Rust / Axum) │
│ • Project lifecycle (create / suspend / delete) │
│ • API key + JWT validation, RLS context injection │
│ • Rate limiting (per project, per scope) │
│ • Built-in REST→SQL engine (replaces PostgREST) │
│ • Audit log ingestion, secrets vault API, KMS abstraction │
└──┬──────────┬──────────┬──────────┬──────────┬───────────────┘
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌──────┐ ┌────────┐ ┌───────┐ ┌──────┐ ┌──────────┐
│ Auth │ │Realtime│ │Storage│ │ Deno │ │ Webhooks │
│Better│ │Phoenix │ │ MinIO │ │ Edge │ │ (Rust) │
│ Auth │ │Channels│ │ /S3 │ │ Funcs│ │ │
└──┬───┘ └───┬────┘ └───┬───┘ └──┬───┘ └────┬─────┘
│ │ │ │ │
└──────────┴───────────┴────┬────┴───────────┘
┌──────────────────────────────────────────────┐
│ PostgreSQL 15 (one database per project) │
│ platform_<project_id> │
│ + pgvector, PGMQ, pg_cron, pg_net, │
│ pgcrypto, pg_stat_statements │
└───────────────┬──────────────────────────────┘
┌───────────┴───────────┐
▼ ▼
┌──────────┐ ┌──────────┐
│ Supavisor│ │ Valkey │
│ (pooler) │ │ (cache) │
└──────────┘ └──────────┘

The two planes

AnvilBase exposes two distinct API surfaces on the same host. This split is fundamental — keep it straight.

PlanePrefixWho uses itCredential
Management plane/api/v1/*Operators, the console, the CLIAdmin token or Personal Access Token (anvilbase_pat_…)
Data plane/v1/*Your applications & end-user trafficPer-project API key (anon / service_role) or a project JWT

There are also two unauthenticated platform routes (/health, /health/services, /openapi.json) and an internal service-to-service surface (/internal/*, guarded by X-Internal-Secret) you never call directly.

See Core Concepts for the full credential model and Reference → Management API / Data-Plane API for the endpoint catalogs.

Components

Control plane (Rust / Axum)

The heart of the system. Besides routing and auth, it contains the REST→SQL engine — the component that gives every project a PostgREST-compatible API without a per-project process. Internally:

  • rest/pool_manager.rs — lazy per-project connection pools (deadpool-postgres), created on first request, evicted after ~5 min idle (~2–5 MB each).
  • rest/query_parser.rs — parses PostgREST query syntax (select, filters, order, limit, offset, operators).
  • rest/sql_builder.rs — generates parameterized SQL with validated, quoted identifiers (SQL-injection-safe; never string-interpolates user SQL).
  • rest/handler.rs — the request pipeline: parse → build SQL → acquire pool → inject RLS context (SET LOCAL) → execute → format response.

Auth (Better Auth, TypeScript/Bun)

One shared binary serves all projects, each with its own config and JWT secret. Handles email/password, magic links, OAuth, sessions, refresh tokens, and bans. The control plane proxies and administers it. See Auth.

Realtime (Elixir / Phoenix)

Phoenix Channels on the BEAM VM — built for millions of concurrent WebSockets. Subscribes to Postgres LISTEN/NOTIFY and fans out change events; also provides broadcast (pub/sub) and presence. The wire protocol matches @supabase/realtime-js. See Realtime.

Storage (MinIO, S3-compatible)

Per-project bucket isolation with SSE-S3 encryption. The control plane proxies S3 operations and issues presigned URLs. An opt-in overlay swaps MinIO for RustFS (a Rust S3 server) for a single-binary dependency story. See Storage.

Edge Functions (Deno)

TypeScript/JavaScript functions executed in the Deno runtime, with each project’s functions served by their own isolated OS process (filesystem-scoped to that project). The control plane stores code (metadata in Postgres, body in object storage) and the Deno runtime executes it, with the project’s DB URL, service-role key, and anon key injected as request headers. See Edge Functions and Functions Runtime.

Webhooks (Rust)

A standalone delivery service. The control plane writes a delivery row and a pg_notify; the webhooks service LISTENs, POSTs the signed payload (HMAC-SHA256 in X-Webhook-Signature, over "{timestamp}.{body}"), retries with exponential backoff, and dead-letters failures. See Webhooks.

PostgreSQL 15 + extensions

The single source of truth. One database per project (platform_<id>). Bundled extensions include pgvector (vectors), PGMQ (queues), pg_cron (scheduling), pg_net (outbound HTTP from SQL), pgcrypto (secrets & column encryption), and pg_stat_statements. See Extensions.

Supavisor (connection pooling) & Valkey (cache)

Supavisor (Elixir) provides transaction/session pooling. Valkey is the Apache-2.0 Redis fork used for rate limiting and the per-project namespaced cache (<project_id>:<key>). See Cache.

Traefik (reverse proxy)

Terminates TLS at the edge (Let’s Encrypt), routes by host/path, and is the only component that should be exposed publicly.

Request lifecycle (data plane)

A typical GET /v1/rest/<project_id>/messages?status=eq.published flows:

  1. Traefik terminates TLS and forwards to the control plane.
  2. Auth middleware resolves the API key / JWT, identifies the project and scope (anon / authenticated / service_role), and rejects unknown credentials.
  3. Rate limiter checks the per-project, per-scope budget in Valkey.
  4. REST engine parses the query, acquires the project’s pool, opens a transaction, and runs SET LOCAL to inject the user/role context.
  5. Postgres evaluates RLS policies against that context and returns only permitted rows.
  6. The engine formats a PostgREST-compatible JSON response with rate-limit headers.

The same middleware chain fronts auth, storage, functions, realtime, and eIDAS — isolation is enforced before any request reaches a backing service.

Multi-tenancy isolation

Each project is isolated across every layer:

ResourceIsolation
Databaseseparate platform_<id> database
Storageseparate bucket bucket-<id> + separate SSE key
Secretsencrypted vault in the project DB, unique key
Authseparate JWT secret (rotatable) per project
Edge functionsseparate OS process per project, FS-scoped to /functions/<id>
CacheValkey key namespace <id>:<key>
QueuesPGMQ namespace inside the project DB

See Security → Multi-Tenancy.

Language strategy (why each piece is what it is)

  • Rust (Axum) — control plane, REST engine, webhooks, CLI: lowest memory per connection, single-binary distribution, fast JWT validation.
  • Elixir (Phoenix) — realtime + Supavisor: the BEAM is unmatched for massive concurrent WebSocket fan-out.
  • TypeScript (Bun) — Better Auth and the Deno edge bridge are TS-native.
  • React (Vite + shadcn/ui + Tailwind + TanStack) — the console.

Next: Core Concepts.