Multi-tenancy & auth

Physical dir-per-tenant isolation with a process-per-tenant model, a verify-only JWT tenant guard, and the forgedb tenant CLI — plus the honest single-tenant-per-process limit.

ForgeDB's multi-tenancy is physical: one tenant per data directory, one server process per tenant, N processes behind a plain host/subdomain proxy. Generated code stays tenant-oblivious — it opens the data root it is handed. A verify-only JWT guard checks that the caller's token names the tenant this process serves.

The model: process-per-tenant#

Each tenant is its own data directory. A process is pointed at one root and serves only that tenant; the multiplexer is a reverse proxy out in front, not code inside ForgeDB. Turning tenancy on or off changes nothing about the generated code.

The scaffolded main.rs is an env-driven server. Resolve the tenant, data root, and JWT config once, and it feeds both the data-directory open and the auth cross-check:

# run the compiled server binary (e.g. ./target/release/<app>), not a forgedb subcommand
FORGEDB_TENANT=acme \
FORGEDB_DATA=/srv/tenants/acme \
FORGEDB_JWT_ISSUER=https://auth.example.com \
FORGEDB_JWT_AUDIENCE=forgedb \
  ./target/release/myapp

The tenant guard — verify-only JWT#

When configured, the forgedb-auth substrate crate wraps every REST and WebSocket data route. It verifies the caller's asymmetric JWT, extracts the configured tenant claim, and cross-checks it against this process's tenant — a mismatch is a 403.

Ops routes stay unauthenticated

The tenant guard wraps only the data routes. The ops routes (/health, /ready, /metrics, /snapshot) are merged in after the guard, so k8s/load-balancer probes need no JWT.

See tenant & auth configuration for the [tenant] / [auth] config tables (these live in forgedb.toml, never in your .forge schema).

The forgedb tenant CLI#

Manage the per-tenant data directories under a root:

forgedb tenant create acme
forgedb tenant list
forgedb tenant drop acme

Limits#

  • Single tenant per process. A cross-tenant read is a fan-out across processes; there is no in-process registry (that model is a deferred strict superset on the same open_at seam).
  • WebSocket clients must send the token in the Authorization header (there is no query-param token path).
  • JWKS-over-HTTP fetch is not yet wired into the scaffold — the crate parses JWKS offline, and static PEM is the wired path today.
  • Verify-only: no token issuance and no per-principal (RLS-style) authorization.

Migrations sweep all tenants under a root in one command — see Migrations.

Search documentation

Find pages across the ForgeDB docs