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 entrypointsfeatures/: product code grouped by domainconfig/product.ts: starter contract, branding defaults, route constants, and module flagsfeatures/starter/: starter selectors plus disabled-state helpersfeatures/workspace-home/: neutral protected workspace home for starter modeshared/: config, env helpers, shared providersplatform/: environment parsing and integration adaptersconvex/: backend functions grouped by domaincontent/docs/: shipped public docs for buildersdocs/: contributor docs and engineering references
Public and protected routes
The runtime splits into three clear surfaces:
- public marketing surface on
/ - public docs surface on
/docswhen 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/executeafter 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-infor the branded sign-in UI/session-tasks/reset-passwordfor the Clerk reset-password task inside the same shell/sso-callbackfor 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:
docscoursecodexobservability
Current behavior:
- if
docsis off, docs links hide and/docsrenders a disabled-state surface - the packet-first flow now uses
/startas the first protected landing surface and nested packet pages under/product,/design,/marketing, and/build-planas the canonical form pages /missionand/lesson/[lessonKey]now act as the optional post-review bonus library after the Execute / Ship unlock, while/artifactsstill forwards into/review- the legacy
starterRepomodule 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.sectionscontrols the fixed public section order:hero,sprintPath,whatsInside,starterSystem,durability,socialProof,faq,pricing, andfooterstarterConfig.homePage.heroPrimaryCtaandstarterConfig.homePage.pricingPrimaryCtadecide whether those calls to action use checkout or a plain link targetfeatures/marketing/lib/home-page-render-model.tsis the adapter that turns those starter defaults plusfeatures/marketing/content/home.tsinto the render contract thatfeatures/marketing/components/home-page.tsxuses
For the file-by-file operating guide, see Configure the Homepage.
Purchase-to-access flow
- A buyer starts from the public landing page.
- The buyer calls
POST /api/stripe/checkoutfrom the public CTA, whether they are already signed in or not. - 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.
- Stripe hosts the payment flow and returns to
/checkout/success. POST {NEXT_PUBLIC_CONVEX_SITE_URL}/stripe/webhookverifies the signature and handles completed checkout events.- 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.
/checkout/successbecomes 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.- Once the signed-in account’s verified primary email matches the Stripe checkout email, the app claims the purchase and grants product access.
/startand 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./missionand/lesson/[lessonKey]now act as the optional post-review bonus library after review unlock, while/artifactsstill forwards into/review.- 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 Snapshotzip - 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, andapps/web/.env.production.example
What the buyer does not get:
- separate Design, Marketing, or Build pack zip downloads
- raw internal docs like
docs/openai/*ordocs/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.