Skip to content
Documentation
Build your product

Architecture

How requests, permissions and services fit together, with the commands you'll use while building features.

View Markdown

These notes help you and your agent extend the starter without breaking workspace permissions or exposing secrets. Read them when you start building features; you don't need them for Setup or Kickstart. For what each tool does, start with Tech stack.

One application, hosted services

Your app is one Next.js App Router application. Pages, server actions, HTTP routes and reusable UI live in the same repository. Hosted Supabase owns Postgres, Auth, Storage and Edge Functions. Stripe owns checkout and billing objects, Resend sends email, and AI requests go through OpenRouter from the server.

Local development runs Next.js on your computer against those hosted services, with no local database or Docker container. The Ship & Scale manual you're reading is separate from the product documentation included in your app.

Next.js, React and TypeScript

Next.js supplies the routing and server runtime. React components describe the interface, while TypeScript catches mismatched data shapes before a production build. App Router folders determine routes: app/dashboard owns protected product pages, and app/api owns HTTP endpoints.

Server Components can load authorized data before rendering. Add a Client Component for interaction such as an editable form or a menu. A server action handles a submitted mutation, validates its input and checks access. You can follow a complete example in Build your first workspace feature.

Server and browser boundaries

Client Components use 'use client' when they need state, events or browser APIs. Server actions use 'use server' and still need authentication, validation and authorization: a form is an HTTP entry point, even if the UI hides it.

Keep modules importing server-only out of client imports. Only intentionally public values use NEXT_PUBLIC_. Pass the minimum presentation data into client props; never pass a Supabase admin client, API secret or provider response containing private account details.

Tailwind CSS and the component library

Tailwind provides utility classes; shadcn/ui places editable component source in components/ui/. The installed preset uses Base UI's interaction primitives with the Vega style, neutral colors, Inter typography and Lucide icons. All 61 component files are in your repository, so you can inspect and adapt their actual props.

Shared design tokens in app/globals.css control colors, borders and spacing. Reusing those tokens and the existing buttons, dialogs and fields keeps a new feature consistent with the rest of the product. See Branding and UI customization before replacing the component foundation.

Hosted Supabase

The browser and server use the Supabase client libraries with the current user's session. Row-level security, or RLS, checks which rows that user can access; workspace membership and roles determine the permitted reads and writes.

Schema and permission changes live in versioned SQL files under supabase/migrations/. Kickstart applies them through the Supabase Management API and records a checksum for each file. The default workflow has no ORM schema generation, local database container or direct database password. Database and migrations explains how to extend it without bypassing that record.

Auth handles sessions, recovery and provider sign-in. Storage holds private avatars; workspace logos use access-controlled database rows. Edge Functions run the hosted email and billing work. Everything stays in your own Supabase project.

Stripe and Resend

Stripe owns checkout sessions, subscriptions, invoices and portal configuration. The app keeps a protected copy of each subscription's state, updated from verified webhook events. Billing belongs to a workspace, and the server decides whether that workspace has paid access. Subscriptions and webhooks covers that lifecycle.

Resend delivers transactional email. The starter publishes five source HTML templates and connects a signed Supabase email hook for Auth messages. Workspace invitations and other application emails use their own narrowly scoped server code. Editing a template file doesn't change sent emails until you publish it through Kickstart; see email templates.

GitHub and Vercel

Git tracks your changes, GitHub stores the private repository, and Vercel builds and runs the deployed app. Deployment Kickstart sets up their initial connection and uploads the runtime settings your app needs. After that, pushing to main deploys new versions.

Your setup folder keeps .kickstart and .secrets records that Kickstart uses to recognize the resources it created. They stay out of the repository. See Deployment Kickstart for the first deployment and Updates and recovery for later changes.

Fumadocs, validation and AI

Fumadocs turns MDX into documentation pages with navigation, search, headings and Markdown actions. The product docs are files in your repository; the blog reads published posts from Supabase. Keep those two authoring paths separate when adding public content.

Zod validates structured input at application boundaries. Next.js route handlers serve the REST API, and the MCP SDK exposes the same operations as assistant tools. Both authenticate the caller and enforce explicit workspace permissions. Adding a field to a schema doesn't grant access to another workspace.

The AI example uses the Vercel AI SDK with the OpenRouter provider for streamed responses. The provider key stays on the server, alongside credit and request checks. The AI guide covers configuration, enabling chat and removing it while keeping Auth, workspaces and billing.

Follow a workspace mutation

Workspace invitations show how a change travels through the app:

  1. A form in the workspace UI sends the explicit workspace org_id and the selected recipient and role.
  2. inviteMember() in app/actions/workspaces.ts gets the verified user through requireUser() and validates the IDs and role.
  3. The signed-in user's Supabase client calls issue_workspace_invitation.
  4. The database checks current membership, role, rate limits and invitation state. Hidden buttons are not the permission boundary.
  5. The server sends the saved invitation through lib/email/invitation.mjs and records the outcome of that send attempt.
  6. revalidatePath('/dashboard/workspace') refreshes the page. Resend accepting a message doesn't confirm it reached the inbox.

lib/auth.ts centralizes requireUser() and getWorkspace(target?). The latter checks an explicit target against the user's current memberships. Never use a browser-supplied workspace ID in an elevated query without an equivalent authorization check.

Daily commands

CommandUseEffect on your services
npm ciInstall pinned dependencies and generate the docs collectionDownloads packages only
npm run devRun the app locallyUsing the app reads and writes your hosted services
npm run typecheckTypeScript validationNone
npm run lintStatic code checksNone
npm run buildProduction compilationNone; a passing build doesn't test provider flows
npm testSetup and failure checksUses fake providers; no live calls
npm run test:uiBrowser tests against a running serverReview what each suite does first
npm run test:hostedSQL isolation checksRuns rolled-back test transactions on a chosen Supabase project

Restart the development server after changing .env. If you change APP_URL, change it consistently: localhost and 127.0.0.1 count as different addresses for cookies and sign-in links.

Development previews

/preview renders development-only example states, and /setup helps with local configuration. They don't bypass /dashboard sign-in or show real provider behavior. For example, a preview of an active billing card has disabled payment controls. Use previews for layout work, and test real behavior with a signed-in test account.

Work with a coding assistant

Give the assistant a concrete outcome, the relevant files and the permission rule, for example: "Add a note visible only to members of its workspace; owners and admins can create one; test that other workspaces can't read or write it." Ask it to inspect the current source and installed framework docs before choosing helpers.

Keep credentials in .env rather than pasting them into the conversation. Your repository's AGENTS.md gives the assistant project rules; review it and add your own. Don't ask an assistant to reset a shared database or rewrite applied migrations to make a test pass.

Dependencies, formatting and verification

package.json and package-lock.json belong together. Review upgrades deliberately, update the lockfile through npm and rerun the affected checks. npm run format formats TypeScript files only; it doesn't touch SQL, JSON or MDX. The starter doesn't include a database-type generator or CI pipeline, so add your own if you need them.

After a change, test the real path, including wrong roles, stale forms, failure states and recovery. The first workspace feature is a worked example.

Template 0.0.1 · September 2026 source snapshot