# Launch Checklist — Supabase RLS Pattern Library

All 18 patterns are built. This checklist is what Keita needs to do to go live.
Estimated time: 45-60 minutes total.

---

## Step 1 — Final product review (~10 min)

- [ ] Skim the 8 AI-gap `.md` files to confirm the failure modes are clearly explained
- [ ] Confirm the 10 standard patterns are readable and self-contained
- [ ] Check README.md reads correctly as the buyer's first document

Quick sanity test for the signature hook:

```bash
# Test that the anon-role omission SQL is valid PostgreSQL syntax
psql "$DATABASE_URL" -f builds/idea-06-supabase-rls/ai-gaps/01-anon-role-omission.sql
```

---

## Step 2 — Create the zip (~5 min)

```bash
cd ~/Documents/Vaults/ClaudeMemory/01-Projects/Ventures/venture-agent
zip -r supabase-rls-patterns-v1.0.zip builds/idea-06-supabase-rls/ \
  --exclude "*.DS_Store" \
  --exclude "__MACOSX/*"
```

Verify the zip contains 37 files:
- README.md
- LAUNCH-CHECKLIST.md (exclude from zip — internal only)
- 8 pairs in ai-gaps/ (16 files)
- 4 pairs in multi-tenant/ (8 files)
- 2 pairs in jwt-claims/ (4 files)
- 4 pairs in storage/ (8 files)

Exclude LAUNCH-CHECKLIST.md from the zip — it's internal.

---

## Step 3 — Create the Gumroad product (~15 min)

Go to [gumroad.com](https://gumroad.com) → Products → New Product → Digital product

**Title:** Supabase RLS Pattern Library: 18 Production-Ready Policies (+ 8 AI Gets Wrong)

**Price:** $24

**Description** (paste this):

---

Supabase RLS protects your data — when it's written correctly. The problem: AI tools (Claude, ChatGPT, Copilot) consistently produce structurally wrong RLS code for 8 specific patterns. Not "could be improved" wrong. Silently-broadens-access wrong.

**This pack gives you:**

- **8 AI-gap patterns** — the exact cases where AI generates broken code, with the correct version and a test to verify
- **4 multi-tenant patterns** — org-level row isolation, INSERT enforcement, admin cross-tenant access, soft delete
- **2 JWT claim patterns** — safe custom role extraction, org-scoped access without DB lookups
- **4 Supabase Storage patterns** — private buckets, team sharing, public CDN buckets, signed URLs

Each pattern: a drop-in `.sql` file + a `.md` explaining what it does, why AI gets it wrong, and how to test it.

**Who this is for:** developers building on Supabase who want production-correct RLS without debugging silent failures.

Try this on any AI tool to see the failure in action:
> "Write a Supabase RLS policy for a multi-tenant app where each row has an org_id"

Then check the result against pattern 09 in this pack.

---

**File:** supabase-rls-patterns-v1.0.zip
**Support:** None — this is a static SQL/markdown download. The patterns work on PostgreSQL 15+ / Supabase.

---

## Step 4 — Create the r/Supabase post (~10 min)

**Subreddit:** r/Supabase

**Title:**
`8 Supabase RLS patterns where AI consistently generates wrong code (+ how to fix them)`

**Post body:**

---

I've been building on Supabase for the past year across several client projects. The hardest problems have always been RLS — specifically the cases where the generated policy looks correct, runs without errors, but silently does the wrong thing.

I ran the same prompts through Claude, ChatGPT, and Copilot across dozens of sessions. Here are the 8 patterns where they consistently get it wrong:

**1. Anon-role omission** — Generating `CREATE POLICY ... TO authenticated` but forgetting `TO anon` on the SELECT for a public read. Result: anon users get empty results with no error.

**2. SECURITY DEFINER bypass** — Writing helper functions with `SECURITY DEFINER` without realising this runs as superuser and bypasses RLS entirely for every table the function touches.

**3. Permissive policy broadening** — Adding a second policy thinking it will narrow access (like adding a role check). It doesn't — permissive policies combine with OR, so the second policy *widens* access.

**4. NULL equality for shared resources** — Using `org_id = NULL` or `shared_with = NULL` in a USING clause. `= NULL` is always NULL in PostgreSQL. It never matches. Nothing is ever shared.

**5. INSERT without WITH CHECK** — Writing a SELECT policy (which uses `USING`) and assuming it covers INSERTs. It doesn't. INSERT needs `WITH CHECK`. Without it, any user can insert a row claiming any `user_id` or `org_id`.

**6. UPDATE without WITH CHECK** — Same thing for UPDATE. Without `WITH CHECK`, a user can transfer row ownership to themselves by updating `user_id`.

**7. EXISTS without composite index** — Writing `EXISTS (select 1 from memberships where user_id = auth.uid() and org_id = ...)` with correct logic but no index. Works fine in dev. At scale in production: timeout.

**8. JWT claim extraction** — Using `(auth.jwt()->>'custom_claim')::text` which throws when the claim is absent or the value isn't the expected type. The safe pattern uses `coalesce`.

---

I put together a pack of 18 patterns total (these 8 plus multi-tenant CRUD, JWT roles, and Storage bucket policies) with a drop-in `.sql` for each and a companion `.md` explaining the failure mode and testing approach.

It's on Gumroad: [INSERT GUMROAD URL]

The README is free — it lists all 18 patterns with their failure modes. The zip is $24 if you want the actual SQL files.

Happy to answer questions about any of these in the comments — the SECURITY DEFINER one trips up even experienced Postgres devs.

---

**Timing:** Post Tuesday-Thursday, 8-11am AEST (r/Supabase peak is US morning, which is AEST afternoon actually — post Thu-Fri 8-11pm AEST to hit US Tuesday-Wednesday morning).

---

## Step 5 — After posting

- [ ] Update ideas-ledger.md Log: with the Gumroad URL and post date
- [ ] Tell the venture twin the product is live (so it can start monitoring)
- [ ] Check r/Supabase comments within 24h to answer any questions

---

**That's it.** The product is done. This checklist is the only thing standing between the build and income.
