Skip to content

Preset

Content Site

Sanity content, Clerk accounts and Supabase Postgres. No payments until you need them.

What this preset installs

  • 6

    Agents

  • 21

    Skills

  • 22

    Rules

  • 6

    Hooks

  • 47

    Solution docs

  • 2

    MCP servers

Counted from the manifests for this exact selection. The repo wires hooks for Claude Code only. Codex and Cursor get the rules and the skills.

Why this one

Who it is for

For a marketing site, docs site or publication that may grow into a product. Sanity holds the content, with the Studio at /studio, draft previews and webhook revalidation. The landing page, /terms, /privacy and /llms.txt all read one file, src/lib/site.ts. Clerk handles accounts, styled to your design in light and dark, and signed-in readers get a dashboard and settings. Supabase Postgres is ready for app data. Resend sends mail and PostHog tracks what readers do.

No payments battery, on purpose. When you are ready, regenerate with one selected and diff the result.

Path-scoped rules cover the Sanity layer, components (design tokens only) and analytics. Agents, skills and guard hooks come installed. docs/onboard.md walks through the Sanity, Clerk and Supabase keys in order.

Highlights

  • Sanity Studio at /studio, typed GROQ queries, draft previews and webhook revalidation
  • A landing page, /terms, /privacy and /llms.txt, all written from one src/lib/site.ts
  • Clerk sign-in and sign-up in your design's colours, plus a signed-in dashboard and settings
  • Supabase Postgres with a Clerk webhook that syncs users into your tables
  • PostHog events with naming and identify rules, plus /add-event and /ask-product skills
  • No payments and no admin panel: a smaller repo you can regenerate later

The exact selection

Stack
Next.js on Vercel
Package manager
bun
Design system
Paper
Mode
solo
Agent targets
claude
Admin panel
not included
AI bundle
off

Resolver

What the resolver added for you

Nothing is added silently. The picker prints these same lines as you toggle options.

  • auto-addedDrizzle ORM added: Supabase needs orm.
  • recommendedSupabase recommends Supabase Auth.
  • recommendedSupabase recommends Supabase Storage.
  • recommendedPostHog recommends errors.

The output

Read every file before you download it

The real generated repo for Content Site: 346 files, content hash 760dba1c131c. The same selection always produces the same bytes.

  • .claude/57 files
    • agents/6 files
      • designer.md
      • documentarian.md
      • pr-reviewer.md
      • product-analyst.md
      • security-auditor.md
      • system-manager.md
    • hooks/7 files
      • README.md
      • auto-lint.ts
      • block-destructive.ts
      • enforce-doc-meta.ts
      • enforce-typecheck.ts
      • env-leak-detector-write.ts
      • env-leak-detector.ts
    • rules/22 files
      • app-shell.md
      • authorise-on-the-server.md
      • clerk-ui-follows-the-design.md
      • code-style.md
      • deployment.md
      • drizzle-migrations.md
      • drizzle-schema.md
      • email-sending-discipline.md
      • event-naming.md
      • git.md
      • identify-timing.md
      • landing-and-legal.md
      • sanity-runtime.md
      • sanity-schema.md
      • security.md
      • server-truth-and-pii.md
      • supabase-db-access.md
      • supabase-migrations.md
      • testing.md
      • tokens-only.md
      • ui-kit.md
      • verify-the-webhook-first.md
    • skills/21 files
      • add-app-page/1 file
        • SKILL.md
      • add-clerk-role/1 file
        • SKILL.md
      • add-email-template/1 file
        • SKILL.md
      • add-event/1 file
        • SKILL.md
      • add-rls-policy/1 file
        • SKILL.md
      • add-sanity-type/1 file
        • SKILL.md
      • add-table/1 file
        • SKILL.md
      • ask-product/1 file
        • SKILL.md
      • deploy-to-vercel/1 file
        • SKILL.md
      • help/1 file
        • SKILL.md
      • landing-copy/1 file
        • SKILL.md
      • local-supabase/1 file
        • SKILL.md
      • migrate/1 file
        • SKILL.md
      • new-component/1 file
        • SKILL.md
      • preview-and-test-email/1 file
        • SKILL.md
      • preview-draft/1 file
        • SKILL.md
      • qa-feature/1 file
        • SKILL.md
      • security-audit/1 file
        • SKILL.md
      • sync-clerk-user/1 file
        • SKILL.md
      • verify/1 file
        • SKILL.md
      • write-spec/1 file
        • SKILL.md
    • settings.json
  • .vscode/2 files
    • extensions.json
    • settings.json
  • docs/51 files
    • plans/2 files
      • README.md
      • TEMPLATE.md
    • solutions/48 files
      • blog-sanity/5 files
        • draft-mode-and-the-app-router-cache.md
        • groq-projections-that-avoid-over-fetching.md
        • image-urls-and-lqip.md
        • modelling-references-without-n-plus-one.md
        • webhook-revalidation-vs-time-based.md
      • clerk/7 files
        • clerk-impersonation-and-the-act-claim.md
        • protecting-handlers-vs-pages.md
        • roles-publicmetadata-vs-local-table.md
        • styling-clerk-with-css-variables.md
        • testing-clerk-webhooks-locally.md
        • the-matcher-that-ate-your-static-assets.md
        • webhook-idempotency-and-ordering.md
      • drizzle/5 files
        • adding-a-column-with-a-backfill.md
        • generate-vs-push.md
        • relations-vs-joins.md
        • transactions-in-serverless.md
        • typing-partial-selects.md
      • nextjs-vercel/11 files
        • auth-checks-in-an-app-shell.md
        • compound-engineering-loop.md
        • env-vars-on-vercel-without-leaking-them.md
        • guard-hooks-and-how-to-extend-them.md
        • llms-txt-for-a-saas-site.md
        • privacy-policy-that-lists-your-real-vendors.md
        • regenerate-from-config-and-diff.md
        • rules-skills-agents-when-to-use-each.md
        • sample-testimonials-without-the-ftc-risk.md
        • sidebar-state-without-a-flash.md
        • writing-path-scoped-rules-agents-follow.md
      • paper/4 files
        • a-chart-palette-that-survives-dark-mode.md
        • class-and-system-dark-mode-that-both-work.md
        • pasting-shadcn-components-into-your-own-token-names.md
        • tailwind-v4-dark-mode-does-nothing.md
      • posthog/5 files
        • ad-blocker-reverse-proxy.md
        • event-naming-that-survives.md
        • feature-flags-without-flicker.md
        • identify-race-conditions.md
        • server-vs-client-events.md
      • resend/5 files
        • batching-and-rate-limits.md
        • bounces-complaints-and-webhooks.md
        • domain-verification-spf-dkim-dmarc.md
        • magic-link-deliverability.md
        • previewing-templates-locally.md
      • supabase/5 files
        • beyond-the-anon-key-on-the-server.md
        • direct-vs-pooler-connection-strings.md
        • generated-types-drift.md
        • local-dev-vs-supabase-branches.md
        • rls-with-an-orm-in-front.md
      • README.md
    • onboard.md
  • sanity/10 files
    • lib/5 files
      • client.ts
      • fetch.ts
      • image.ts
      • queries.ts
      • types.ts
    • schemas/4 files
      • author.ts
      • block-content.ts
      • index.ts
      • post.ts
    • env.ts
  • scripts/5 files
    • email/2 files
      • render-samples.ts
      • send-test.ts
    • clerk-webhook.ts
    • verify-hooks.ts
    • verify.ts
  • src/190 files
    • app/36 files
      • (app)/10 files
        • dashboard/1 file
          • page.tsx
        • settings/5 files
          • profile/1 file
            • page.tsx
          • security/1 file
            • page.tsx
          • layout.tsx
          • loading.tsx
          • page.tsx
        • error.tsx
        • layout.tsx
        • loading.tsx
        • not-found.tsx
      • (auth)/3 files
        • sign-in/1 file
          • [[...sign-in]]/1 file
            • page.tsx
        • sign-up/1 file
          • [[...sign-up]]/1 file
            • page.tsx
        • layout.tsx
      • (legal)/2 files
        • privacy/1 file
          • page.tsx
        • terms/1 file
          • page.tsx
      • (sanity)/2 files
        • blog/2 files
          • [slug]/1 file
            • page.tsx
          • page.tsx
      • api/6 files
        • draft-mode/2 files
          • disable/1 file
            • route.ts
          • enable/1 file
            • route.ts
        • health/1 file
          • route.ts
        • sanity/1 file
          • revalidate/1 file
            • route.ts
        • webhooks/2 files
          • clerk/1 file
            • route.ts
          • resend/1 file
            • route.ts
      • ingest/1 file
        • [...path]/1 file
          • route.ts
      • llms.txt/1 file
        • route.ts
      • studio/1 file
        • [[...tool]]/1 file
          • page.tsx
      • error.tsx
      • fonts.ts
      • global-error.tsx
      • globals.css
      • layout.tsx
      • loading.tsx
      • not-found.tsx
      • page.tsx
      • robots.ts
      • sitemap.ts
    • components/84 files
      • app/10 files
        • account-card.tsx
        • app-header.tsx
        • app-shell.tsx
        • app-sidebar.tsx
        • dashboard-card.tsx
        • empty-state.tsx
        • page-header.tsx
        • settings-nav.tsx
        • user-avatar.tsx
        • user-menu.tsx
      • auth/5 files
        • account-settings.tsx
        • clerk-root.tsx
        • clerk-user-profile.tsx
        • header-auth-actions.tsx
        • sign-out-button.tsx
      • dashboard/5 files
        • card-action.tsx
        • chart-area-interactive.tsx
        • data-table.tsx
        • data.json
        • section-cards.tsx
      • marketing/17 files
        • brand.tsx
        • cta-band.tsx
        • faq.tsx
        • feature-grid.tsx
        • hero.tsx
        • json-ld.tsx
        • legal-document.tsx
        • logo-cloud.tsx
        • product-preview.tsx
        • sample-badge.tsx
        • section-heading.tsx
        • showcase-visuals.tsx
        • showcase.tsx
        • smart-link.tsx
        • social-icons.tsx
        • steps.tsx
        • testimonials.tsx
      • sanity/3 files
        • draft-banner.tsx
        • portable-text.tsx
        • sanity-image.tsx
      • site/6 files
        • auth-frame.tsx
        • chrome.tsx
        • footer.tsx
        • header.tsx
        • mobile-nav.tsx
        • nav-link.tsx
      • theme/2 files
        • theme-provider.tsx
        • theme-toggle.tsx
      • ui/36 files
        • accordion.tsx
        • alert-dialog.tsx
        • alert.tsx
        • avatar.tsx
        • badge.tsx
        • breadcrumb.tsx
        • button.tsx
        • card.tsx
        • chart.tsx
        • checkbox.tsx
        • collapsible.tsx
        • dialog.tsx
        • drawer.tsx
        • dropdown-menu.tsx
        • field.tsx
        • input.tsx
        • kbd.tsx
        • label.tsx
        • pagination.tsx
        • popover.tsx
        • progress.tsx
        • radio-group.tsx
        • select.tsx
        • separator.tsx
        • sheet.tsx
        • sidebar.tsx
        • skeleton.tsx
        • spinner.tsx
        • switch.tsx
        • table.tsx
        • tabs.tsx
        • textarea.tsx
        • toaster.tsx
        • toggle-group.tsx
        • toggle.tsx
        • tooltip.tsx
    • db/17 files
      • README.md
      • audit.ts
      • clerk-schema.ts
      • client.ts
      • connection-url.ts
      • direct-url.ts
      • driver.ts
      • drizzle.ts
      • email-schema.ts
      • index.ts
      • load-env.ts
      • migrate.ts
      • schema.ts
      • supabase-browser.ts
      • tables.ts
      • url.ts
      • verify.ts
    • hooks/1 file
      • use-mobile.ts
    • lib/51 files
      • analytics/5 files
        • events.ts
        • identify.ts
        • posthog-client.ts
        • posthog-server.ts
        • provider.tsx
      • auth/13 files
        • appearance.ts
        • clerk-env.d.ts
        • origin.ts
        • proxy.ts
        • publishable-key.ts
        • redirect.test.ts
        • redirect.ts
        • roles.ts
        • session.ts
        • user-store.ts
        • user-sync.test.ts
        • user-sync.ts
        • webhook-idempotency.ts
      • email/15 files
        • templates/6 files
          • magic-link.tsx
          • receipt.tsx
          • reset-password.tsx
          • theme.ts
          • verify-email.tsx
          • welcome.tsx
        • address.ts
        • index.ts
        • observability.ts
        • outbox.ts
        • resend.ts
        • retry.ts
        • store.ts
        • suppression.ts
        • tags.ts
      • app-shell.test.ts
      • app-shell.ts
      • client-ip.test.ts
      • client-ip.ts
      • cn.test.ts
      • cn.ts
      • cx.ts
      • design.ts
      • env.ts
      • llms.test.ts
      • llms.ts
      • nav.test.ts
      • nav.ts
      • routes.ts
      • site.test.ts
      • site.ts
      • validate.ts
      • verify.ts
    • proxy.ts
  • supabase/2 files
    • seed/1 file
      • 00_conventions.sql
    • config.toml
  • tests/11 files
    • e2e/7 files
      • app-shell.spec.ts
      • auth.spec.ts
      • auth.ts
      • fixtures.ts
      • landing.spec.ts
      • legal.spec.ts
      • users.ts
    • unit/3 files
      • email-outbox.test.ts
      • email.test.ts
      • env.test.ts
    • smoke.spec.ts
  • .env.example
  • .gitignore
  • .mcp.json
  • CLAUDE.md
  • DESIGN.md
  • README.md
  • agentic.config.json
  • biome.jsonc
  • components.json
  • drizzle.config.ts
  • next.config.ts
  • package.json
  • playwright.config.ts
  • postcss.config.mjs
  • sanity.cli.ts
  • sanity.config.ts
  • tsconfig.json
  • vitest.config.ts

The three files that do the work

CLAUDE.mdmarkdown118 lines
# my-app

Generated by [Agentic Boilerplate](https://github.com/agentic-studio/agentic-boilerplate) from [Agentic Studio](https://theagentic.studio). Same `agentic.config.json`, same repo: regenerate and diff any time.

- **Stack:** Next.js on Vercel (`nextjs-vercel`)
- **Batteries:** Drizzle ORM (`drizzle`), Supabase (`supabase`), Clerk (`clerk`), Resend (`resend`), PostHog (`posthog`), Sanity blog (`blog-sanity`)
- **Design:** Paper (`paper`). See [DESIGN.md](DESIGN.md).
- **Package manager:** bun
- **Mode:** solo
- **Agent targets:** claude

## Read this first

On a fresh clone, read [docs/onboard.md](docs/onboard.md) before running or
editing anything. It lists every environment variable, where to get it, and
the order to set the services up.

```sh
bun install
cp .env.example .env.local
bun run verify
bun run dev
```

## How this repo is set up for agents

- `.claude/rules/`: 22 rules. 3 load every session, 19 load when you read a file they cover.
- `.claude/agents/`: 6 subagents, listed below.
- `.claude/skills/`: 21 skills, listed below.
- `.claude/hooks/`: 6 guard hooks, wired in `.claude/settings.json` for Claude Code.
- `.mcp.json`: 2 MCP servers (`posthog`, `supabase`). Setup is in [docs/onboard.md](docs/onboard.md).
- `docs/solutions/`: 47 solved problems. Read the relevant one before re-solving anything.
- `docs/plans/`: one plan per unit of work.

Run `bun run verify:hooks` to prove the guards still block what they claim to block.
Do not edit `.claude/settings.json` by hand: the `system-manager` agent owns it.

## Workflow

The Compound Engineering plugin adds the loop: `/ce-brainstorm`, `/ce-plan`, `/ce-work`, `/ce-code-review`, `/ce-compound`.
`.claude/settings.json` enables it once you trust this folder. If the commands are missing, run `/plugin install compound-engineering@compound-engineering-plugin`.

## Rules

Loaded every session:

- [Code style and file conventions](.claude/rules/code-style.md)
- [Git and change hygiene](.claude/rules/git.md)
- [Security rules](.claude/rules/security.md)

Loaded when you read a file they cover:

| Rule | Applies to |
|---|---|
| [Pages in the signed-in app](.claude/rules/app-shell.md) | `src/app/(app)/**`, `src/components/app/**`, `src/components/ui/sidebar.tsx`, `src/lib/app-shell.ts`, `src/lib/nav.ts` |
| [Authorise from the session, never from the client user object](.claude/rules/authorise-on-the-server.md) | `src/app/**`, `src/components/**`, `src/lib/auth/**` |
| [Clerk's components wear the design's tokens and only render inside the provider](.claude/rules/clerk-ui-follows-the-design.md) | `src/components/auth/**`, `src/lib/auth/appearance.ts`, `src/app/(auth)/**`, `src/components/site/header.tsx` |
| [Deployment rules](.claude/rules/deployment.md) | `next.config.ts`, `vercel.json`, `package.json`, `src/proxy.ts`, `src/app/**/route.ts`, `.env.example` |
| [Every schema change ships with its generated migration](.claude/rules/drizzle-migrations.md) | `src/db/**`, `drizzle/**`, `drizzle.config.ts` |
| [Schema and query conventions for Drizzle](.claude/rules/drizzle-schema.md) | `src/db/**` |
| [Every email goes through sendEmail, from a verified domain, with a reply-to](.claude/rules/email-sending-discipline.md) | `src/lib/email/**`, `src/app/api/webhooks/resend/**`, `src/lib/auth/**`, `src/lib/billing/**` |
| [Events are declared in the catalogue, named object_verb, past tense](.claude/rules/event-naming.md) | `src/lib/analytics/**`, `src/app/**`, `src/components/**` |
| [Identify before the first event that matters, reset on sign-out](.claude/rules/identify-timing.md) | `src/lib/analytics/**`, `src/app/**`, `src/components/**` |
| [Landing page, legal pages and llms.txt](.claude/rules/landing-and-legal.md) | `src/lib/site.ts`, `src/lib/llms.ts`, `src/app/page.tsx`, `src/app/(legal)/**`, `src/app/llms.txt/**`, `src/components/marketing/**`, `src/components/site/**` |
| [Read tokens stay on the server, Portable Text uses design tokens](.claude/rules/sanity-runtime.md) | `src/app/(sanity)/**`, `src/app/studio/**`, `src/app/api/draft-mode/**`, `src/app/api/sanity/**`, `src/components/sanity/**`, `sanity/lib/**` |
| [The schema is code, and every GROQ query lives in one file](.claude/rules/sanity-schema.md) | `sanity/**`, `sanity.config.ts` |
| [Server events for anything a client can lie about, and never PII in properties](.claude/rules/server-truth-and-pii.md) | `src/lib/analytics/**`, `src/app/**`, `src/components/**` |
| [Row level security is on by default and the service role is a last resort](.claude/rules/supabase-db-access.md) | `src/db/**` |
| [Schema changes go through supabase/migrations, never the dashboard](.claude/rules/supabase-migrations.md) | `supabase/**` |
| [Testing rules](.claude/rules/testing.md) | `tests/**`, `src/**/*.test.ts`, `src/**/*.test.tsx` |
| [Paper: tokens only, and both modes every time](.claude/rules/tokens-only.md) | `src/components/**`, `src/app/**` |
| [Build UI from the component kit](.claude/rules/ui-kit.md) | `src/components/**`, `src/app/**` |
| [The Clerk webhook verifies its signature before anything else](.claude/rules/verify-the-webhook-first.md) | `src/app/api/webhooks/clerk/**`, `src/lib/auth/user-sync.ts`, `src/lib/auth/user-store.ts`, `src/lib/auth/webhook-idempotency.ts` |

## Skills

| Skill | Use it for |
|---|---|
| `/add-app-page` | Add a page to the signed-in app (sidebar entry, session check, loading state), or a new tab under /settings. |
| `/add-clerk-role` | Add a role to the Clerk-backed role vocabulary, wire it into the session claim, gate a route with it, and assign it safely. |
| `/add-email-template` | Add a React Email template, preview it, wire it into a send, and check it renders and lands in a real inbox. |
| `/add-event` | Add a product event end to end: catalogue entry, the question it answers, the capture call on the correct side of the network, and a check that it arrives. |
| `/add-rls-policy` | Add or fix row level security policies on a Supabase table, with a migration, a policy test and regenerated types. |
| `/add-sanity-type` | Add or extend a Sanity document type end to end (schema, GROQ projection, result type, renderer and cache tags) so nothing renders blank. |
| `/add-table` | Add a table to the Drizzle schema, generate and apply its migration, and wire the typed queries for it. |
| `/ask-product` | Answer a question about user behaviour from this repo's event catalogue and the PostHog project, with the caveats that make the number usable. |
| `/deploy-to-vercel` | Ship my-app to Vercel: local gates, environment variables per scope, preview verification, promotion and rollback. |
| `/help` | Explain the agentic system in this repo (rules, skills, agents, hooks, solution docs and the CE loop) and where to go for help beyond it. |
| `/landing-copy` | Rewrite the landing page, the metadata and the legal details for the real product from a short brief, by editing src/lib/site.ts only. |
| `/local-supabase` | Boot, reset, inspect and troubleshoot the local Supabase stack, and pull schema down from a hosted project. |
| `/migrate` | Generate, review and apply Drizzle migrations safely, including backfills, destructive changes and the deploy step. |
| `/new-component` | Add a component to the Paper kit. Prefer pasting from shadcn/ui, fix the two bridge classes, keep it token-only and verify it in both light and dark. |
| `/preview-and-test-email` | Diagnose an email problem (not sending, landing in spam, rendering wrong) in the order that finds the cause fastest. |
| `/preview-draft` | Set up or debug Sanity draft previews: the enable route, the secret, the banner, and why an editor is still seeing published content. |
| `/qa-feature` | Exercise a feature end to end (happy path, unhappy paths, auth boundaries, refresh and mobile) before anyone calls it done. |
| `/security-audit` | Run the standing security pass through the security-auditor agent (secrets, auth boundaries, injection, dependencies and deploy config) and turn findings into fixes. |
| `/sync-clerk-user` | Extend, backfill or re-verify the Clerk user mirror: the users table and ClerkSyncStore that this battery already ships. |
| `/verify` | Prove the repo is actually configured: every required env var present, every configured service reachable, and the guard hooks still blocking what they claim to block. |
| `/write-spec` | Turn a loose request into a written spec (problem, scope, behaviour, acceptance criteria) that /ce-plan can consume without guessing. |

## Subagents

| Agent | Use it for |
|---|---|
| `designer` | Owns the Paper design system and its shadcn bridge. The only agent allowed to introduce a new visual pattern or a new token. Refuses to ship a raw colour or a single-mode change. |
| `documentarian` | Keeps README, CLAUDE.md, DESIGN.md, docs/onboard.md and docs/solutions/ true to the code. Writes solution docs from work that just landed. |
| `pr-reviewer` | Reviews a diff against this repo's rules before it becomes a PR. Convention-aware, blocking on correctness and security, advisory on taste. |
| `product-analyst` | Read-only product analyst. Answers questions about user behaviour from the event catalogue and the PostHog project, and says plainly when the instrumentation cannot answer them. |
| `security-auditor` | Audits the repo or a diff for leaked secrets, broken auth boundaries, injection, unsafe dependencies and unsafe deploy configuration. |
| `system-manager` | Maintains the .claude agentic layer itself: rules, skills, agents, hooks, settings. Adds a rule when a correction repeats. |

## Credit

Generated by [Agentic Boilerplate](https://github.com/agentic-studio/agentic-boilerplate) from [Agentic Studio](https://theagentic.studio).

- [AI Mechanic](https://theagentic.studio/ai-mechanic): Fix a vibe-coded repo, then install this system into it.
- [Claude Engineering System](https://theagentic.studio/claude-engineering-system): The same agentic layer, installed into your existing codebase.
- [AI Product Sprint](https://theagentic.studio/ai-product-sprint): We build the MVP on top of a repo like this one.
.claude/rules/authorise-on-the-server.mdmarkdown133 lines
---
paths:
  - src/app/**
  - src/components/**
  - src/lib/auth/**
---

# Authorise from the session, never from the client user object

## The client user object is display data

`useUser()`, `useAuth()`, `useOrganization()` and the `<Show>` component
(`<Show when="signed-in">`, `<Show when={{ role: "admin" }}>`; Clerk Core 3
replaced `<SignedIn>`, `<SignedOut>` and `<Protect>` with it) exist to render the
right UI. They read state the browser
holds, which the browser can edit. They decide what is *shown*. They never
decide what is *allowed*.

```tsx
// no — this is a visual hint being used as a control
const { user } = useUser();
if (user?.publicMetadata.role === "admin") {
  await fetch("/api/refunds", { method: "POST", body });
}
```

The route handler behind that call is what an attacker invokes, with curl,
without ever loading your page. It authorises itself:

```ts
const user = await requireApiRole("admin");   // from @/lib/auth/session
```

Hiding the button remains worth doing. It is the second step, not the only one.

## `unsafeMetadata` is never an input to a decision

Clerk gives you three metadata buckets:

| Bucket | Written by | Trust |
|---|---|---|
| `publicMetadata` | your backend only | authorisation input |
| `privateMetadata` | your backend only, never sent to the browser | authorisation input |
| `unsafeMetadata` | **the signed-in user, from the browser** | never |

`unsafeMetadata` is named that way for a reason: it is a scratchpad for user
preferences. Reading a role, an entitlement, a plan or a feature flag from it is
a self-service privilege escalation.

## Read the role through one helper

Every role check in this repo goes through `src/lib/auth/session.ts`, which
reads the `metadata` session claim first and falls back to the Backend API only
when it is absent. Do not re-implement it:

- No `auth().sessionClaims?.metadata?.role === "admin"` inline in a page.
- No `clerkClient().users.getUser(id)` in a component to look up a role.
- No role strings outside `src/lib/auth/roles.ts`.

One helper means one place to fix when the claim shape changes, and one place a
security review has to read.

There are two ways in, and the difference is a network call:

| Helper | Answers | Cost |
|---|---|---|
| `getSessionClaims()` / `requireApiClaims()` | `id`, `role`, `orgId`, `impersonatedBy` | the token only |
| `getSessionUser()` / `requireUser()` / `requireRole()` | the whole `SessionUser`, including `email` and `name` | one Backend API call, cached per request |

Clerk's token does not carry the email address, so a helper that promises one
has to fetch it. Guards that only decide yes-or-no should take the cheap path;
anything that renders or emails the person takes the other. Both resolve the
role identically, so neither is more or less trustworthy than the other.

## Never take identity from the request

The user id comes from the session, never from the body, the query string or a
header:

```ts
// no
const { userId } = await request.json();
await deleteAccount(userId);

// yes
const { id } = await requireApiUser();
await deleteAccount(id);
```

When an action legitimately targets *another* user (an admin banning someone)
the target id may come from the request, but only after the actor has been
authorised for that action.

## `auth()` needs the proxy

`auth()` throws if `clerkMiddleware` did not run for the request. That is why
`clerkProxy` from `src/lib/auth/proxy.ts` is a step in `src/proxy.ts`, and why
the matcher there must cover every route that reads a session. A route that
"randomly" throws an auth error is almost always a route the matcher does not
reach.

The proxy is not the authorisation boundary either. It redirects signed-out
visitors early; the page, handler or action still checks the role. A page
under `src/app/(app)/` calls `requireUser(path)` itself: a layout does not
re-run on client navigation.

Before the Clerk keys are set, `clerkIsConfigured()` is false, the proxy skips
Clerk, and every helper answers "signed out". Keep that path working: never
call `auth()` or a Clerk hook without going through those helpers or
`clerkProviderMounted()`.

## Impersonated sessions change nothing on the account

An administrator can sign in as someone from the Clerk dashboard. The token's
`act` claim then names the administrator, and `SessionUser.impersonatedBy`
carries its `sub` (null otherwise, and null for an AI agent actor). While it is
set, the settings pages are read-only summaries. Any new action that changes a
password, an email, a sign-in method or a payment method checks
`impersonatedBy` on the server and refuses, and anything it does write names
both ids in the log.

Never render `<UserProfile />`, `<UserButton />` or `<OrganizationProfile />`
for an actor session: Clerk does not document hiding their forms for one. And never tell an
operator Clerk itself makes the session read-only. Clerk documents no such
lock, and the browser can call Clerk's Frontend API directly, so the app's
lock stops mistakes, not an admin who goes around it (see
`docs/solutions/clerk/clerk-impersonation-and-the-act-claim.md`).

## Server-only imports

`CLERK_SECRET_KEY` and everything that reads it stay on the server.
`src/lib/auth/session.ts` starts with `import "server-only"` so an accidental
import from a client component is a build error rather than a leaked key.
.claude/settings.jsonjson65 lines
{
  "$schema": "https://json.schemastore.org/claude-code-settings.json",
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "bun \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/auto-lint.ts"
          },
          {
            "type": "command",
            "command": "bun \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/enforce-doc-meta.ts"
          }
        ]
      },
      {
        "matcher": "Edit|Write|MultiEdit|Bash|Read|Grep",
        "hooks": [
          {
            "type": "command",
            "command": "bun \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/env-leak-detector-write.ts"
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bun \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-destructive.ts"
          },
          {
            "type": "command",
            "command": "bun \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/enforce-typecheck.ts"
          }
        ]
      },
      {
        "matcher": "Bash|Read|Grep",
        "hooks": [
          {
            "type": "command",
            "command": "bun \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/env-leak-detector.ts"
          }
        ]
      }
    ]
  },
  "extraKnownMarketplaces": {
    "compound-engineering-plugin": {
      "source": {
        "source": "github",
        "repo": "EveryInc/compound-engineering-plugin",
        "ref": "compound-engineering-v3.28.2"
      }
    }
  },
  "enabledPlugins": {
    "compound-engineering@compound-engineering-plugin": true
  }
}

After you unzip

cd my-app
bun install
# then follow docs/onboard.md for env vars
bun run verify

The agentic layer

Everything an agent reads on its first run

Compiled from neutral definitions into Claude Code’s format, plus Codex and Cursor when you pick those targets. Each entry names where it came from: a battery, the design or the base stack.

Agents (6)

Subagents in .claude/agents/. Each carries its own system prompt and, where it matters, a tool allowlist.

  • designer

    Owns the Paper design system and its shadcn bridge. The only agent allowed to introduce a new visual pattern or a new token. Refuses to ship a raw colour or a single-mode change.

    tools: Read, Grep, Glob, Edit, Write

    Paper
  • documentarian

    Keeps README, CLAUDE.md, DESIGN.md, docs/onboard.md and docs/solutions/ true to the code. Writes solution docs from work that just landed.

    tools: Read, Grep, Glob, Edit, Write, Bash

    Next.js on Vercel
  • pr-reviewer

    Reviews a diff against this repo's rules before it becomes a PR. Convention-aware, blocking on correctness and security, advisory on taste.

    tools: Read, Grep, Glob, Bash

    Next.js on Vercel
  • product-analyst

    Read-only product analyst. Answers questions about user behaviour from the event catalogue and the PostHog project, and says plainly when the instrumentation cannot answer them.

    tools: Read, Grep, Glob, mcp__posthog

    PostHog
  • security-auditor

    Audits the repo or a diff for leaked secrets, broken auth boundaries, injection, unsafe dependencies and unsafe deploy configuration.

    tools: Read, Grep, Glob, Bash

    Next.js on Vercel
Show all 6
  • system-manager

    Maintains the .claude agentic layer itself: rules, skills, agents, hooks, settings. Adds a rule when a correction repeats.

    tools: Read, Grep, Glob, Edit, Write, Bash

    Next.js on Vercel

Skills (22)

Slash commands in .claude/skills/, encoding the repeatable jobs for this selection.

  • /add-app-page

    Add a page to the signed-in app (sidebar entry, session check, loading state), or a new tab under /settings.

    .claude/skills/add-app-page/SKILL.md

    Next.js on Vercel
  • /add-clerk-role

    Add a role to the Clerk-backed role vocabulary, wire it into the session claim, gate a route with it, and assign it safely.

    .claude/skills/add-clerk-role/SKILL.md

    Clerk
  • /add-email-template

    Add a React Email template, preview it, wire it into a send, and check it renders and lands in a real inbox.

    .claude/skills/add-email-template/SKILL.md

    Resend
  • /add-event

    Add a product event end to end: catalogue entry, the question it answers, the capture call on the correct side of the network, and a check that it arrives.

    .claude/skills/add-event/SKILL.md

    PostHog
  • /add-rls-policy

    Add or fix row level security policies on a Supabase table, with a migration, a policy test and regenerated types.

    .claude/skills/add-rls-policy/SKILL.md

    Supabase
Show all 22
  • /add-sanity-type

    Add or extend a Sanity document type end to end (schema, GROQ projection, result type, renderer and cache tags) so nothing renders blank.

    .claude/skills/add-sanity-type/SKILL.md

    Sanity blog
  • /add-table

    Add a table to the Drizzle schema, generate and apply its migration, and wire the typed queries for it.

    .claude/skills/add-table/SKILL.md

    Drizzle ORM
  • /ask-product

    Answer a question about user behaviour from this repo's event catalogue and the PostHog project, with the caveats that make the number usable.

    .claude/skills/ask-product/SKILL.md

    PostHog
  • /deploy-to-vercel

    Ship my-app to Vercel: local gates, environment variables per scope, preview verification, promotion and rollback.

    .claude/skills/deploy-to-vercel/SKILL.md

    Next.js on Vercel
  • /edit-pricing

    Add, change or remove a plan or a price (monthly, yearly or one-time lifetime) and wire it to the payment provider.

    .claude/skills/edit-pricing/SKILL.md

    Next.js on Vercel
  • /help

    Explain the agentic system in this repo (rules, skills, agents, hooks, solution docs and the CE loop) and where to go for help beyond it.

    .claude/skills/help/SKILL.md

    Next.js on Vercel
  • /landing-copy

    Rewrite the landing page, the metadata and the legal details for the real product from a short brief, by editing src/lib/site.ts only.

    .claude/skills/landing-copy/SKILL.md

    Next.js on Vercel
  • /local-supabase

    Boot, reset, inspect and troubleshoot the local Supabase stack, and pull schema down from a hosted project.

    .claude/skills/local-supabase/SKILL.md

    Supabase
  • /migrate

    Generate, review and apply Drizzle migrations safely, including backfills, destructive changes and the deploy step.

    .claude/skills/migrate/SKILL.md

    Drizzle ORM
  • /new-component

    Add a component to the Paper kit. Prefer pasting from shadcn/ui, fix the two bridge classes, keep it token-only and verify it in both light and dark.

    .claude/skills/new-component/SKILL.md

    Paper
  • /preview-and-test-email

    Diagnose an email problem (not sending, landing in spam, rendering wrong) in the order that finds the cause fastest.

    .claude/skills/preview-and-test-email/SKILL.md

    Resend
  • /preview-draft

    Set up or debug Sanity draft previews: the enable route, the secret, the banner, and why an editor is still seeing published content.

    .claude/skills/preview-draft/SKILL.md

    Sanity blog
  • /qa-feature

    Exercise a feature end to end (happy path, unhappy paths, auth boundaries, refresh and mobile) before anyone calls it done.

    .claude/skills/qa-feature/SKILL.md

    Next.js on Vercel
  • /security-audit

    Run the standing security pass through the security-auditor agent (secrets, auth boundaries, injection, dependencies and deploy config) and turn findings into fixes.

    .claude/skills/security-audit/SKILL.md

    Next.js on Vercel
  • /sync-clerk-user

    Extend, backfill or re-verify the Clerk user mirror: the users table and ClerkSyncStore that this battery already ships.

    .claude/skills/sync-clerk-user/SKILL.md

    Clerk
  • /verify

    Prove the repo is actually configured: every required env var present, every configured service reachable, and the guard hooks still blocking what they claim to block.

    .claude/skills/verify/SKILL.md

    Next.js on Vercel
  • /write-spec

    Turn a loose request into a written spec (problem, scope, behaviour, acceptance criteria) that /ce-plan can consume without guessing.

    .claude/skills/write-spec/SKILL.md

    Next.js on Vercel

Rules (23)

Rules in .claude/rules/. The glob is what loads them only where they apply, instead of one CLAUDE.md an agent skims.

  • app-shell

    Pages in the signed-in app

    src/app/(app)/** · src/components/app/** · src/components/ui/sidebar.tsx · src/lib/app-shell.ts · src/lib/nav.ts

    Next.js on Vercel
  • authorise-on-the-server

    Authorise from the session, never from the client user object

    src/app/** · src/components/** · src/lib/auth/**

    Clerk
  • billing-core

    Billing is one shared layer with one provider adapter

    src/lib/pricing.ts · src/lib/billing/** · src/app/pricing/** · src/app/(app)/billing/** · src/components/billing/**

    Next.js on Vercel
  • clerk-ui-follows-the-design

    Clerk's components wear the design's tokens and only render inside the provider

    src/components/auth/** · src/lib/auth/appearance.ts · src/app/(auth)/** · src/components/site/header.tsx

    Clerk
  • code-style

    Code style and file conventions

    repo-wide

    Next.js on Vercel
Show all 23
  • deployment

    Deployment rules

    next.config.ts · vercel.json · package.json · src/proxy.ts · src/app/**/route.ts · .env.example

    Next.js on Vercel
  • drizzle-migrations

    Every schema change ships with its generated migration

    src/db/** · drizzle/** · drizzle.config.ts

    Drizzle ORM
  • drizzle-schema

    Schema and query conventions for Drizzle

    src/db/**

    Drizzle ORM
  • email-sending-discipline

    Every email goes through sendEmail, from a verified domain, with a reply-to

    src/lib/email/** · src/app/api/webhooks/resend/** · src/lib/auth/** · src/lib/billing/**

    Resend
  • event-naming

    Events are declared in the catalogue, named object_verb, past tense

    src/lib/analytics/** · src/app/** · src/components/**

    PostHog
  • git

    Git and change hygiene

    repo-wide

    Next.js on Vercel
  • identify-timing

    Identify before the first event that matters, reset on sign-out

    src/lib/analytics/** · src/app/** · src/components/**

    PostHog
  • landing-and-legal

    Landing page, legal pages and llms.txt

    src/lib/site.ts · src/lib/llms.ts · src/app/page.tsx · src/app/(legal)/** · src/app/llms.txt/** · src/components/marketing/** · src/components/site/**

    Next.js on Vercel
  • sanity-runtime

    Read tokens stay on the server, Portable Text uses design tokens

    src/app/(sanity)/** · src/app/studio/** · src/app/api/draft-mode/** · src/app/api/sanity/** · src/components/sanity/** · sanity/lib/**

    Sanity blog
  • sanity-schema

    The schema is code, and every GROQ query lives in one file

    sanity/** · sanity.config.ts

    Sanity blog
  • security

    Security rules

    repo-wide

    Next.js on Vercel
  • server-truth-and-pii

    Server events for anything a client can lie about, and never PII in properties

    src/lib/analytics/** · src/app/** · src/components/**

    PostHog
  • supabase-db-access

    Row level security is on by default and the service role is a last resort

    src/db/**

    Supabase
  • supabase-migrations

    Schema changes go through supabase/migrations, never the dashboard

    supabase/**

    Supabase
  • testing

    Testing rules

    tests/** · src/**/*.test.ts · src/**/*.test.tsx

    Next.js on Vercel
  • tokens-only

    Paper: tokens only, and both modes every time

    src/components/** · src/app/**

    Paper
  • ui-kit

    Build UI from the component kit

    src/components/** · src/app/**

    Next.js on Vercel
  • verify-the-webhook-first

    The Clerk webhook verifies its signature before anything else

    src/app/api/webhooks/clerk/** · src/lib/auth/user-sync.ts · src/lib/auth/user-store.ts · src/lib/auth/webhook-idempotency.ts

    Clerk

Hooks (6)

Guard hooks wired into .claude/settings.json. The repo wires hooks for Claude Code only. Run verify:hooks to prove each one still blocks what it claims to.

  • auto-lint

    Runs Biome on the file that was just edited, applies safe fixes, and reports anything it could not fix.

    PostToolUse · Edit|Write|MultiEdit

    Next.js on Vercel
  • block-destructive

    Blocks irreversible shell commands: recursive force deletes, DROP and TRUNCATE sent to a database, force pushes, git reset --hard, git clean, dd, and truncating redirects onto tracked files.

    PreToolUse · Bash

    Next.js on Vercel
  • enforce-doc-meta

    Checks that files written under docs/solutions/ and docs/plans/ carry the frontmatter those directories depend on, and reports exactly what is missing.

    PostToolUse · Edit|Write|MultiEdit

    Next.js on Vercel
  • enforce-typecheck

    Rewrites a bare tsc, however it is launched, into the project's typecheck script before it runs.

    PreToolUse · Bash

    Next.js on Vercel
  • env-leak-detector

    Blocks tool calls that would print, transmit or commit a secret: literal credential shapes, reads of local .env files by any command or by the Read and Grep tools, echo of secret variables, environment dumps, and live values from your .env files.

    PreToolUse · Bash|Read|Grep

    Next.js on Vercel
Show all 6
  • env-leak-detector-write

    The second half of env-leak-detector: redacts live secret values from command, read and search output before the agent sees them, and flags secrets written into files, private env vars read in client components, and secrets passed to log calls.

    PostToolUse · Edit|Write|MultiEdit|Bash|Read|Grep

    Next.js on Vercel

Institutional memory

51 solution docs, seeded on day one

They ship inside the repo under docs/solutions/. They are published here too, so you can read them first.

Show all 51

Clerk (7)

Drizzle ORM (5)

Next.js on Vercel (15)

Paper (4)

PostHog (5)

Resend (5)

Supabase (5)

Use Content Site

The builder opens with these 6 batteries picked. Change anything, then download the zip.