Files
nur-falah-prevention/docs/architecture/ramz-tenant-isolation-decision.md
T
wmj 14b34ad532 docs: add architecture visuals and RAMZ tenant isolation decision
Clio/Stripe competitive benchmark, moslem03/04 backend reality check,
and the moslem04 sync API spec, plus the resolved schema-per-tenant
isolation decision that unblocks Phase 2 of the sync spec.
2026-08-18 13:12:05 +08:00

59 lines
2.7 KiB
Markdown

# RAMZ Tenant Isolation — Decision
**Status:** Resolved
**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
## The question
Falah OS runs one shared backend serving three tenants — EstateOS (law firm estate/probate,
Syariah Court), Tanah Hidup (waqf land lease, JKPTG), and Cair (govt case routing). How is
each tenant's data isolated from the others?
Two options were on the table:
1. **Separate database per tenant** — strongest isolation, highest ops overhead.
2. **Row-level RAMZ-gated access in a shared schema** — cheapest, weakest isolation story.
## Decision
**Schema-per-tenant on a single shared Postgres cluster.**
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.
## Why
- The three tenants aren't multiple firms sharing one product (the Clio model) — they're
three structurally different products, each answering to a different regulator
(Syariah Court, JKPTG, government agencies). A `WHERE tenant_id = ?` filter on a shared
table is not an answer a regulator's auditor will accept at face value; a schema
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.
## 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
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.
## Still open
This resolves the isolation *model*. It does not resolve whether Core Ledger already emits
the billing-trigger events Phase 2 needs, or who owns panel-lawyer vetting — both remain
open from the earlier PRD review.