OneShot

← All packs

Multi-Tenant Auth & RBAC Core

verified

Sign-in, organisations, invites and roles — with one customer's data provably unable to reach another's.

You're building software sold to companies, where each customer has their own team.

How you actually use this

You don’t install a library or wire up an SDK. Your own coding agent builds the code in your project, and you keep it — no runtime dependency on us.

  1. Step 1

    Download and unzip

    You get a folder: the docs that tell an agent what to build, a starting skeleton, and the test suite that decides when it's done.

  2. Step 2

    Open it in Claude Code or Cursor

    Point your coding agent at the folder. Nothing to install, no account with us, no API key.

  3. Step 3

    Paste one prompt

    The pack contains the exact prompt. Paste it as your first message and leave it alone — it works through the build itself, choosing a cheaper or stronger model per task.

  4. Step 4

    Run ./verify.sh

    One command. It prints a pass or fail for every check. Green means the build is done — the same script we ran to produce the receipt on this page.

Typical build: about 12 minutes of your agent working, mostly unattended. Then you integrate the working module into your app the way you would any code you’d written yourself.

Technically: Orgs, invites, roles, sessions, and Postgres-RLS tenant isolation — the auth spine of a B2B SaaS, with cross-tenant isolation proven by the verify suite.

Execution receipt2 of 3 runs passed
Model
claude-sonnet-5
Run at
2026-08-03T01:56:26+00:00
Pack version
a6e13fed82dd
Tokens in / out
6,451,348 / 68,746 · per-run mean · billed input includes 6,156,458 cache reads, priced far below fresh input
Wall time
11.7 min · per-run mean
Run cost
$4.56 · what this verification run cost us at API pricing, per build pass · your plan and model prices differ
Deploy
https://app-production-456e.up.railway.app

Acceptance checks

  • prerequisitesPASS
  • postgres-upPASS
  • postgres-readyPASS
  • deps-installPASS
  • migratePASS
  • migrate-no-password-leakPASS
  • migrate-idempotentPASS
  • buildPASS
  • server-startPASS
  • auth-signup2 of 3 runs
  • auth-signup-duplicate2 of 3 runs
  • auth-login2 of 3 runs
  • auth-me2 of 3 runs
  • auth-logout-invalidates-server-side2 of 3 runs
  • auth-password-change-revokes-others2 of 3 runs
  • org-create-and-me2 of 3 runs
  • invites-accept-flow2 of 3 runs
  • invites-single-use2 of 3 runs
  • members-visibility2 of 3 runs
  • invites-permissions2 of 3 runs
  • members-role-change-fresh2 of 3 runs
  • members-role-change-owner-only2 of 3 runs
  • members-remove2 of 3 runs
  • members-last-owner-protected2 of 3 runs
  • invites-revoke2 of 3 runs
  • invites-reinvite-supersedes2 of 3 runs
  • org-delete-owner-only2 of 3 runs
  • iso-setup2 of 3 runs
  • iso-http-note-scoping2 of 3 runs
  • iso-http-cross-tenant-reads2 of 3 runs
  • iso-http-cross-tenant-writes2 of 3 runs
  • invites-expired2 of 3 runs
  • iso-http-idor2 of 3 runs
  • iso-http-bad-uuid2 of 3 runs
  • iso-db-context-scoping2 of 3 runs
  • iso-db-cross-tenant-writes-blocked2 of 3 runs
  • iso-db-membership-self-grant-blocked2 of 3 runs
  • iso-db-posture2 of 3 runs
  • iso-db-no-plaintext-tokens2 of 3 runs
  • test-suite2 of 3 runs

Versions lock

nextjs-postgres

next16.2.12
react19.2.8
react-dom19.2.8
pg8.22.0
zod4.4.3
@node-rs/argon22.0.2
typescript5.9.3
@types/node24.13.3
@types/pg8.20.3
@types/react19.2.18
@types/react-dom19.2.4
tailwindcss4.3.3
@tailwindcss/postcss4.3.3
class-variance-authority0.7.1
clsx2.1.1
tailwind-merge3.6.0
@radix-ui/react-slot1.3.3
tw-animate-css1.4.0
postgres18.4
node24.18.1

Scaffold integrity

2 scaffold files modified or deleted by the buyer-agent during verification
  • src/app/page.tsx
  • tsconfig.json

Every B2B SaaS eventually needs the same spine — orgs, roles, invites, sessions, and a guarantee that tenant A can never see tenant B's data — and it's the code teams reliably get wrong the first time, usually by finding out in a security review or a customer's pentest. This pack builds it: DB-backed sessions, multi-org membership with owner/admin/member roles, a full invite lifecycle, and Postgres row-level security as the actual enforcement wall, not just an app-layer check that hopes nobody forgets a WHERE clause. The receipt proves it: a 39-check, ~105-assertion verify suite — including cross-tenant probes run directly against the database as the app's own restricted role — has to pass before this pack ships.

Example use cases

  • A solo founder turning a single-user side project into a real team plan — adding "invite your team," role-gated admin controls, and per-org data before their first paid pilot.
  • An agency shipping the same B2B admin panel to multiple clients, each client isolated as its own org on shared infrastructure, without operating a separate database per customer.
  • A dev-tools startup adding organization support ahead of a security review, where "prove tenant data can't leak" is a question an enterprise buyer's security team will actually ask.
  • A vertical SaaS team (scheduling, ticketing, ops tooling) forking the pack's notes exemplar resource into their real per-tenant table — the org-scoped CRUD, permission checks, and RLS policies are already worked out; they copy the pattern instead of re-deriving it.

Why this pack

Everyone building multi-tenant SaaS chooses between four paths. Here's the honest comparison:

Bare agent, no packAuth SaaS (Clerk/Auth0-class)Hand-rolling it yourselfThis pack
What it's forSame goal, no pre-made decisionsAuthentication & session UX — genuinely excellent at thisThe same architecture, built from scratchOrg/role/tenancy layer, enforced in Postgres
CostToken spend, repeated every time a leak is found in reviewRecurring per-MAU fee, indefinitelyEngineer time — days to weeks to get right$149 once + normal token spend to run the build
TimeLooks fast; a few hours to something that runsFast for login/session screens specificallySlow — this is deliberately fiddly codeOne build pass, 3–4 sessions on a $20/mo-equivalent plan
Correctness riskHigh — agents reliably reach for exactly the bugs belowLow for auth itself, but doesn't touch data-layer tenant isolation at allHigh until proven otherwise — proving it is the hard partThe isolation suite ships 10 dedicated isolation checks inside the 39-check acceptance suite (~47 assertions in scaffold/tests/verify/isolation.test.mjs), probing at both HTTP (including IDOR) and direct SQL as the RLS-bound role; the entire 39-check suite runs against a real postgres:18.4 container
Lock-inNone, but the pattern is undocumented and untestedYour identity data lives in a third party's systemNone, but the risk is entirely yoursNone — your code, your Postgres, no runtime dependency on this pack

On the bare-agent column, specifically — these are the classes of bug this pack pre-solves, not hypothetical:

  • Connecting the app as the Postgres owner role on a managed host. Neon and Railway both hand you an owner connection string by default, and row-level security does not bind the table owner — so RLS is silently off, and everything still looks like it works in testing.
  • USING-only write policies. A policy that correctly blocks cross-tenant reads but has no WITH CHECK still lets a request insert rows into another tenant's org.
  • Pooler-unsafe session context. Setting tenant identity with a session-scoped SET instead of a transaction-local set_config(..., true) leaks one request's org context into the next request that reuses the same pooled connection — invisible until concurrent traffic on a transaction-mode pooler exposes it.

On the auth-SaaS column — Clerk, Auth0, and similar are genuinely good at what they do: login screens, session UX, SSO, MFA. That's a different problem from this pack. An auth SaaS tells you who someone is; it says nothing about whether your database will hand tenant A's rows to tenant B once they're logged in. This pack is the tenancy-enforcement layer and pairs fine with an auth SaaS in front of it if you want one — it isn't a replacement for one, and this pack ships its own DB-backed sessions instead because owning exact invalidation semantics (revoke-on-password-change, no-stale-role-claims) is the actual product here.

On hand-rolling — what you're buying isn't the idea, it's the ~300-line frozen migration (schema, RLS policies, SECURITY DEFINER helpers, grants) and the 39-check verify suite that had to pass against it. That's the part that takes an experienced engineer real time to get exactly right and is easy to get subtly wrong in ways that don't show up until a specific bad request arrives.

We won't claim this pack makes your app unhackable — no pack can promise that. What the receipt backs is narrower and true: the specific cross-tenant leak classes above are closed by construction and asserted by an automated suite that connects to the database directly, not just through the app.

Scale envelope

Derived from the shipped architecture, not aspiration — and labeled as an engineering estimate, since it isn't load-tested at this pack's price point:

  • Per-query cost of enforcement: every tenant-scoped policy resolves through a SECURITY DEFINER helper (is_org_member) doing a single primary-key-indexed lookup on org_members(org_id, user_id) — not a join, not a scan. org_id is denormalized onto every tenant table specifically so policies never need one. In practical terms, RLS enforcement adds the cost of one indexed lookup per query, not a cost that grows with your data.
  • Why the pooling design matters at scale: identity is set with set_config('app.user_id', ..., true) inside an explicit transaction — is_local=true means it dies at COMMIT/ROLLBACK. That's what makes this safe under transaction-mode connection pooling (PgBouncer, Neon's pooler), which is the standard way to serve far more concurrent app requests than Postgres's raw connection limit allows. A session-scoped SET — the naive version — would leak context between requests sharing a pooled connection and simply doesn't work under transaction pooling. This design does.
  • Estimate: a single small managed Postgres instance (roughly the smallest paid tier on Railway or Neon — shared vCPU, a few hundred MB–1GB RAM, fronted by a transaction-mode pooler) should comfortably serve low thousands of orgs and tens of thousands of users at this schema's normal CRUD load. This is an estimate based on the indexing and policy shape, not a benchmark result.
  • Where the first ceiling actually is: not RLS policy cost — it's the same two things that limit any single-instance Postgres app: raw connection count (before you're pooling) and general write throughput/vacuum overhead as org_members and your tenant tables grow. Nothing about the RLS/GUC design imposes a lower ceiling than plain Postgres would have anyway.
  • Upgrade path: the standard Postgres playbook, unchanged by this pattern — bump instance size, add a read replica for read-heavy endpoints (member lists, notes lists), and only at genuinely large single-tenant volume consider partitioning a table like notes by org_id. Because org_id is already denormalized onto every tenant table, this schema is also a reasonable starting point for sharded/distributed Postgres (e.g. Citus-style) if you ever outgrow a single instance — that's a distant follow-on, not something this pack implements.

What it costs to skip the pack (modelled, not measured): building this spine yourself the way most people actually work — one agent on a top-tier model, no cheap-tier routing, no parallel subagents, no prompt-caching discipline — models out at roughly $60–245 in API tokens, likely around $120. That's this pack's own measured build volume repriced at Opus-class rates, multiplied by an exploration factor for the ~20 tenancy decisions in ARCHITECTURE.md; the pack-guided build itself measured ≈$6 of tokens per verification run (August 2026 rates), because tasks route down to cheaper tiers. Honest reading: at the likely figure that is less than the $149 price — the token math alone does not pay for this pack, and we won't pretend otherwise. What it pays for is the 39-check isolation suite, and the fact that the leak classes above are the ones you'd otherwise find in someone else's pentest report.

Verified against

  • claude-opus-5
  • claude-sonnet-5
  • claude-haiku-4-5

Supported stacks

  • nextjs-postgres
    next16.2.12
    react19.2.8
    react-dom19.2.8
    pg8.22.0
    zod4.4.3
    @node-rs/argon22.0.2
    typescript5.9.3
    @types/node24.13.3
    @types/pg8.20.3
    @types/react19.2.18
    @types/react-dom19.2.4
    tailwindcss4.3.3
    @tailwindcss/postcss4.3.3
    class-variance-authority0.7.1
    clsx2.1.1
    tailwind-merge3.6.0
    @radix-ui/react-slot1.3.3
    tw-animate-css1.4.0
    postgres18.4
    node24.18.1

Estimated buyer token cost

364,000 tokens for one build pass (estimate, not a guarantee).

FAQ

What if the build fails?

Run bash scaffold/verify.sh — it's deterministic and non-interactive, and prints one CHECK: <name> PASS|FAIL line per check; ACCEPTANCE.md maps every name to what it's asserting. If your agent's fix loop (prompts/02-verify.md) can't reach a clean pass after 5 iterations on a qualifying clean machine (Docker + Node 24.x, pack followed as written), it writes FAILURE-REPORT.md with the failing checks, hypotheses, and log tail instead of stopping silently. Full refund within 14 days of purchase if verify.sh fails on a supported configuration (the pinned stack and a model from the pack's matrix) — reply to your Stripe receipt email with the verify output or FAILURE-REPORT.md attached.

What models does this need?

claude-opus-5 for the security core (T2/T3 — sessions, password auth, the tenancy pipeline — tagged capable on purpose), claude-sonnet-5 for the feature build and verification (T4–T8, standard), and claude-haiku-4-5 for environment setup (T1, cheap). Plan on ~364k fresh tokens (input + output) for one full build pass, measured; the receipt separately reports ~6.5M tokens of total billed input (95% cache reads, billed near a tenth of fresh-input price) and a measured $4.56 per verification run — the most reliable of the three numbers. Expect 3–4 sessions on a $20/mo-equivalent plan — driven by T2/T3's Opus-tier weight against Pro's rolling-window and weekly caps, not raw token volume (Anthropic doesn't publish an exact token-per-window figure, so treat the session count as an expectation, not a guarantee). If your plan is tighter, you can downgrade T2/T3 to standard and budget one extra verify/fix loop; the frozen test suite catches what a cheaper model misses.

Can I customize it after the build?

Yes. The notes resource is deliberately a worked exemplar meant to be copied for every real tenant table you add. The permission map is one file (src/lib/auth/permissions.ts), designed to be swapped for a DB-backed model later. Email delivery is one function (notifyInvite() in src/lib/mail.ts) waiting to be pointed at a real provider. SSO is a seam, not a feature — auth_identities is already keyed for adding an OIDC/SAML provider without touching sessions or tenancy.

Do I own the code?

Yes, entirely. No runtime license check, no dependency on OneShot at runtime, no phone-home. It's a plain Next.js + Postgres codebase — deploy it anywhere Node and Postgres run.

Is Postgres RLS enough for compliance (SOC 2, HIPAA, etc.)?

No, and we won't pretend otherwise. Row-level security is a real, verified enforcement layer inside your database — it's not a compliance certification. Frameworks like SOC 2 or HIPAA also require documented controls, access reviews, audit logging (not shipped in v1 — see Non-goals in PRD.md), incident response processes, and usually a third-party audit. This pack gives you a strong, tested technical answer to "can tenant data leak between customers" — it doesn't replace the paperwork and process compliance actually requires.

What if I'm not on Next.js + Postgres?

This pack currently ships one verified stack variant (nextjs-postgres, pinned versions in pack.yaml). If that's not your stack, it isn't verified for you yet.

Does this include SSO or MFA?

No — v1 ships the seams, not the implementation. auth_identities(provider, provider_user_id) exists so an OIDC/SAML provider slots in later without touching sessions or tenant logic; wiring an actual provider, and MFA, are explicit non-goals for this pack, documented in PRD.md.

Why enforce this in the database instead of just checking in app code?

Because a missed check in app code is exactly the bug class this pack exists to close — one forgotten WHERE org_id = ... and tenant A sees tenant B's rows, and nothing in a normal test suite catches it unless someone thinks to test that exact path. RLS makes the database refuse by default even when application code forgets, and the verify suite proves this by connecting directly as the app's own restricted database role and trying to break it — not just testing through the app's own (possibly buggy) checks.

Specific problems this pack pre-solves

Prescribed services

Disclosure: some links on this page are affiliate links. We may earn a commission if you sign up through them, at no extra cost to you. We only link to services the pack actually verified against.