supabase-rls
Skill0 starsChanged 26 days agoMITView on GitHub
Write Supabase migrations, RLS policies and grants that do not leak data. Use when creating or changing Supabase tables, policies, grants, views or security definer functions, when handling the service_role key, or when debugging 42501 permission denied, 42P17 infinite recursion, PGRST116, PGRST205, or an update/delete that silently changes nothing.
1 other project uses a version of this.
Adopted by
Most recently taken up by
- penalverbj/ClaudeSkillstheir own version, first seen Mar 8, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 1
- penalverbj/ClaudeSkills★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository2 other files it gives its agents.
- AGENTS.mdAGENTS.md
- .cursor/rules/supabase-rls.mdcCursor rule
The file
We split this skill into the rules it gives an agent
8 rules. About 0 are common, 2 are distinctive, and 6 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 16
Never "fix" a 42501 with grant ...
- Very specific: line 20
Write one policy per operation (for select, for insert, for update, for delete) and name the role with to authenticated (or to anon).
- Very specific: line 23
No policy protects a column it doesn't mention: an owner policy on user_id won't stop a user from changing is_admin on their own row.
- Distinctive: line 24
UPDATE also needs a SELECT policy for the same rows, or it silently affects 0 rows.
- Very specific: line 25
Never use using (true) / with check (true) on private data.
- Very specific: line 36
Never put the service_role / sb_secret_...
- Very specific: line 47
Test policies as the real roles (anon, authenticated as user A and as user B), not in the SQL editor, which bypasses RLS.
- Distinctive: line 48
Check what the API roles can reach:
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Supabase RLS and grants: rules for writing secure migrations
Apply these whenever you create or change a Supabase table, view, function, policy or grant, or write client code that reads or writes Supabase data.
Every table in an exposed schema
- Enable RLS in the same migration that creates the table:
alter table public.x enable row level security;. A table must never exist without it. - RLS on with no policy denies everything. Add the policies the app needs in that same migration.
- Grant explicitly, per table, only to the role that needs it and only the operations the app uses:
grant select on public.x to authenticated;New tables don't get automatic grants foranon/authenticated/service_role(Supabase change of Oct 30, 2026); without a grant the API returns42501 permission denied for table. - Never "fix" a 42501 with
grant ... on all tables in schema public to anon, authenticated. It also grants every table with RLS off, making it readable and writable with the public anon key. - Grant to
anononly for data that signed-out visitors should read, and pair it with a policy that allows exactly that.
Policies
- Write one policy per operation (
for select,for insert,for update,for delete) and name the role withto authenticated(orto anon). - Wrap
auth.uid()as(select auth.uid())so Postgres evaluates it once per query, not per row. - INSERT policies need
with check(they can't haveusing, and withoutwith checkthey let no rows in). On UPDATE, write bothusing(which rows) andwith check(what they may become). Ifwith checkis missing, Postgres checks the new row againstusing, which is only safe whenusingis as strict as what a row may become: with anor(shared or team rows) a row can be handed to another user. - No policy protects a column it doesn't mention: an owner policy on
user_idwon't stop a user from changingis_adminon their own row. For sensitive columns, grantupdateonly on the columns users may change (grant update (body) on public.notes to authenticated;). - UPDATE also needs a SELECT policy for the same rows, or it silently affects 0 rows.
- Never use
using (true)/with check (true)on private data. Allow it only for data that is public on purpose, and say so in a comment. - A policy must not query the table it protects (that raises
42P17 infinite recursion detected in policy). Put the lookup in asecurity definerfunction in a non-exposed schema (e.g.private), withset search_path = '', schema-qualified names, a filter on(select auth.uid()),revoke all ... from publicandgrant executeonly toauthenticated. - Multi-tenant data: scope rows to the caller's tenant through a membership lookup (the helper above), never through a value the client sends.
Views, functions, other schemas
- Views run with the owner's privileges by default and skip RLS. Create them with
with (security_invoker = true), or don't expose them. security definerfunctions bypass RLS: keep them out of exposed schemas unless they are meant as an API, and validate the caller inside.- A table in a non-
publicschema needs that schema in the Data API's exposed schemas,grant usage on schema ... to <role>, plus the table grant and RLS.
Keys and clients
- The
anon/ publishable key is public by design; RLS is what protects data. - Never put the
service_role/sb_secret_...key in frontend code,NEXT_PUBLIC_*/VITE_*env vars, or anything shipped to the browser. It bypasses all RLS. Use it only on the server. - On the server, create the Supabase client from the user's session (request cookies) so
auth.uid()resolves; otherwise queries run asanon.
Errors and what they mean
42501 permission denied for table|schema: missing grant or schema usage (before RLS runs).new row violates row-level security policy: an INSERT/UPDATE failedwith check.PGRST116 Cannot coerce the result to a single JSON objectwith.single()and a row that exists: RLS is hiding it from this caller (or there's no session). Use.maybeSingle()only when 0 rows is a normal answer.- An update or delete that returns no error but changes nothing: no matching UPDATE/DELETE (or SELECT) policy. Chain
.select()to see the affected rows. PGRST205 Could not find the table ... in the schema cache: wrong name/schema, schema not exposed, or a stale cache (notify pgrst, 'reload schema';).
Prove it
- Test policies as the real roles (
anon,authenticatedas user A and as user B), not in the SQL editor, which bypasses RLS. - Check what the API roles can reach:
select c.relname, has_table_privilege('anon', c.oid, 'select') as anon_can_read, c.relrowsecurity as rls_on from pg_class c join pg_namespace n on n.oid = c.relnamespace where n.nspname = 'public' and c.relkind = 'r' order by 1;anon_can_read = truewithrls_on = falsemeans the table is public. - Gate it in CI:
npx airlock-rlsfails the build when a table ships without RLS or with an always-true policy.
Guides with tested SQL for each rule: https://shipsealed.com/rls/
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

