Home / RLS / owner_id vs workspace_id

Supabase RLS · Decision

owner_id vs workspace_id: which RLS pattern to use

Postgres 15 · Supabase✓ code tested against a real database
TL;DR

Single-user data? Scope rows to owner_id = auth.uid(). Anything a team shares? Scope to workspace_id + a membership check. If teams are even plausible, start with workspace — retrofitting it later is the painful path.

01 The two models

Owner-scoped — every row belongs to one user. Simplest possible RLS: user_id = (select auth.uid()). Perfect for personal apps, single-player tools, per-user settings.

Workspace-scoped — rows belong to a team/workspace, and membership decides access. More moving parts (a members table, a membership function), but it's the only model that supports sharing, invites, and roles.

02 How to choose

The trap is picking owner because it's easy, then bolting on teams a year later — every table, policy, and query has to change under load. If teams are plausible, the small upfront cost of workspace is cheap insurance.

03 Side by side

owner-scoped

create policy "select own" on notes for select
  using (user_id = (select auth.uid()));

workspace-scoped

create policy "members read" on notes for select
  using (workspace_id in (select public.user_workspace_ids()));

04 Migrating owner → workspace later

If you must retrofit: create a workspace per existing user, backfill workspace_id from user_id, add the membership rows (each user is the sole member of their own workspace), then swap the policies. It's doable, but it's a data migration plus a policy rewrite across every table — which is exactly why starting with workspace is worth considering.

◆ FREE · MIT · npm + GitHub Action

Catch this before it ships

airlock-rls is a CI gate that fails your build when a table ships exposed or a policy is permissive — the same class of bug, caught on the pull request instead of in prod.

npx airlock-rls

Or start from nextjs-supabase-starter — auth + a table with RLS + an isolation test, so a fresh table is safe by default.

FREE PDF

Grab the Supabase RLS cheat sheet

The golden rules, the footguns that leak in prod, correct policy snippets, and the isolation test — on one page.

FAQ

Can I mix both in one app?

Yes, and many apps do — per-user settings stay owner-scoped while shared resources are workspace-scoped. Just be deliberate about which model each table uses, and keep the membership function as the single source of truth for the workspace side.

Is workspace-scoped slower?

Slightly, because each policy resolves membership. Keep it fast by resolving membership once in a stable security-definer function and indexing workspace_id. At normal scale the difference is negligible next to the flexibility you gain.