Pick how the table is accessed and get the migration: RLS on, one policy per operation, only the grants the API roles need, and the indexes the policies use. Every pattern is tested against Postgres as signed-out and signed-in users.
Then check it: npx airlock-rls fails your CI build if any table ships without RLS or with an always-true policy.
with check, UPDATE gets using and with check so nobody can hand a row to someone else, and UPDATE always has a SELECT policy, without which it silently changes nothing.(select auth.uid()) so Postgres evaluates it once per query, not once per row.grant ... on all tables: it also opens every table with RLS off.security definer, lives in a schema the API doesn't expose, and only returns the caller's teams.Tested: each pattern is applied to Postgres and checked as a signed-out visitor and as two users (own rows only, no cross-user reads or writes, no reassigning rows, no recursion on the membership table).
A policy edited by hand or a table added in a hurry can reopen a leak. airlock-rls blocks it in CI for free; Airlock Monitor re-checks production on a schedule.