Next.js · Supabase · Vercel starter kit

Multi-tenant from the first commit.

Keystone is the B2B SaaS boilerplate that gets organizations, invites, and row-level security right — enforced by the database itself, so a bug in your app code can't leak one company's data into another's.

One-time payment · full source · no subscription
rls_boundary.sql — live query trace
RLS
Acme Inc org_id: a1
select * from projects
DB
Postgres + RLS
Globex Corp org_id: b2
select * from acme_inc.projects
200 OK · 3 rows
403 · 0 rows
same-org query, allowed cross-org query, denied by policy

What's inside

Everything a team-based B2B product needs before you can build the part that's actually yours — wired together and tested end to end.

01
Email/password + GitHub OAuth
Supabase Auth handles sessions and providers; protected routes via middleware. Password reset included.
02
Organizations & teams model
Users belong to a company, not just an account — the model most starter kits skip entirely.
03
Row Level Security, scoped per org
Enforced at the database layer. Cross-tenant reads fail in Postgres, before the app ever sees them.
04
Invite-a-teammate flow
A Postgres trigger joins the new user to the right org automatically the moment they sign up.
05
Team members list, real email lookups
The service role key stays server-side and is never exposed to the browser.
06
Full org lifecycle
Belong to multiple orgs and switch between them; remove members, cancel invites, leave or delete an org — with owner-only actions gated in the database.
07
Example resource: "projects"
Full CRUD + RLS pattern wired end to end — the template to copy for every table you add next.
08
One-shot SQL schema
A single file provisions tables, policies, functions, and triggers in one run.
09
Setup docs + Vercel deploy config
Clone, paste in your Supabase keys, deploy — nothing assumed.

A bug we caught before it shipped

Every multi-tenant starter kit promises isolation. This is the exact policy that would have broken it in ours — found in a pre-release audit, not by a customer.

✕ EXPLOITABLE

The insert policy on organization_members checked who was joining, but never which organization. Any authenticated user could grant themselves membership — including role: 'owner' — in any organization, just by knowing its id.

✓ FIXED
The policy is scoped to organizations the caller actually owns — the only legitimate client-side path to create a membership row. Invite acceptance happens separately, server-side, via a trigger that bypasses RLS entirely.

Read the full write-up → the exploit, the blast radius, and how to prove your own policies hold.

supabase/schema.sql
 1-- ✕ checks who, not which org — anyone can join anyone's org
 2create policy "Users can insert their own membership"
 3on organization_members for insert
 4with check (user_id = auth.uid());
 5
 6-- ✓ fixed: scoped to orgs the caller actually owns
 7create policy "Owners can add themselves when creating an org"
 8on organization_members for insert
 9with check (
10  user_id = auth.uid()
11  and role = 'owner'
12  and organization_id in (
13    select id from organizations where owner_id = auth.uid()
14  )
15);

Stack

Nothing exotic — three tools most teams already trust, wired together correctly.

Next.js
App Router, server actions, middleware-based route protection.
Supabase
Auth, Postgres, Row Level Security policies, database triggers.
Vercel
Deploy target, environment variables pre-configured.

One payment, full source

Everything you need to ship a multi-tenant SaaS this weekend, yours to build on.

What's included

  • Full Next.js + Supabase source code
  • Organizations/teams model with per-org RLS
  • Invite flow + auto-join trigger
  • Team members list with server-side email lookups
  • Org switching, member removal, leave/delete org
  • Example "projects" CRUD resource
  • One-shot SQL schema file
  • Setup docs + Vercel deploy config
  • Unlimited personal & commercial projects
Being upfront about v1
  • Invites don't send an actual email yet — tell the invited person to sign up with that email address.
  • No billing/subscription integration yet — bring your own (Stripe, etc.) when you need it.
  • No automated test suite yet.
$49
one-time payment
Get Keystone — $49