Row-level security (RLS), explained for founders
Last updated: July 26, 2026
Row-level security (RLS) is a database rule that decides which rows each user is allowed to see or change. Without an RLS policy, anyone with your app's public key can read every row in a table, so one user can see another user's data. It is the #1 issue we flag on AI-built apps.
What is row-level security in plain words?
Row-level security is a rule you attach to a database table that says, for each row, "this user is allowed to see it" or "this user is not." A row is one record, like one order, one message, or one profile. RLS makes the database itself enforce who can read and write each one.
Without RLS, the table trusts anyone who can reach it. With RLS, the table checks a policy on every single request, like "only return rows where the user_id matches the person logged in."
Most AI-built apps store data in Supabase (hosted Postgres). Supabase talks to your app using a public key, called the anon key, that is meant to be visible in the browser. RLS, not that key, is what actually protects your data.
Why does a missing RLS policy expose other users' data?
A missing RLS policy exposes data because the public anon key is designed to be visible, and RLS is the only thing standing between that key and your whole table. When RLS is off, anyone who opens your app's network requests can copy the key and read or change every row in the table, not just their own.
Here is the trap that catches AI-built apps: in Supabase, RLS is turned on automatically only when you create a table using the visual Table Editor. Tables created by raw SQL, by a migration, or by an AI coding tool often ship with RLS off. The app looks like it works, because each logged-in user sees their own screen, but the database is not enforcing anything underneath.
This is not theoretical. In May 2025, a security researcher publicly disclosed a flaw (tracked as CVE-2025-48757) affecting more than 170 production apps built on one popular AI builder, with hundreds of live endpoints where tables were readable by anyone holding the public key, exposing data such as emails, phone numbers, payment records, and API keys, all because RLS wasn't protecting them.
A worked example
Say you built a to-do app on Lovable. It has a todos table with a user_id column, so each todo belongs to one person. When Ana logs in, she sees only her todos. Looks perfect.
But if RLS is off on that table, the filtering is only happening in the app's front-end code. The database will hand over everyone's todos to anyone who asks with the public key. A curious visitor opens their browser's network tab, copies the key, sends one request, and receives every user's todos, including anything private they wrote.
The fix is small: turn RLS on for the table and add a policy such as "a user may only read rows where user_id equals their own id." Explain My Build reads your actual schema, tells you which tables are missing this, and hands you a code-verified fix-list you can paste to your AI. See it applied to Lovable and Bolt apps.
Frequently asked questions
Is the Supabase anon key supposed to be secret?
No. The anon (public) key is designed to be visible in the browser. That is why it cannot be your security boundary. Row-level security is what actually protects your data. If you rely on the key being "hidden," your data is not protected. See API key and Supabase.
Why does my AI-built app not have RLS turned on?
Because Supabase only enables RLS automatically for tables you create in its visual Table Editor. Tables created by an AI coding tool, a migration, or raw SQL usually ship with RLS off, and the app keeps working, so nobody notices. That is why it is worth checking explicitly. Run a code read to find out.
How do I know if my tables are missing RLS?
You (or a tool) have to read the database configuration, not just use the app, because a missing policy is invisible from the front end. Explain My Build reads your schema and flags any table the browser can reach without an RLS policy, then gives you the exact fix. This is a code read, not a live penetration test.
Turn your app into a guide your users can follow.
Paste a public GitHub link and get your founder runbook + fix-list free in about a minute — no sign-up. Private code runs entirely in your browser.