RLS Studiobeta
Guide

Lock down your Supabase tables, without writing SQL

Pick who can read and write each table in plain English. RLS Studio generates production-ready Postgres Row Level Security policies you can paste straight into Supabase.

It all runs in your browser, so your schema never leaves this page.

Quick start

Who can access this table?Each rule lets a group of people do certain things. Rules stack up, so more rules means more access.

Rule 1

Who

Can

Rule 2

Who

Can

Fine-tune by roleGive specific roles (like admin or member) specific permissions. Leave empty if you don't use roles.

Your roles (shared across all tables)
adminmemberviewer
ROLEReadAddEditDel
Role settings· scope: all rows

Tip: click a role name to toggle all of its permissions at once.

rls_policies.sql
-- Generated by RLS Studio
-- Review every policy before running against production.
-- Permissive policies on the same command combine with OR.
-- ============================================================
-- Table: public.posts
-- ============================================================
alter table "public"."posts" enable row level security;

-- Anyone (incl. anonymous) can read.
drop policy if exists "posts_select_public" on "public"."posts";
create policy "posts_select_public"
  on "public"."posts"
  for select
  to anon, authenticated
  using ( true );

-- Owner can insert their own rows.
drop policy if exists "posts_insert_owner" on "public"."posts";
create policy "posts_insert_owner"
  on "public"."posts"
  for insert
  to authenticated
  with check ( (select auth.uid()) = user_id );

-- Owner can update their own rows.
drop policy if exists "posts_update_owner" on "public"."posts";
create policy "posts_update_owner"
  on "public"."posts"
  for update
  to authenticated
  using ( (select auth.uid()) = user_id )
  with check ( (select auth.uid()) = user_id );

-- Owner can delete their own rows.
drop policy if exists "posts_delete_owner" on "public"."posts";
create policy "posts_delete_owner"
  on "public"."posts"
  for delete
  to authenticated
  using ( (select auth.uid()) = user_id );

-- ============================================================
-- Table: public.documents
-- ============================================================
alter table "public"."documents" enable row level security;

-- Org members can read rows in their org.
drop policy if exists "documents_select_org" on "public"."documents";
create policy "documents_select_org"
  on "public"."documents"
  for select
  to authenticated
  using ( org_id in (
      select org_id from "public"."org_members"
      where user_id = (select auth.uid())
    ) );

-- Org members can insert rows in their org.
drop policy if exists "documents_insert_org" on "public"."documents";
create policy "documents_insert_org"
  on "public"."documents"
  for insert
  to authenticated
  with check ( org_id in (
      select org_id from "public"."org_members"
      where user_id = (select auth.uid())
    ) );

-- Org members can update rows in their org.
drop policy if exists "documents_update_org" on "public"."documents";
create policy "documents_update_org"
  on "public"."documents"
  for update
  to authenticated
  using ( org_id in (
      select org_id from "public"."org_members"
      where user_id = (select auth.uid())
    ) )
  with check ( org_id in (
      select org_id from "public"."org_members"
      where user_id = (select auth.uid())
    ) );

-- Org members can delete rows in their org.
drop policy if exists "documents_delete_org" on "public"."documents";
create policy "documents_delete_org"
  on "public"."documents"
  for delete
  to authenticated
  using ( org_id in (
      select org_id from "public"."org_members"
      where user_id = (select auth.uid())
    ) );

-- ============================================================
-- Table: public.projects
-- ============================================================
alter table "public"."projects" enable row level security;

-- Roles [admin, member, viewer] can read.
drop policy if exists "projects_select_role" on "public"."projects";
create policy "projects_select_role"
  on "public"."projects"
  for select
  to authenticated
  using ( (select (auth.jwt() ->> 'role')) in ('admin', 'member', 'viewer') );

-- Roles [admin, member] can insert.
drop policy if exists "projects_insert_role" on "public"."projects";
create policy "projects_insert_role"
  on "public"."projects"
  for insert
  to authenticated
  with check ( (select (auth.jwt() ->> 'role')) in ('admin', 'member') );

-- Roles [admin, member] can update.
drop policy if exists "projects_update_role" on "public"."projects";
create policy "projects_update_role"
  on "public"."projects"
  for update
  to authenticated
  using ( (select (auth.jwt() ->> 'role')) in ('admin', 'member') )
  with check ( (select (auth.jwt() ->> 'role')) in ('admin', 'member') );

-- Roles [admin] can delete.
drop policy if exists "projects_delete_role" on "public"."projects";
create policy "projects_delete_role"
  on "public"."projects"
  for delete
  to authenticated
  using ( (select (auth.jwt() ->> 'role')) in ('admin') );
posts: Assumes a "user_id" uuid column referencing auth.users(id).
documents: Assumes "org_id" plus a "org_members" table. Enable RLS on that table too.
projects: Reads the "role" JWT claim. Set it with a custom access token hook.
auth.uid()/auth.jwt() are wrapped in subselects for the Postgres initplan optimization.
Apply it:1. Copy2. Supabase → SQL Editor3. Paste & RunOpen SQL Editor ↗