How RLS works in PostgreSQL
Ordinary database permissions are all-or-nothing per table: a role can select from orders or it can't. RLS adds a filter per row. You switch it on with ALTER TABLE orders ENABLE ROW LEVEL SECURITY and then write policies with CREATE POLICY. A policy's USING expression decides which existing rows a command can see. Its WITH CHECK expression decides which new or updated rows are allowed. PostgreSQL added RLS in version 9.5.
Three rules catch people out:
- Default deny. Once RLS is on and no policy applies, no rows are visible and none can be changed.
- Policies combine. Permissive policies are joined with OR, so one loose policy opens the table however strict the others are. Restrictive policies are joined with AND.
- Some roles bypass it. Superusers, roles with
BYPASSRLS, and by default the table's owner skip RLS unless the table is set toFORCE ROW LEVEL SECURITY.
Why it matters so much in Supabase
Supabase exposes your tables through an API that the browser calls directly with a public key. Supabase documents that key as safe to ship, because RLS, not secrecy, is what protects the data. That makes RLS the main access control in a Supabase app. A table with RLS off, or a policy of using (true), lets any visitor read the whole table.
Typical policies compare a column to auth.uid(), the ID of the signed-in user. Supabase recommends writing it as (select auth.uid()) so Postgres evaluates it once per statement instead of once per row, and checking that it isn't null, because an unauthenticated request returns null and the policy fails silently. The service role and secret keys bypass RLS, which is why they must never reach a browser. Views bypass RLS by default too, unless they're created with security_invoker = true on Postgres 15 or later.
How Vibely uses it
When Vibely builds a backend in your Supabase project, the agent writes the tables, migrations and RLS policies. It pauses for your approval before running a destructive migration or query. To confirm what's actually live, the deep security scan connects to your linked project and checks RLS two ways. It pulls Supabase's database advisor lints: RLS disabled on a public table, RLS enabled with no policy, a policy on a table with RLS off, exposed auth users, policies that reference user metadata, and security-definer views. It also queries pg_policies directly and reports any policy in the public schema whose USING clause is empty or true as an Error. Both scans are free on every plan.
Related terms
Sources
- Row security policies (PostgreSQL documentation)
- CREATE POLICY (PostgreSQL documentation)
- Row Level Security (Supabase docs)