What is RLS Policy Recipe?
A tested pattern for row-level policies: scope by owner column, check membership once per statement, and deny by default. BYOB chat schemas gate every table on project or user ownership with zero public fallback.
Example
BYOB’s chat schema gates messages and sessions on project ownership: policies compare auth.uid() to the owner column, with service-role bypass reserved for server jobs.
What people get wrong
Shipping permissive “true” policies during development and forgetting them. Open RLS is silent data exposure; start deny-by-default and open narrowly with tests.
Related terms
Row-Level Security (RLS)
Per-user table policies enforced by the database itself: users can only read or write their own rows. RLS is what makes the anon key safe to ship in client code.
Anon Key vs Service-Role Key
The public client key (safe in browsers, constrained by RLS) versus the privileged server key (bypasses policies, never leaves the server). Mixing them up is the classic Supabase security bug.
RLS Performance Subquery
The slow path where row policies re-evaluate per-row subqueries instead of once per statement. Wrapping the check in a single stable subquery lets Postgres cache the result and skip repeated work.
B-Tree Index
The default balanced-tree structure making equality and range lookups fast without scanning every row. Add one where queries filter, join, or sort — then confirm with EXPLAIN before assuming victory.
Composite Index
One index spanning several columns for queries that always filter them together, ordered most-selective first. A three-column composite beats three single-column indexes on the same query shape.
Partial Index
An index covering only rows matching a predicate, such as live subscriptions where status is active. Smaller, faster, and cheaper to maintain than indexing rows queries never touch.