Row-Level Security
Row-level security is the specific Postgres mechanism — policies forced on every table, keyed to a session-level user identifier — that implements tenant isolation at the database layer.
In short
The concrete "how" behind tenant isolation. Every table has row-level security forced on, checked against a session variable set once per database transaction, so a query only ever sees rows for the current user — a guarantee that holds even against application code that got something wrong.
Also called: RLS
Row-level security is a Postgres feature that restricts which rows a query can see or modify, based on a policy checked against the current database session — in BalanceMCP's case, a session variable identifying which user is making the request. Every table has this forced on, meaning the restriction can't be silently skipped even by a query that doesn't explicitly ask for it.
Mechanically, a transaction sets the current user's identifier once, and every query within that transaction is transparently filtered by Postgres itself against that identifier — not by a WHERE clause someone remembered to add in application code, but by the database engine enforcing the policy on every table, every time.
The word "forced" is doing real work here specifically: Postgres row-level security can otherwise be bypassed by a superuser or a role explicitly marked to skip it. Forcing the policy, and connecting as a role that doesn't carry that bypass privilege, is what makes the guarantee actually apply rather than being merely available and potentially skipped.
This is real defense in depth, not a redundant layer: even a bug in application code that forgot to scope a query to the right user would still be caught by row-level security refusing to return rows outside that session's own tenant, which is exactly the kind of failure a database-level guarantee is meant to survive.
What people get wrong
- Assuming row-level security is optional or a display-layer filter — it's an enforced database policy, checked on every query regardless of application code.
- Assuming a superuser or bypass-RLS database role is automatically covered by the same guarantee — such a role can skip row-level security entirely unless deliberately avoided.
- Treating row-level security as redundant with application-level checks — it's specifically the layer that survives a bug in the application code that got a check wrong.
Common questions
- Can row-level security be bypassed?
- A superuser or a role explicitly marked to bypass row-level security can skip it — which is exactly why connecting as a non-bypass role, with the policy forced, matters for the guarantee to actually hold.
- Is row-level security the same as an application-level permission check?
- No — it's enforced by Postgres itself on every query, regardless of the application code, which is what lets it survive a bug in that application-level code.
Machine-readable: /api/knowledge/concept:row-level-security