Repository navigation
Multi Tenancy and Row Level Security
TL;DR: every tenant-owned table has Postgres row-level security enabled
and forced. The app connects as a non-owner role so RLS applies to it,
and each request sets the current org via SET LOCAL for one transaction.
Every tenant-owned table has Postgres row-level security enabled and
forced (FORCE ROW LEVEL SECURITY — without this, the table's own owner
role bypasses RLS regardless of the policy). The app connects as a separate,
non-owner dmarc_app role specifically so RLS applies to it. Each request
sets app.current_org_id via SET LOCAL for the duration of one
transaction; a small number of cross-org background jobs (the DNS check
sweep, domain verification sweep, retention purge) instead set
app.is_platform_admin = true, which every policy also accepts.
backend/tests/test_rls.py runs against a real Postgres as the
non-owner dmarc_app role: cross-tenant reads are hidden, an unset org
context fails closed, the platform-admin bypass works, a cross-tenant write
is refused by the policy's WITH CHECK, and a meta-guard asserts every
table with an organization_id column has FORCE RLS and a policy, so a
PR that adds a tenant-owned table but forgets its policy fails CI. See
Running the Test Suite.
← Back to the README · Live demo · Report an issue · MIT licensed
- Quick Start: Docker Compose
- Deploying with Portainer
- Deploying on Azure
- Configuration Reference
- Onboarding an Organization
- IMAP Mailbox
- Google Workspace Mailbox
- Sign-in Providers
- Multi-Tenancy & RLS
- DMARC Subdomain Inheritance
- Report Attribution
- SPF Conditional Mode
- Detected Domains
- DNS Check Scheduling
- Domain Rating
🏗️ Architecture
🗺️ Roadmap