The stack

Batteries included. All of them off.

Every subsystem here is fully wired and switched off until you say otherwise. Nothing half-works, nothing is a stub, and a clone you have just downloaded runs end-to-end without a single third-party account.

01 — The bare clone

What you get before signing up for anything

This is the whole point of flag-gating. The starter has to be fully usable on a laptop with no accounts, or the batteries are decoration.

Auth, roles and RLS multi-tenancy

Notes CRUD as the reference feature

Feedback widget

Onboarding tour

i18n, accessibility checks and the test suite

Supabase runs in Docker via pnpm supabase start, so even the database is local. The first account you create is the one you choose to create.

02 — The inventory

Every subsystem, and what it costs

Grouped the way the manifest groups them. If a row says no account, it means the feature is genuinely self-hosted — not a trial tier.

Core

Always on. No flag, because without it there is no app.

Supabase

no account

Postgres, Auth, Row Level Security, Storage and Realtime. Runs locally in Docker, so development and CI need no hosted project.

core — always on

Security

No flag — present the keys and the buttons start working.

GitHub OAuth login

needs GitHub

Social login alongside email and password. The login page buttons are already wired; you supply the client id and secret.

no flag — set 2 keys

Google OAuth login

needs Google

The same, for Google. Both are enabled in supabase/config.toml rather than in app code.

no flag — set 2 keys

Growth

The subsystems that grow the product. Two of them are self-hosted and cost nothing.

Email notifications

needs Resend

A database trigger fans a new note out to tenant admins as notification rows; a Supabase webhook mirrors each row to email. In-app feed and bell included.

NUXT_PUBLIC_NOTIFICATIONS_ENABLED

Feedback widget

no account

A floating widget for signed-in users that submits to your own feedback table, RLS-scoped like everything else. Users read their own; admins read the tenant’s.

NUXT_PUBLIC_FEEDBACK_ENABLED

Onboarding tour

no account

A first-run product tour that targets nav links by href, so restyling the nav doesn’t break it. Completion is remembered per browser.

NUXT_PUBLIC_TOUR_ENABLED

PostHog product analytics + feature flags

needs PostHog

Pageviews and useFeatureFlag(key, fallback) — a reactive flag that degrades to its fallback when PostHog is off, so gated UI still works on a bare clone.

NUXT_PUBLIC_POSTHOG_ENABLED

Billing

Behind an adapter, so the provider is replaceable.

Billing

needs Polar

Checkout, customer portal and webhooks behind an adapter. A Merchant of Record, which is why it is here instead of Stripe — it onboards sellers in countries Stripe will not.

NUXT_PUBLIC_BILLING_ENABLED

Observability

Also keyed rather than flagged: no DSN, no reporting.

Sentry error tracking

needs Sentry

Error tracking, with hidden source maps generated for upload but never referenced in the bundle.

no flag — set 1 keys

BetterStack log forwarding

needs BetterStack

Server log forwarding from Nitro.

no flag — set 2 keys

03 — One manifest, two scripts

Something writes it, something else checks it

pnpm setup picks the subsystems and writes .env. pnpm doctor verifies what's there. Both read the same manifest, so adding a subsystem to it wires up both — and neither can drift from the other.

terminal
$ pnpm setup

  Which subsystems do you want?
 Feedback widget      (no account needed)
 Onboarding tour      (no account needed)
 Notifications        (Resend)
 Billing              (Polar)

  wrote .env

$ pnpm doctor
 supabase        4 vars set
 feedback        enabled, no keys required
  ! notifications   disabled

Unpicked subsystems are set to false, never deleted, and existing values are never overwritten — so re-running setup after you have edited .env by hand is safe.

doctor fails on a missing required key and warns on a missing optional one, which is how a half-configured subsystem gets caught before it is deployed rather than after.

04 — Where it runs

Vercel is the default, not the requirement

The build output is Nitro, which targets Node, Deno, Bun, Cloudflare, Netlify and a plain server. Nothing in the app reaches for a platform-specific API.

Portable by construction

Swap the Nitro preset and deploy elsewhere. The only Vercel-specific pieces are the analytics and speed-insights modules, and both no-op off Vercel.

Supabase, hosted or not

The same migrations run against a local Docker stack and a hosted project. Self-host the whole thing if you'd rather.

Migrations, not snapshots

Schema changes are ordered SQL files. After changing one, regenerate types or typecheck fails — the schema can't silently drift from the code.

Ready to wire one of these up?

The configuration reference has the exact variables and the webhook steps for each subsystem.

Configuration reference