Access Control

Row-level security, explained in plain terms.

Row-level security, or RLS, is a rule that lives in the database and decides which rows each user is allowed to read and write. Instead of trusting the app to filter data correctly, the database itself refuses to hand back rows the caller does not own. It is the wall between one user's data and everyone else's.

Get a quote

Without it, one public key reads the whole table.

Backends like Supabase and PostgreSQL connect to your app through a key, and on many apps that key is public by design. What stops that key from reading every row in every table is row-level security. When it is not enabled, the key reaches everything behind it, and because the app still works perfectly, nobody notices the wall is missing until someone reads the table directly.

What a row-level security policy actually does

A policy is a condition the database checks on every query, per row. A typical one reads: allow a user to select a row only where the row's user_id equals the id of the account making the request. With that policy on, a query for all orders returns only the caller's orders, even if the query asked for everything, because the database filters the rest out before it answers.

The important part is where the check happens. It is enforced by the database, underneath your application code. Even if an endpoint forgets to filter, even if an attacker sends a raw query with the public key, the policy still applies and the unauthorised rows never come back.

Why apps ship without it

On most platforms, row-level security is off by default. A new table is wide open until you add policies, and the app works identically whether they are there or not. When an app is generated quickly, enabling RLS and writing a correct policy for each table is the step most likely to be skipped, because nothing breaks when you skip it. The result is a table that is private in intent and public in fact.

This is the single most common serious finding in AI-built apps: a real database, a public key that is meant to be shared, and no policies deciding who may read what. The fix is not to hide the key. The key is designed to be seen. The fix is the policies.

Getting the policies right

Enabling RLS is only half the job. A table with RLS on but no policies denies everyone, which is safe but breaks the app. A table with a policy that is too loose, for example one that allows any authenticated user to read any row, is barely better than no policy at all. The correct policy expresses your actual rule: this user may read their own rows, an admin may read all rows, a public post may be read by anyone.

Write and read paths need separate thought. A common mistake is to lock down reads and leave writes open, so a stranger cannot see the orders table but can still insert a forged order into it. Every table needs the right rule for select, insert, update and delete.

How to check yours

The fastest check is the two-account test: log in as one user and try to read or write another user's rows through the app. If it works, your policies are missing or too loose. For a thorough check, every table should be queried directly with the public key to confirm it returns only what it should. That endpoint-by-endpoint verification is part of our security audit.

Common questions.

Is row-level security only a Postgres or Supabase thing?
The term comes from PostgreSQL, and Supabase surfaces it directly. Other backends have the same idea under different names: Firebase calls it security rules. The principle is identical, a rule in the data layer that decides who can touch each record.
Does enabling RLS slow my app down?
For normal policies the cost is negligible, and it is the wrong thing to optimise against. A fast app that leaks its data is not a trade worth making. Well-written policies on indexed columns perform fine at scale.
Can you set up my policies for me?
Yes. Our Supabase and security audits go through every table, find the gaps, and write the correct policy for each read and write path.

Related services.

Let's build something real.

Tell us about your app or idea. You'll get a clear plan and a fixed quote back within 24 hours.

Get a quote