Skip to content

Multi-tenancy ladder (L1–L5)

LevelMechanismCostShips in
L1Branded ID types in Drizzle schemafreev0
L2ORPC + Hono middleware (requireOrg)freev0
L3Postgres RLS via Better-Auth claimslowv1
L4Schema-per-tenantmediumv2
L5DB-per-tenant (regulated)highv3

Tenant-as-afterthought is the most expensive mistake a platform can make. The cost of L1 is one type definition. The cost of retrofitting L4 onto a single-tenant codebase is months of work. The decision to ship L1 + L2 from day 1 is what the plan calls the cheapest insurance you’ll ever buy.

@ahamie/schema exports OrgId, UserId, AgentRunId, etc. as nominal types. The Drizzle schema widens the column with .$type<OrgId>(). So a query like eq(automation_runs.org_id, runId) fails to typecheck — RunId is not assignable to OrgId. Cross-tenant slip-ups become compile errors.

requireOrg(auth) rejects any request that doesn’t carry an active org claim. The Hono context exposes c.get("ahamie_auth").org_id for downstream queries.

Postgres Row-Level Security is enabled per tenant. The Drizzle adapter picks up the active session’s org_id claim and Postgres filters every query. Defense in depth on top of L2.

One Postgres schema per organization. The connection’s search_path is set per request. Useful for regulated tenants who need physical separation.

One Postgres database per organization. The connection pool is per-tenant. The most expensive option; reserved for tenants under FedRAMP / HIPAA boundaries.