docs: revise RAMZ isolation decision to database-per-tenant
Schema-per-tenant was the original call, but falah-os-master's infrastructure/postgres/init.sql already establishes database-per-service as the platform convention (falahid, falahwallet, falahramz, falahmock, falahistore). Database-per-tenant matches that pattern and is a stronger isolation boundary than schemas would have been.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# RAMZ Tenant Isolation — Decision
|
||||
|
||||
**Status:** Resolved
|
||||
**Status:** Resolved (revised 2026-08-18 — see amendment below)
|
||||
**Blocks:** Phase 2 of the [moslem04 Sync API spec](./sync-api-spec.html) (admin console read view)
|
||||
**Originally flagged in:** Nur Falah Admin Console PRD review
|
||||
|
||||
@@ -17,10 +17,19 @@ Two options were on the table:
|
||||
|
||||
## Decision
|
||||
|
||||
**Schema-per-tenant on a single shared Postgres cluster.**
|
||||
**Database-per-tenant on the shared Postgres instance.**
|
||||
|
||||
Each tenant (EstateOS, Tanah Hidup, Cair) gets its own Postgres schema. Not a separate
|
||||
server, not a shared table filtered by a `tenant_id` column.
|
||||
Each tenant (EstateOS, Tanah Hidup, Cair) gets its own Postgres *database* — `falahestateos`,
|
||||
`falahtanahhidup`, `falahcair` — not a schema, not a shared table filtered by a `tenant_id`
|
||||
column.
|
||||
|
||||
> **Amendment (2026-08-18):** this was originally decided as schema-per-tenant. Inspecting
|
||||
> `falah-os-master/infrastructure/postgres/init.sql` showed the codebase already has an
|
||||
> established convention — **database-per-service** (`falahid`, `falahwallet`, `falahramz`,
|
||||
> `falahmock`, `falahistore`, all separate databases in one Postgres instance, not schemas).
|
||||
> Database-per-tenant is a stronger isolation boundary than schema-per-tenant anyway, and it
|
||||
> keeps one consistent pattern across the platform instead of introducing a second, competing
|
||||
> one. Revised to match.
|
||||
|
||||
## Why
|
||||
|
||||
@@ -31,25 +40,25 @@ server, not a shared table filtered by a `tenant_id` column.
|
||||
boundary is something that can actually be demonstrated.
|
||||
- This was flagged as a trust/compliance blocker for the UK Innovator Visa pitch — the
|
||||
isolation model isn't just an engineering choice, it's part of the pitch.
|
||||
- Fully separate databases (or separate servers) solve the same trust problem but multiply
|
||||
ops burden — separate backup policies, connection pools, migrations to run three times
|
||||
instead of once. Not justified at current scale.
|
||||
- Schema-per-tenant keeps one cluster, one backup policy, one connection pool — while still
|
||||
giving each tenant a real, auditable namespace boundary.
|
||||
- Separate databases on one Postgres instance is the platform's existing convention, not a
|
||||
new ops burden — `falah-os-master` already runs 5 databases this way. Adding 3 more
|
||||
follows the grain of the system instead of cutting against it.
|
||||
|
||||
## What RAMZ still does
|
||||
|
||||
RAMZ doesn't disappear — it moves down a level. Within each tenant's schema, RAMZ handles
|
||||
the finer-grained access control that a schema boundary can't: which user or law firm
|
||||
RAMZ doesn't disappear — it moves down a level. Within each tenant's database, RAMZ handles
|
||||
the finer-grained access control that a database boundary can't: which user or law firm
|
||||
within EstateOS can see which case, which land officer within Tanah Hidup can see which
|
||||
lease. RAMZ was never going to be strong enough to be the *only* thing standing between
|
||||
tenants — now it doesn't have to be.
|
||||
|
||||
## Consequence for the sync API
|
||||
|
||||
The `tenant_id` field in the [sync spec](./sync-api-spec.html) becomes a schema selector,
|
||||
not a row filter. The admin console's `/v1/admin/metadata` endpoint queries across schemas
|
||||
explicitly, per-tenant — there is no accidental cross-tenant query path.
|
||||
The `tenant_id` field in the [sync spec](./sync-api-spec.html) becomes a database selector
|
||||
(one connection pool per tenant), not a row filter. The `moslem-admin` service's
|
||||
`/v1/admin/metadata` endpoint holds one Postgres pool per tenant database — there is no
|
||||
shared table and no accidental cross-tenant query path, because a query issued against one
|
||||
pool cannot physically reach another tenant's rows.
|
||||
|
||||
## Still open
|
||||
|
||||
|
||||
Reference in New Issue
Block a user