When you're building a multi-tenant app, there's one bug category that's worse than any other: a user seeing someone else's data. Not a crash, not a broken button — a genuine privacy failure.

I recently built a Next.js + Supabase + Stripe starter kit, and before I'd call the database layer "done," I wanted to actually prove the security held, not just assume it did because the code looked right.

The setup

Supabase's Row Level Security (RLS) lets you write policies directly in Postgres that filter rows based on who's asking — the database itself becomes the security boundary, not just your application code. That's powerful, but it also means a single wrong policy (or a missing one) silently exposes everything.

Here's the policy pattern I used for an owner-scoped table: