Kataven separates tenants by database isolation, not row-level filtering.Documentation Index
Fetch the complete documentation index at: https://docs.kataven.ai/llms.txt
Use this file to discover all available pages before exploring further.
One account = one database
Each account has a dedicated PostgreSQL database. The account slug (acme) and the database name are the same string.
There’s no account_id column in any per-account table because there
can be no cross-tenant query — the database is the boundary.
How requests get routed
Every protected request carries anX-Account-ID header. The handler:
- Reads the header.
- Validates the slug against
^[a-z0-9][a-z0-9_-]{0,62}$. - Calls
GetDBForAccount(slug), which returns a pooled*sql.DBfor the per-account database (DSN:postgres://user:pw@host:port/<slug>). - Runs the query on that connection.
400. If the slug fails validation or the
database doesn’t exist → 400 "Invalid account".
Where the header comes from
Two paths setX-Account-ID:
- Explicit — the client sends it (the SDKs do this with the
account_idyou configure). - Auto-injected from JWT claims — the auth middleware extracts the account slug from the bearer token’s Zitadel org claims and sets the header before passing to handlers.
Reserved slug
kataven-admin is rejected with 403. It’s a system administration
account, not a tenant.
Why this design
- Hard isolation. A bug that mis-routes a query just gets you a different DB connection — no possibility of seeing another tenant’s row.
- Per-tenant migrations. Schema changes can roll out per-account.
- Per-tenant performance isolation. A tenant doing a slow query only stresses their own database.
- Backup / restore is a tenant-level operation. Restoring one account doesn’t touch any other.
accounts.go pool manager caches and reaps idle pools to
keep this manageable.
What lives in the system DB
A separatesystem database holds the accounts table —
account_id, account_name, database_name, status. The public
/api/accounts/validate/{account} endpoint reads from this DB to
answer “does this account exist and is it active?” before any tenant
DB lookup.