Skip to Content
How the Starter Works

How the Starter Works

This starter is opinionated in one specific way: it gives you the full public-to-paid-to-protected path instead of a landing page plus disconnected example code.

Locked stack

  • App shell: Next.js
  • Deployment target: Vercel
  • Checkout: Stripe
  • Auth: Clerk
  • App data and access state: Convex

Repo shape

  • app/: route entrypoints
  • features/: product code grouped by domain
  • config/product.ts: starter contract, branding defaults, route constants, and module flags
  • features/starter/: starter selectors plus disabled-state helpers
  • features/workspace-home/: neutral protected workspace home for starter mode
  • shared/: config, env helpers, shared providers
  • platform/: environment parsing and integration adapters
  • convex/: backend functions grouped by domain
  • content/docs/: shipped public docs for builders
  • docs/: contributor docs and engineering references

Public and protected routes

The runtime splits into three clear surfaces:

  • public marketing surface on /
  • public docs surface on /docs when the docs module is enabled
  • protected product surface on /start, the stage-entry routes /product, /design, /marketing, and /build-plan, the canonical nested packet pages under those stage roots, /review, optional /execute after review unlock, and /settings

That split matters because checkout and access recovery live between those worlds.

app/(workspace)/layout.tsx is the single server-side product-access gate for the protected app routes. Middleware only handles sign-in.

The auth bridge between public and protected surfaces uses three route-group screens:

  • /sign-in for the branded sign-in UI
  • /session-tasks/reset-password for the Clerk reset-password task inside the same shell
  • /sso-callback for OAuth completion

/sign-up is not a visible auth product surface in this repo. It redirects to pricing so account creation still flows through checkout.

Starter config and module gating

The starter control plane currently lives in starterConfig inside config/product.ts.

Important module flags:

  • docs
  • course
  • codex
  • observability

Current behavior:

  • if docs is off, docs links hide and /docs renders a disabled-state surface
  • the packet-first flow now uses /start as the first protected landing surface and nested packet pages under /product, /design, /marketing, and /build-plan as the canonical form pages
  • /mission and /lesson/[lessonKey] now act as the optional post-review bonus library after the Execute / Ship unlock, while /artifacts still forwards into /review
  • the legacy starterRepo module is now off by default, so starter-repo cards are hidden from the visible protected app unless someone explicitly re-enables that obsolete path

These module gates sit above the existing auth, checkout, and access model. They do not replace it.

The public homepage has a second layer of starter configuration inside the same file:

  • starterConfig.homePage.sections controls the fixed public section order: hero, sprintPath, whatsInside, starterSystem, durability, socialProof, faq, pricing, and footer
  • starterConfig.homePage.heroPrimaryCta and starterConfig.homePage.pricingPrimaryCta decide whether those calls to action use checkout or a plain link target
  • features/marketing/lib/home-page-render-model.ts is the adapter that turns those starter defaults plus features/marketing/content/home.ts into the render contract that features/marketing/components/home-page.tsx uses

For the file-by-file operating guide, see Configure the Homepage.

Purchase-to-access flow

  1. A buyer starts from the public landing page.
  2. The buyer calls POST /api/stripe/checkout from the public CTA, whether they are already signed in or not.
  3. The app stores the pending Stripe Checkout Session ID in a first-party cookie so the handoff can recover if the Stripe return tab is lost.
  4. Stripe hosts the payment flow and returns to /checkout/success.
  5. POST {NEXT_PUBLIC_CONVEX_SITE_URL}/stripe/webhook verifies the signature and handles completed checkout events.
  6. If the checkout already belonged to a signed-in Clerk account, Convex can grant access directly. Otherwise the webhook stores a durable pending paid checkout keyed by the Stripe session.
  7. /checkout/success becomes the canonical post-payment handoff. If the buyer is not signed in yet, they can sign in or create the account tied to the checkout email there.
  8. Once the signed-in account’s verified primary email matches the Stripe checkout email, the app claims the purchase and grants product access.
  9. /start and the other packet-first workspace routes use the same access state and can perform a guarded fallback claim when the browser still holds the pending checkout session cookie.
  10. /mission and /lesson/[lessonKey] now act as the optional post-review bonus library after review unlock, while /artifacts still forwards into /review.
  11. Returning users with granted access who hit / are resumed into the first incomplete packet-first stage unless they explicitly force marketing mode.

Protected app routes also start from server-fetched mission or access data before the client-side Convex subscriptions take over. That avoids a hydration-time auth race where the browser can briefly act signed out even though the server already proved access.

Review output contract

/review is now the single download moment for the packet-first flow.

What the buyer gets:

  • one Starter Snapshot zip
  • one repo folder when the zip is unpacked
  • generated Design, Marketing, Build, facts, and prompt materials under repo-root docs/
  • generalized starter-safe operator surfaces such as .agents/, qa/, workflow examples, and apps/web/.env.production.example

What the buyer does not get:

  • separate Design, Marketing, or Build pack zip downloads
  • raw internal docs like docs/openai/* or docs/ship-by-sunday/*
  • raw env files, local output, or machine-specific junk

Why the Clerk convex template matters

The server-side Clerk-to-Convex read path is explicitly wired to the Clerk JWT template named convex.

If that template is missing or the issuer is wrong, the app can look mostly fine while protected server reads still fail.

What success looks like

You understand the starter when you can answer these questions without guessing:

  • Which route creates the Stripe Checkout Session?
  • Which route handles the Stripe webhook?
  • Which system is the source of truth for product-access grants?
  • Which protected routes should a newly granted user reach first?

If any of those are fuzzy, do not rewrite the purchase-to-access path yet. Fix your understanding first.

Last updated on