# ADR-002 Isolate tenants with PostgreSQL Row-Level Security ## Status Accepted, 2026-05-17. ## Context Лидерра is a multi-tenant SaaS CRM: many tenants share one PostgreSQL database. Tenant isolation is a security boundary — a cross-tenant data leak is a reportable personal-data incident under 152-ФЗ. The boundary has to hold even when application code has a bug. This decision predates the ADR process and is recorded retroactively (see ADR-000) because it is the load-bearing data-layer decision the rest of the backend depends on. ## Decision Every tenant-scoped table carries PostgreSQL Row-Level Security policies. The current tenant identifier is set per transaction via `SET LOCAL app.current_tenant_id` from the `SetTenantContext` middleware. The database defines five roles; `crm_supplier_worker` holds `BYPASSRLS`, so any queued job running as that role must filter `tenant_id` explicitly in its queries — Row-Level Security will not do it for that role. ## Alternatives Considered - **Application-layer scoping only (global Eloquent query scopes).** Rejected: a single forgotten scope, a raw query, or a new developer unaware of the convention leaks cross-tenant rows; defense in depth requires the isolation to live in the database. - **One database per tenant.** Rejected: the operational cost grows linearly with tenant count — every migration runs N times, every backup multiplies — and the project specification targets shared-schema multi-tenancy. ## Consequences **Positive:** - A cross-tenant read or write is blocked by the database even when the application layer has a bug, a missing scope, or a raw query. - The isolation rule is auditable in one place — the policy set in `db/schema.sql`. **Negative:** - Every new tenant-scoped table must ship an RLS policy plus a `db/CHANGELOG_schema.md` entry; omitting the policy is a silent security hole until it is caught. - `BYPASSRLS` roles such as `crm_supplier_worker` need careful review — code running as them carries the isolation responsibility the database otherwise enforces. ## Related Decisions - ADR-000 — defines the ADR process under which this record was written. ## References - `db/schema.sql` — the RLS policy set and role definitions. - `db/00_create_roles.sql` — the five production database roles. - `app/` Laravel middleware `SetTenantContext` — sets the per-request tenant. - `app/` feature test `RlsSmokeTest` — the RLS isolation smoke test.