RLS & roles
This page is a placeholder for an upcoming topic — shipping alongside the client portal (Phase 3). The URL is stable, so any links you write today won't rot when the real page lands. For now, the `supabase/policies/` directory covers the closest live topic.
What it'll cover
The exact RLS policy attached to every public schema table, plus
how the four user surfaces (/admin, /app portal, ingest API,
unauthenticated marketing) actually authorize a query.
The short version today:
profiles,admins,org_members— RLS-respecting, controlled by who you're authenticated as via Supabase Auth.access_requests— staff-only via the admin server actions.clients,episodes,client_api_keys— service-role only in Phase 1. The admin UI accesses them throughgetSupabaseServiceClient()after a per-route role check; the ingest API does the same aftervalidateApiKey()passes. The client portal (Phase 3) will add narrower RLS that lets a client read its own rows directly.app_public_settings— public read (anon + authenticated), service-role write. This is what makes maintenance mode visible to the marketing edge proxy.
The canonical source is
supabase/policies/
in the monorepo. Every new client-data table needs a policy file in
the same PR as the migration, per AGENTS.md.
Why service-role for client data in Phase 1
It's a smaller blast radius. Phase 1 has no client portal — every
read of clients / episodes / client_api_keys happens from
trusted server code (admin Server Actions, ingest API route handlers)
that's already verified the caller's role / API key. Adding RLS that
mirrors those checks would duplicate logic without adding security.
When the client portal lands in Phase 3, we'll add per-client RLS
so a client's browser session can read its own rows directly,
without going through getSupabaseServiceClient().