Free · MIT · Nuxt 4 · Supabase · Postgres RLS

Ship multi-tenant SaaS, not scaffolding.

A free, MIT-licensed Nuxt 4 + Supabase starter where access control lives in the database, every subsystem is wired but off by default, and one tenant can never read another's rows — proven by a test, not a promise.
gh repo create my-app --template TerrorSquad/gstack --private --clone
cd my-app            # or hit "Use this template" on GitHub

pnpm install
pnpm setup          # pick your integrations; writes .env
pnpm supabase start # local Postgres + Auth (needs Docker)
pnpm db:reset       # migrate + seed a demo tenant
pnpm dev            # http://localhost:3000
01 — The guarantee

The isolation test

Most starters claim multi-tenancy. This one logs in as a second tenant and asserts the first tenant's rows are invisible — through the rendered page, the search box, and the SSR payload. Widen a policy by accident and this goes red.
e2e/tenant-isolation.spec.ts
test('a Globex admin cannot see Acme notes', async ({ page }) => {
  await login(page, ADMIN2.email)

  // The seeded Acme secret title must be absent from Globex's notes list.
  await page.goto('/notes')
  await expect(page.getByText(ACME_SECRET_NOTE_TITLE)).toHaveCount(0)
})

Every table carries a tenant_id, and RLS scopes rows through a current_tenant_id() security-definer helper that reads the caller's own profile. Page-level role gates are UX only — a bug in a component cannot leak another tenant's data, because the component never had the rows.

How multi-tenancy works
02 — What it looks like

The screens you don't have to build

Dashboard, notes CRUD, tenant admin with invites and impersonation, billing, account with avatar upload, pricing, changelog and a feedback widget — all in the template on the first run, in both themes and on mobile.
See the screens
03 — Batteries included

Every subsystem is one env flag

Nothing half-works. Each subsystem is fully wired and switched off until you set its flag, so a bare clone runs end-to-end with zero third-party accounts — Supabase runs locally in Docker.
SubsystemFlagNeeds an account
Supabase — Postgres, Auth, RLS, Storagecore, always onNo (local via Docker)
Feedback widget → your own DBNUXT_PUBLIC_FEEDBACK_ENABLEDNo
Billing — checkout, portal, webhooksNUXT_PUBLIC_BILLING_ENABLEDPolar

Six more — OAuth, notifications, analytics, the onboarding tour, error tracking and log forwarding — are on the stack page.

pnpm setup writes the flags, pnpm doctor verifies them, and both read the same manifest — so a subsystem can't be half-configured without one of them saying so.

Configuration reference
04 — Proof, not adjectives

What CI won't let you break

The interesting part of a starter isn't what it ships on day one, it's what it refuses to let you regress on day ninety.
GateWhat it catches
pnpm lintoxlint + oxfmt, auto-fixing on pre-commit
pnpm lint:i18na key added to en but not sr, or a key nothing uses
pnpm testlogic + the email-shell drift test: auth templates that no longer match their generator
pnpm typechecka query that under-selects, against types generated from the live schema
pnpm test:e2etenant isolation, auth flows, notes CRUD
axe, twice dailycontrast and landmark failures, on every page, in both light and dark

Releases are cut by release-please from conventional commits; the user-facing changelog is curated separately, so shipping a refactor doesn't spam your users.

05 — What else is in the box

The parts you would otherwise build twice

Ten independent Nuxt layers over a shared design system, and the unglamorous work that usually gets deferred until it hurts.
  • Type-safe end to end
    Postgres schema → generated types → composables → UI. No ORM. Change a column and the build tells you every call site that cared.
  • Auth that survives SSR
    Email/password plus GitHub and Google OAuth, password reset, confirmation, and a role-aware global middleware instead of the Supabase module's redirect.
  • One email shell
    Transactional mail and the Supabase auth templates render from the same generated shell, so the branding can't drift. A unit test fails if it does.
  • Bilingual from the start
    English and Serbian, with key parity enforced in CI. Dynamic keys are written so the usage checker can still resolve them.
  • Portable deploy
    Nitro output, so Vercel is the default and not a lock-in. Supabase runs locally in Docker for development and CI alike.
  • Decisions on the record
    The reversible-but-significant choices — Polar over Stripe, no ORM yet, layers over a monorepo — are written down as ADRs with their trade-offs.
06 — In the wild

Someone built a real product on it in 13 days

job-finder is a multi-tenant product that reads company ATS boards, scores every posting against your CV with an LLM, and drafts the application material. Tenancy, auth, billing, email and the admin surface came from GStack on day one, so the 13 days went on the part that was actually the product.

That is the whole claim being made here: not that the starter is clever, but that the work it removes is work you would otherwise do before writing a line of your own.

Free and MIT licensed

Clone it, rename it with one command, and start building your product instead of its scaffolding.