Data isolation in a multi-company system: why RLS in the database and not only a filter in the application
Expert knowledge. Content verified: 1 November 2026.
In a system where one installation serves many companies, the most dangerous bug is a query that forgot to add the "only my company" condition. When isolation relies solely on that condition in application code, a single oversight is enough for a leak. That is why isolation is worth anchoring in the database.
What RLS is
RLS (row-level security) is a database mechanism that itself adds a row access condition to every query. The application declares which company it acts for, and the database returns only that company's rows, even if the query did not ask for it.
Rules that make the difference
- Every table with company data has a company identifier and an access policy; the rule is enforced even for the table owner.
- The company context is set locally for a transaction, not for the connection. This way the connection pool never carries one company's context into another request.
- The application connects with a role that cannot bypass the rules. Cross-company reports (for example the support panel) use a separate read-only role, in read-only transactions.
- An accounting office reaches a client through membership in that client's company, not through a "back door" between companies.
How to test it
- For every table, test an attempt to read someone else's data: the response should be empty, not an application error.
- Test an attempt to write with another company's identifier: the database should reject it.
- Add an isolation test to every new table and make it a condition for deployment.