Based on each vendor’s official docs, checked
Supabase RLS for AI-built apps: grants, policies and tests
Coding agents create Supabase tables fast, and a table in an exposed schema without Row Level Security is readable and writable by any role with a grant on it. Supabase’s own docs put it plainly: enable RLS on every table in an exposed schema, and on projects that still grant anon and authenticated by default, revoke those grants, because adding policies doesn’t remove them. This guide follows Supabase’s Row Level Security docs, checked November 1, 2026, and turns them into steps you can hand to Claude Code, Codex, Cursor or Copilot.
1. Enable RLS on the table
Run this in the SQL Editor or, better, in a migration so it is reproducible. Once RLS is on, nothing is readable through the API with a publishable key until you add policies.
alter table public.reports enable row level security;2. Revoke the default grants, then grant back only what is needed
On existing projects a new table in public starts with every privilege granted to anon, authenticated and service_role. Policies decide which rows; grants decide whether a role can run the operation at all. Keep grants and RLS in the same migration.
revoke all on table public.reports from anon, authenticated; grant select, insert, update, delete on table public.reports to authenticated;3. Write one policy per operation
Supabase recommends a separate policy for select, insert, update and delete, each naming its role with to. Insert uses with check; update uses both using (which rows) and with check (what the new row may look like), and needs a matching select policy to work.
create policy "Users can view their own profile." on profiles for select to authenticated using ( (select auth.uid()) = user_id ); create policy "Users can update their own profile." on profiles for update to authenticated using ( (select auth.uid()) = user_id ) with check ( (select auth.uid()) = user_id );4. Prove it with a pgTAP test
Create one test file per table under supabase/tests/ that asserts allow and deny for each operation, for anon and authenticated, then run the suite. Supabase’s point: until the suite passes, you don’t know whether the policies do what you intended.
supabase test new reports_rls supabase test db
Why AI-built apps hit this
An agent asked to “add a todos table” will usually create the table and the client code that reads it, and the app works in the browser. Working is not the same as safe: with default grants and no RLS, the same publishable key in your frontend can read or write every row. Ask your agent to treat enable RLS, revoke, grant and policies as part of every table it creates, in the same migration.
How denials show up in tests
A missing grant or a with check violation raises error 42501. A using clause that filters a row out raises nothing and simply matches zero rows, so assert those with is_empty on the statement with returning, then read the target row to show it is intact. Supabase warns never to prove an allowed write with lives_ok, because it passes when the write matched zero rows.
Mistakes the docs call out
Views bypass RLS by default because they are created by the postgres user; on Postgres 15 and above create them with security_invoker = true. Don’t base policies on user_metadata in the JWT, since signed-in users can change it; store authorization data in app_metadata. auth.uid() returns null for signed-out requests, so make that case explicit. Two tables whose policies read each other fail with 42P17 infinite recursion; break the cycle with a security definer function in a private schema with search_path set to an empty string.
Keep the secret key on the server
A secret key uses the service_role role, which bypasses RLS, so it belongs only in server code, never in the browser. The JWT-based service_role key is legacy; Supabase prefers secret keys. A secret key bypasses RLS only when the request carries no user access token; with a user token, the user’s policies apply.
Keep policies fast
Index every column a policy filters on, such as user_id. Wrap helper calls as (select auth.uid()) so Postgres evaluates them once per statement instead of once per row.
Tips
- Prompt your agent: “For every new table, write one migration that enables RLS, revokes anon and authenticated, grants only what the app needs, adds one policy per operation and a pgTAP test file.”
- Check grants before editing a policy when a request your policy should allow fails with 42501.
- Never let an agent paste a secret key into frontend code or a committed .env file.
Next steps
How to add an MCP server to Codex
How to add an MCP server to Cursor
How to add an MCP server to GitHub Copilot in VS Code
How to add an MCP server to Google Antigravity
How to migrate from Gemini CLI to Antigravity CLI
Portable agent skills: one SKILL.md for every coding agent
Safe “always allow” rules for Claude Code and Codex CLI
GitHub Copilot CLI yolo mode: --allow-all and safer tool rules
Codex sandbox modes and approval policies explained
How to install Claude Code on macOS, Linux and Windows
How to install the OpenAI Codex CLI
Claude Code auto mode: what runs, what gets blocked, how to turn it off
Agent Skills moves to the Agentic AI Foundation: what changes
MCP security: the risks in MCP servers and how to lock them down
Claude Code in VS Code: install, shortcuts and CLI differences
What is AGENTS.md? The open instruction file for coding agents
Claude Code on Windows: native, WSL and common errors
Codex skills: create, install and use SKILL.md skills in Codex
GitHub Copilot CLI: install, sign in and start using it
Cursor Agent SDK: build, test and deploy agents as code
GPT-6.1 Terra in Codex and the API: setup, pricing and limits
MCP OAuth: connect a remote MCP server in Claude Code, Codex and Cursor
Codex config.toml: location, precedence and the settings to change
Codex on Windows: native sandbox setup, WSL and fixes
Antigravity CLI (agy): install, sign in, modes and commands
Google Antigravity on Windows: install, WSL, sandbox and fixes
GitHub Copilot coding agent (now cloud agent): how to use it
GitHub Copilot agent mode: how to use it in your IDE
Claude Code tips from Anthropic’s best practices
Claude Code with OpenRouter: setup, models and troubleshooting
How to add an MCP server: Claude Code, Codex, Cursor, Copilot and Antigravity
Antigravity skills: create, store and run SKILL.md skills
Claude Code in JetBrains IDEs: install, connect and fix issues
GitHub Copilot skills: create, install and use agent skills
Claude Code subagents: create, configure and run them
Check which plugins, hooks and MCP servers your agent loads
Cursor rules examples: .mdc files for each rule type
Fix spawn npx ENOENT when starting an MCP server
How to add a Supabase database with Claude Code
Supabase
Supabase vs Firebase
Safe permission rules for Claude Code and Codex
Frequently asked questions
Postgres Row Level Security: rules written in SQL that run inside the database and act like a WHERE clause added to every query on a table.
Sources
- https://supabase.com/docs/guides/database/postgres/row-level-security · checked 2026-11-01