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,/privacyand/llms.txt, all written from onesrc/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-eventand/ask-productskills - 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.
Batteries
6 batteries, each with its own rules
Path-scoped rules, at least one skill and at least five solution docs. That is the bar for getting into the registry at all.
ORM
Drizzle ORM
Typed SQL in TypeScript, no engine binary, and migrations you read as plain SQL.
Next.js with Drizzle ORM
Database
Supabase
Managed Postgres with row level security, realtime and a local stack in Docker.
Next.js with Supabase
Auth
Clerk
Hosted auth with prebuilt sign-in UI in your design's colours and a user sync webhook.
Next.js with Clerk
Email
Resend
Transactional email with React Email templates you review in a pull request.
Next.js with Resend
Analytics
PostHog
Product analytics, session replay, feature flags and experiments behind one key.
Next.js with PostHog
Blog
Sanity blog
A real editorial CMS your writers can use, with the schema still living in your repo.
Next.js with Sanity blog
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
# 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.
---
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.
{
"$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 verifyThe 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.
- Paper
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
- Next.js on Vercel
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
- PostHog
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
- Next.js on Vercel
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
Show all 6Show fewer
- Next.js on Vercel
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
Skills (22)
Slash commands in .claude/skills/, encoding the repeatable jobs for this selection.
- Next.js on Vercel
/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
- Clerk
/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
- Resend
/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
- PostHog
/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
- Supabase
/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
Show all 22Show fewer
- Sanity blog
/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
- Drizzle ORM
/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
- PostHog
/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
- Next.js on Vercel
/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
- Supabase
/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
- Drizzle ORM
/migrate
Generate, review and apply Drizzle migrations safely, including backfills, destructive changes and the deploy step.
.claude/skills/migrate/SKILL.md
- Paper
/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
- Resend
/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
- Sanity blog
/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
- Next.js on Vercel
/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
- Clerk
/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
- Next.js on Vercel
/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
Rules (23)
Rules in .claude/rules/. The glob is what loads them only where they apply, instead of one CLAUDE.md an agent skims.
- Next.js on Vercel
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
- Clerk
authorise-on-the-server
Authorise from the session, never from the client user object
src/app/** · src/components/** · src/lib/auth/**
- Next.js on Vercel
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/**
- Clerk
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
- Next.js on Vercel
code-style
Code style and file conventions
repo-wide
Show all 23Show fewer
- Next.js on Vercel
deployment
Deployment rules
next.config.ts · vercel.json · package.json · src/proxy.ts · src/app/**/route.ts · .env.example
- Drizzle ORM
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/**
- Resend
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/**
- PostHog
event-naming
Events are declared in the catalogue, named object_verb, past tense
src/lib/analytics/** · src/app/** · src/components/**
- Next.js on Vercel
git
Git and change hygiene
repo-wide
- PostHog
identify-timing
Identify before the first event that matters, reset on sign-out
src/lib/analytics/** · src/app/** · src/components/**
- Next.js on Vercel
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/**
- Sanity blog
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
- Next.js on Vercel
security
Security rules
repo-wide
- PostHog
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/**
- Supabase
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/**
- Next.js on Vercel
testing
Testing rules
tests/** · src/**/*.test.ts · src/**/*.test.tsx
- Paper
tokens-only
Paper: tokens only, and both modes every time
src/components/** · src/app/**
- Next.js on Vercel
ui-kit
Build UI from the component kit
src/components/** · src/app/**
- Clerk
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
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.
- Next.js on Vercel
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
Show all 6Show fewer
- Next.js on Vercel
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
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.
Sanity blog (5)
- Draft mode is on, and the page still shows published contentDraft mode only bypasses caches for reads that know about it. One direct client.fetch, or a token-less client, and the editor sees yesterday's copy.docs/solutions/blog-sanity/draft-mode-and-the-app-router-cache.md
- GROQ projections that stop your blog index fetching every article body*[_type == "post"] returns whole documents, drafts of fields included. Project explicitly, dereference in the query, and slice every list.docs/solutions/blog-sanity/groq-projections-that-avoid-over-fetching.md
- Sanity images without the 4MB original and the layout jumpasset->url hands the browser the raw upload. Build a transform URL with a width, and use the LQIP Sanity already generated as the blur placeholder.docs/solutions/blog-sanity/image-urls-and-lqip.md
- Modelling references in Sanity without an N+1 on every pageA reference is a pointer, not an embed. Dereference inside the projection, model the direction that reads well, and never resolve in a loop.docs/solutions/blog-sanity/modelling-references-without-n-plus-one.md
- Webhook revalidation or a revalidate timer: pick oneA 60-second timer rebuilds pages nobody asked for and still makes editors wait. Tag every read, invalidate on publish, and stop guessing.docs/solutions/blog-sanity/webhook-revalidation-vs-time-based.md
Show all 51Show fewer
Clerk (7)
- Clerk impersonation, the act claim, and what to lock while it is onWhen an admin signs in as a user from the Clerk dashboard, the session token carries an act claim. Read it on the server, show a banner, and refuse account changes until it ends.docs/solutions/clerk/clerk-impersonation-and-the-act-claim.md
- Protecting route handlers is not the same as protecting pagesA redirect is the right answer for a page and a terrible answer for fetch. Use 401 and 403 in route handlers, and never let the proxy be the only check.docs/solutions/clerk/protecting-handlers-vs-pages.md
- Clerk roles: publicMetadata or your own table?publicMetadata is free and instant but vendor state you cannot join on. A local roles table joins and audits but must be kept in sync. Pick per what you need to query.docs/solutions/clerk/roles-publicmetadata-vs-local-table.md
- Make Clerk's sign-in look native, in light and dark, with CSS variablesPoint Clerk's appearance variables at your design's CSS custom properties so the prebuilt components follow your theme and your dark mode, with no copied hex values.docs/solutions/clerk/styling-clerk-with-css-variables.md
- Testing a Clerk webhook locally without a tunnel round tripSign the payload yourself and POST it at localhost. You get replays, retry ids and bad-signature cases in one second instead of thirty.docs/solutions/clerk/testing-clerk-webhooks-locally.md
- The proxy matcher that also matches your static assetsA matcher like "/(.*)" runs auth on every CSS file, image and font. The symptoms are an unstyled site, a redirect loop, or a surprising invocation bill.docs/solutions/clerk/the-matcher-that-ate-your-static-assets.md
- Clerk user-sync webhooks: duplicates, retries and out-of-order eventsSvix retries and can deliver twice, and updates can arrive before creates. Make the write idempotent on the user id and reject stale payloads by timestamp.docs/solutions/clerk/webhook-idempotency-and-ordering.md
Drizzle ORM (5)
- Adding a NOT NULL column to a table that already has rowsThe one-line migration fails on any populated database. Split it into add-nullable, backfill in batches, and enforce: three migrations across two deploys.docs/solutions/drizzle/adding-a-column-with-a-backfill.md
- drizzle-kit generate or drizzle-kit push, and when each is safepush diffs your schema straight onto the database with no file to review; generate writes SQL you commit. Use push only on a database you can throw away.docs/solutions/drizzle/generate-vs-push.md
- db.query relations or an explicit join: choosing in Drizzle without an N+1The relational API returns nested objects and one round trip; the core builder returns flat rows and total control. Which to reach for, and the loop that quietly becomes N+1.docs/solutions/drizzle/relations-vs-joins.md
- Transactions in Drizzle on serverless: what works over HTTP and what needs a socketAn interactive db.transaction() needs a real connection held open. On an HTTP driver it silently is not one. Here is what each driver supports and how to write atomic writes without holding a connection.docs/solutions/drizzle/transactions-in-serverless.md
- Typing partial selects and joins in Drizzle without writing the types by hand$inferSelect describes the whole row, not the three columns you selected. Use the query builder's inferred types, Awaited<ReturnType<...>>, and helper types instead of hand-maintained interfaces.docs/solutions/drizzle/typing-partial-selects.md
Next.js on Vercel (15)
- Protecting a signed-in app in the App Router, in three layersA layout that checks the session is not enough, because layouts do not re-render on client navigation. Where the proxy, the layout and the page each check, and why.docs/solutions/nextjs-vercel/auth-checks-in-an-app-shell.md
- The Compound Engineering loop, and where /ce-plan and /ce-work actually fitAgents that start typing immediately produce work nobody can review. Plan in a file, execute against it, then write down what you learned so the next pass is shorter.docs/solutions/nextjs-vercel/compound-engineering-loop.md
- Environment variables on Vercel, without leaking them into the browserNext.js inlines env reads at build time, so one import can ship a server key to every visitor. Here is the boundary that prevents it and the checks that prove it held.docs/solutions/nextjs-vercel/env-vars-on-vercel-without-leaking-them.md
- What each guard hook blocks, and how to extend one without breaking your sessionClaude Code hooks stop the mistakes that cost the most. Here is what each one refuses, how the exit codes work, and the safe way to add a rule of your own.docs/solutions/nextjs-vercel/guard-hooks-and-how-to-extend-them.md
- llms.txt for a SaaS site, generated from the same copy as the pageAn llms.txt file tells AI agents what your product is and where the important pages are. Build it from the landing page's data so it never goes stale, and serve it as a static file.docs/solutions/nextjs-vercel/llms-txt-for-a-saas-site.md
- Selling one-time purchases next to subscriptions without two billing systemsModel a lifetime deal as a one-time price that grants a plan, record it in a purchases table with a monotonic status, and resolve access from subscriptions and purchases in one rule.docs/solutions/nextjs-vercel/one-time-purchases-vs-subscriptions.md
- A pricing page that renders with no database and no payment keysKeep the plan catalogue in code, map provider price ids through env vars, and make /pricing a static page whose buttons are plain links to a checkout route.docs/solutions/nextjs-vercel/pricing-page-without-a-database.md
- A privacy policy that lists the vendors you actually useGDPR and the US state privacy laws expect you to say who processes personal data for you. Let each integration add itself to the list, so the policy changes when the code does.docs/solutions/nextjs-vercel/privacy-policy-that-lists-your-real-vendors.md
- Rate limit checkout in Postgres, before the provider sees itOne small table and one atomic upsert keep a looping user from spending your payment provider's API limit, across every serverless instance, with no Redis.docs/solutions/nextjs-vercel/rate-limiting-checkout-in-postgres.md
- Refunds and disputes should take access away, exactly onceWhich webhook carries a refund or a dispute on each payment provider, how to find the purchase it belongs to, and how to revoke access without a late event giving it back.docs/solutions/nextjs-vercel/refunds-disputes-and-entitlements.md
- Regenerating from agentic.config.json to see what upstream changedThere is no sync service and no template remote. Regenerate a clean copy from your recorded selection, diff it against your repo, and take only the agent layer.docs/solutions/nextjs-vercel/regenerate-from-config-and-diff.md
- Rules, skills and agents, which one you actually needThe same instruction behaves completely differently depending on where you put it. Rules are ambient constraints, skills are invoked procedures, agents are delegated scopes.docs/solutions/nextjs-vercel/rules-skills-agents-when-to-use-each.md
- Sample testimonials that never reach productionA landing page template needs quotes, logos and numbers to look finished, and publishing invented ones is illegal. Mark them as samples, show them in development, and drop them from the production build.docs/solutions/nextjs-vercel/sample-testimonials-without-the-ftc-risk.md
- A collapsible sidebar that remembers its state without a flashSaving the sidebar's open or collapsed state in localStorage makes every page load jump. A cookie the server reads renders the right width in the first HTML.docs/solutions/nextjs-vercel/sidebar-state-without-a-flash.md
- Writing a path-scoped rule that agents actually followRules fail for two reasons: they load when nobody needs them, or they are unfalsifiable. Scope by path, write checkable statements, and give every rule an escape hatch.docs/solutions/nextjs-vercel/writing-path-scoped-rules-agents-follow.md
Paper (4)
- A chart palette that survives dark modeSix brand colours picked on white turn muddy or fluorescent on near-black. Define the series palette as tokens with two values each, assign them in order, and never let colour be the only encoding.docs/solutions/paper/a-chart-palette-that-survives-dark-mode.md
- A dark mode toggle that works on a machine already set to darkprefers-color-scheme and a .dark class fight over specificity and order. One extra :not() makes an explicit choice win in both directions, and one inline script kills the flash.docs/solutions/paper/class-and-system-dark-mode-that-both-work.md
- Pasting a shadcn component into a repo that renamed the tokensshadcn components are written against variable names, not values. Publish those names alongside your own and a paste works unmodified: except for the ones you already claimed.docs/solutions/paper/pasting-shadcn-components-into-your-own-token-names.md
- Tailwind v4: your dark mode does nothingAdding a .dark block that overrides your colour variables changes nothing if the utilities were generated with @theme instead of @theme inline. Here is the difference and the fix.docs/solutions/paper/tailwind-v4-dark-mode-does-nothing.md
PostHog (5)
- Ad blockers eat a third of your analytics: proxy ingestion through your own domainBlockers match on hostnames, not behaviour. A first-party /ingest route handler forwards events to PostHog server-side and recovers most of the missing traffic.docs/solutions/posthog/ad-blocker-reverse-proxy.md
- Event names that still make sense in twelve monthsWhy analytics projects rot into five spellings of "signup", and the object_verb convention plus a typed catalogue that stops it.docs/solutions/posthog/event-naming-that-survives.md
- Feature flags without the flickerClient-side flags render the control experience first and swap it a beat later. Evaluate on the server, pass the decision down, and keep a bootstrap for the client hooks.docs/solutions/posthog/feature-flags-without-flicker.md
- The identify race that empties your signup funnelEvents fired before identify() stay on the anonymous profile forever. Here is why the merge is not retroactive, and the ordering that fixes it.docs/solutions/posthog/identify-race-conditions.md
- Server events versus client events, and when each one liesA browser event is a claim, a server event is a record. Which side to fire from, why serverless drops events without after(), and how to keep the two from double-counting.docs/solutions/posthog/server-vs-client-events.md
Resend (5)
- Sending a lot of email without hitting the rate limit or the spam folderResend allows a couple of requests a second by default. Use the batch endpoint, add backoff for 429s, keep one idempotency key per recipient, and never batch a magic link.docs/solutions/resend/batching-and-rate-limits.md
- Bounces and spam complaints: listen, or lose the inbox for everyoneA hard bounce means the mailbox is gone. Keep sending and mailbox providers downgrade every message from your domain. Wire the webhook, suppress permanently, and never suppress on a soft bounce.docs/solutions/resend/bounces-complaints-and-webhooks.md
- SPF, DKIM and DMARC: what each record does and why your mail needs all threeThree DNS records decide whether a mailbox provider treats your email as authentic. Here is what each one proves, how to set them up on a subdomain, and how to read a failure.docs/solutions/resend/domain-verification-spf-dkim-dmarc.md
- Magic links that actually arrive, and survive the scanner that clicks them firstA sign-in link is the highest-stakes email you send. It has to land in seconds, work once, and survive corporate mail scanners that follow every URL before the human does.docs/solutions/resend/magic-link-deliverability.md
- Previewing React Email templates locally, and what the preview cannot tell youReact Email's preview server renders your templates with realistic props and hot reload. Here is how to set it up, what to check, and the four failure modes only a real client will show you.docs/solutions/resend/previewing-templates-locally.md
Supabase (5)
- Server code should stop using the anon key, and must not reach for the service role insteadThe anon key is a public identifier, not a credential. Server work needs either the caller's JWT or a deliberate, audited service-role call. Here is how to tell which.docs/solutions/supabase/beyond-the-anon-key-on-the-server.md
- Supabase gives you three connection strings: pick the right one or production falls overDirect on 5432, session pooler on 5432, transaction pooler on 6543. Which one serverless needs, why prepared statements break, and what to run migrations on.docs/solutions/supabase/direct-vs-pooler-connection-strings.md
- Stopping Supabase generated types from drifting out of the schemaGenerated database types are only true at the moment they were generated. Commit them, regenerate them in the same commit as the migration, and check them in verify.docs/solutions/supabase/generated-types-drift.md
- Local Supabase or a hosted branch - pick per environment, not per teamThe CLI stack and Supabase branching solve different problems. Use local for the inner loop, a branch for preview deploys, and never share one dev project.docs/solutions/supabase/local-dev-vs-supabase-branches.md
- Row level security when an ORM is doing the queryingYour ORM connects as the postgres superuser, so RLS never runs. Here is how to keep policies meaningful without giving up typed queries.docs/solutions/supabase/rls-with-an-orm-in-front.md
Use Content Site
The builder opens with these 6 batteries picked. Change anything, then download the zip.