Row-policy centered
Operationally compact, with greater dependence on correct policy and role use.
I needed tenant separation to survive more than a remembered filter. The design had to align trusted identity, PostgreSQL privileges, row policy, and tests that could genuinely fail.
A missing WHERE tenant_id = ? can turn an ordinary
request into a disclosure. That makes application discipline useful,
but insufficient as the only boundary.
This entry records a defense-in-depth design direction for CareFlow. It is not a universal database recipe and does not claim that any single mechanism creates compliance.
Tenant count, data sensitivity, recovery boundaries, operational load, and customer evidence all affect the choice.
Operationally compact, with greater dependence on correct policy and role use.
A structural boundary with migrations and provisioning that must stay coordinated.
Stronger isolation possibilities with higher operational cost.
Each layer answers a different authorization question.
Tenant authority comes from validated, trusted context—not a client-supplied tenant field.
Narrow roles constrain reachable namespaces and operations.
Row-level policy is a backstop for shared relations, not the whole design.
Connection pools make leftover session state a cross-request risk.
A privileged local identity can make row-policy tests meaningless.
BYPASSRLS always bypass row security. Table owners
normally bypass it too unless row-level security is forced. Test
through a dedicated non-superuser role that resembles the production
path.
| Probe | Expected evidence |
|---|---|
| Own-tenant read and write | Allowed through the application-like role |
| Other-tenant read | No rows disclosed |
| Other-tenant insert or update | Rejected by privilege or policy |
| Pooled connection reuse | Prior tenant authority does not survive |
| Migration regression | All probes repeat in CI |