In place today
One business cannot read another business's rows
Separation between businesses is enforced by PostgreSQL itself, not by application code remembering to add a filter. Every table that carries a business identifier has row-level security enabled and FORCED. The distinction matters: enabling row-level security alone does not apply it to the table's owner, and this application connects to the database as the owner — so enabling without forcing would be decoration.
The reason this is enforced in the database rather than in code is that application filters are only as good as the last developer who remembered one. A missed WHERE clause in a single query is enough to show one company another company's ledger.
A check runs on every build that reads the live database and, for every table carrying a business identifier, asserts four things: that row-level security is on, that it is forced, that a policy actually references the current business, and that platform-wide support access can read but never write. There is no threshold and no sampling. One unprotected table fails the build.
Where to check this
- scripts/check-rls-coverage.mjs — the build gate
- SQL-FILES/0001_rls_and_audit_guard.sql — the original policies