../constitution/founding-constitution.md
⚠️ Reconstructed from the material we have, not recovered. Tags: [derived] is supported by evidence in the repository · [inferred] is deduced · [proposed] is a gap I filled.
Context
Three shapes were available: a database per tenant, a schema per tenant, or a shared schema with row-level isolation. The first two isolate perfectly and stop scaling at a few hundred tenants — migrations, connection pools and backups all multiply. The third scales and puts the entire isolation burden on getting one thing right, every time. [inferred]Decision
Shared schema withtenant_id on every tenant-scoped table, isolated by row-level security.
[derived]
RLS is the boundary, not a second layer behind application checks. [derived] An application bug
must not be able to cross a tenant, and that is only true if the database refuses.
Database access goes only through getDb(tenantCtx). [derived] There is no path to a connection
without a tenant context.
Every tenant-scoped table also carries cell_id, present from the first migration and unused.
[derived] It is the seam for regional partitioning: when a tenant must be pinned to a region, the
column is already there and populated.
Every uniqueness constraint on tenant-scoped data includes tenant_id. [derived — added 2026-08-17]
Consequences
- One migration, one pool, one backup, for any number of tenants.
- Regional partitioning becomes a routing problem rather than a migration. − Every table needs RLS and every RLS policy needs a denial test. A forgotten policy is a breach. − Noisy-neighbour effects are real and are handled by rate limiting and quotas, not by isolation.
What I could not determine
Whethercell_id was intended as a physical region identifier or a logical shard key. [inferred]
— I have treated it as a region, which is how the data-residency material reads.