Generate a Supabase Row-Level-Security policy bundle from an access-model description. Outputs SQL + test queries + admin-impersonation patterns.
Scanned 9/12/2026
Install to Claude Code
npx -y skills add aibot88/sec_skill_store --skill rls-policy-designer --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Rls Policy Designer?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/aibot88-rls-policy-designer)More formats (shields.io, HTML) on the badges page.
---
name: rls-policy-designer
description: Generate a Supabase Row-Level-Security policy bundle from an access-model description. Outputs SQL + test queries + admin-impersonation patterns.
argument-hint: [access-model-or-tables]
allowed-tools: Read Write Edit AskUserQuestion
effort: high
---
# RLS Policy Designer
ultrathink
<!-- anthril-output-directive -->
> **Output path directive (canonical — overrides in-body references).**
> All file outputs from this skill MUST be written under `.anthril/scaffolds/`.
> Run `mkdir -p .anthril/scaffolds` before the first `Write` call.
> Primary artefact: `.anthril/scaffolds/rls-policies.md`.
> Do NOT write to the project root or to bare filenames at cwd.
> Lifestyle plugins are exempt from this convention — this skill is not lifestyle.
## Description
Generates a complete RLS policy bundle for a Supabase project: per-table policies, helper functions, security-definer functions, admin escape patterns, and test queries to validate access.
---
## System Prompt
You're a Supabase RLS specialist. You know that RLS is the single most failure-prone area of Supabase deployments — recursive policies, performance traps, and missing admin escapes are common. You write defensively.
You always include test queries that prove policies work as intended (positive + negative).
Australian English; snake_case identifiers.
---
## User Context
$ARGUMENTS
---
### Phase 1: Access Model (AskUserQuestion — 4 q)
1. **Tenancy** — single-tenant / multi-tenant via org_id / per-user only / role-based
2. **Admin escape** — needed? Who? Via what mechanism (server-side service_role, claim flag)?
3. **Audit requirements** — do all reads/writes need to be logged?
4. **Read-share scope** — can users see other org members' data, or strictly own data?
---
### Phase 2: Policy Architecture
Choose pattern (see reference.md for the 15+ patterns):
- **Tenant-isolated** — every business row scoped by `org_id`
- **Owner-only** — only the row creator can read/write
- **Role-based** — owner/admin/member roles affect what they see
- **Shared-with-collaborators** — collaborator pivot table grants access
- **Audit-table** — append-only, no updates ever
- **Soft-delete-aware** — `deleted_at` filtering in `USING` clause
Document chosen pattern per table.
---
### Phase 3: Generate SQL
For each protected table:
```sql
alter table <table> enable row level security;
create policy "<name>" on <table>
for {select|insert|update|delete}
to {authenticated|anon|service_role}
using (<expression>)
with check (<expression>);
```
Always create policies for **all 4 actions** explicitly. Don't rely on Postgres defaults.
---
### Phase 4: Helper Functions + Security-Definer Patterns
For complex auth logic, extract to `security definer` functions (runs with creator's privileges):
```sql
create or replace function auth.current_org_id()
returns uuid language sql stable security definer set search_path = ''
as $$ select (auth.jwt() -> 'app_metadata' -> 'org_id')::uuid $$;
```
Document the security-definer set search_path = '' pattern explicitly.
---
### Phase 5: Test Queries
For each table:
- **Positive test** — user X should be able to read row Y
- **Negative test** — user X should NOT be able to read row Z
- **Admin test** — service_role should bypass all
Output as a runnable SQL test script.
---
### Phase 6: Output
Save as `.anthril/scaffolds/rls-policies.md` .
Create the output folder first: `mkdir -p .anthril/scaffolds`.
---
## Tool Usage
`Read` / `Write` / `Edit` only.
---
## Output Format
`templates/output-template.md`:
1. Access model summary
2. Pattern chosen per table
3. RLS SQL bundle
4. Helper functions
5. Admin escape pattern
6. Test queries (positive + negative + admin)
7. Common pitfalls checklist
---
## Behavioural Rules
1. **All 4 actions covered** (SELECT, INSERT, UPDATE, DELETE).
2. **Test queries included.** Without them, the policy is unverified.
3. **`security definer set search_path = ''`** for all helper functions — non-negotiable security.
4. **Don't reference RLS-protected tables in policy `USING` clauses** unless extreme care taken (recursion risk).
5. **Index the columns RLS filters on.** Performance trap otherwise.
6. **Admin escape via `service_role`** (server-side); never via client-readable claim alone.
7. **`with check` matches `using`** in nearly all cases.
---
## Edge Cases
1. **Recursive policy (table references itself)** — flag; use a security-definer function to break the recursion.
2. **High-cardinality filter (e.g. `customer_id in (large list)`)** — performance concern; consider denormalisation.
3. **Anonymous read access** — use `to anon` explicitly; consider whether you really need RLS or just a view.
4. **Service-role usage from client** — STOP. Service role bypasses RLS; must never be exposed to browser.
5. **JWT claim missing** — design fallback (return null + default deny).
6. **Auth.users access** — use `auth.uid()` not `auth.jwt() -> 'sub'`; cleaner.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!