Dental Platform · Internal Docs
ArchitectureDecision records (ADR)

ADR 0004 — PostgreSQL 18 with row-level security for tenant isolation

Status: Accepted

Owner: Platform EngineeringLast reviewed:

Status: Accepted · Date: 2026-07-23

Context

Cross-tenant PHI exposure is the highest-impact failure. Application WHERE clauses alone are one forgotten predicate away from a breach.

Decision

Every tenant-owned table carries clinic_id (and organization_id where the row is org-scoped). RLS is enabled and FORCED on all tenant tables. The API role (dental_app) has no BYPASSRLS. core-api sets per-transaction GUCs:

SELECT set_config('app.org_id',  $1, true);
SELECT set_config('app.clinic_ids', $2, true);  -- csv of authorized clinics
SELECT set_config('app.actor_id', $3, true);
SELECT set_config('app.purpose', $4, true);

Policies compare row tenancy against those GUCs. Platform-admin access flows through a separate audited role (dental_platform) whose every statement is wrapped by a support-access grant row (break-glass, time-boxed, fully audited). Migrations run as dental_migrator.

Consequences

  • Defense in depth: application authz AND database policy must both fail for a cross-tenant read to occur.
  • All connection acquisition goes through one pool helper that refuses to hand out a connection without tenancy GUCs (except explicitly-marked system jobs).
  • Automated cross-tenant tests assert zero leakage per table.

On this page