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.
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user