{"id":"concept:row-level-security","kind":"concept","label":"Row-Level Security","synonyms":["RLS"],"definition":"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.","summary":"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.","explanation":["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."],"commonMistakes":["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."],"category":"ai-mcp","questions":[{"question":"Can row-level security be bypassed?","answer":"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."},{"question":"Is row-level security the same as an application-level permission check?","answer":"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."}],"url":"/glossary/row-level-security","relatedTo":[{"id":"concept:tenant-isolation","relationship":"related","weight":0.9,"api":"/api/knowledge/concept%3Atenant-isolation"},{"id":"concept:mcp-server","relationship":"related","weight":0.9,"api":"/api/knowledge/concept%3Amcp-server"},{"id":"concept:audit-log","relationship":"related","weight":0.9,"api":"/api/knowledge/concept%3Aaudit-log"},{"id":"guide:what-is-mcp","relationship":"references","weight":0.7,"api":"/api/knowledge/guide%3Awhat-is-mcp"},{"id":"topic:security","relationship":"references","weight":0.7,"api":"/api/knowledge/topic%3Asecurity"}],"referencedBy":[{"id":"concept:mcp-server","label":"MCP Server","url":"/glossary/mcp-server"},{"id":"concept:api-key","label":"API Key","url":"/glossary/api-key"},{"id":"concept:tenant-isolation","label":"Tenant Isolation","url":"/glossary/tenant-isolation"},{"id":"topic:security","label":"The security model for giving an AI access to your books","url":"/accounting-for-ai/security"}],"license":"https://creativecommons.org/licenses/by/4.0/","attribution":"BalanceMCP — https://balancemcp.com"}