Engineering
· 11 min read

How we use Postgres RLS for true multi-tenant isolation

Every table, every query, every test. The pattern, the helper functions, and the audit script.

Multi-tenancy without RLS is multi-tenancy with bugs. We enable Row Level Security on every table, scope every policy to a helper function, and run a cross-tenant audit script before every deploy.

The helper function pattern

get_my_tenant_id() returns the current user’s tenant_id by reading the tenant_users table. SECURITY DEFINER + STABLE means it’s safe and fast. Every policy reads it: tenant_id = get_my_tenant_id() OR is_platform_admin().

The audit script

Every CI run executes scripts/rls-audit.ts which logs in as user A and tries to read user B’s data across every table. If any row leaks, the build fails.

What it doesn’t protect against

Bugs in the application layer that bypass RLS by using the service-role key. Our rule: service-role client is server-only, never imported from a Client Component, and every server-side use is reviewed.

Ready to ship faster?

Start with a free trial — no credit card needed for sandbox mode.