ADR 0002: RLS-Based Tenancy + Org-Unit Data Silos
Date: 2025-02-20
Status: Accepted
Context
Evalium must support:
- strict tenant-level isolation
- customer internal subdivision (org units / “silos”)
- future hierarchy (sub-org units)
- high-level admin visibility
- small-team UX where org units shouldn't appear
Initial attempts were middleware filtering or SQL WHERE-based scoping. These proved fragile.
Decision
Evalium adopts PostgreSQL Row Level Security (RLS) as the primary enforcer of all data isolation.
Key rules:
- All user, evaluation, session, submission, and versioned tables include
tenant_idandorg_unit_id. - RLS policies enforce:
- tenant match
- org match
- global admin override
- All DB access goes through TxManager which injects the correct GUCs using
SET LOCAL. - Pool-level GUCs removed.
- Scoped access now verified via shell and Go tests.
Implementation Notes (Current)
- Tenant + org checks are combined in each policy to avoid OR-based bypasses.
- TxManager is the only approved DB entrypoint; direct pool Query/Exec is forbidden because it would skip scope.
- Runtime should use a non-superuser app role (e.g.,
evalium_app) so RLS is enforced; superusers bypass RLS and invalidate silo tests. - Shell regression (org_silos.sh + org_silos_testdb.sh) is the guardrail and must be kept green.
Options Considered
1. App-level filtering (WHERE clauses)
Cons: error-prone; missing filters cause data leaks
2. Hybrid model (filters + partial RLS)
Cons: complicated; still risk of bypass
3. Full RLS + TxManager (chosen)
Pros:
- Cannot accidentally leak data
- Clean separation (auth → GUCs → DB)
- Highly scalable for future staff
- Works with org hierarchies
Cons: - Requires precise testing
- Requires strict DB access patterns (no direct pool use)
Consequences
Positive:
- Secure isolation across tenants/org units
- Future support for sub-orgs
- Defence in depth
Negative:
- Requires strict discipline around TxManager usage
- Slight learning curve for new engineers
Notes
Supersedes older architecture-overview assumptions.
Matches industry best practices (Stripe, GitLab, Heap).