Preset
Indie SaaS
Sign-in, Stripe billing, an admin panel and a blog, with the agent layer already installed.
What this preset installs
7
Agents
28
Skills
30
Rules
7
Hooks
74
Solution docs
5
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
A SaaS starter that works on day one. People sign in with a password, a magic link, or Google, GitHub and Microsoft once their keys are set. Stripe sells monthly, yearly and lifetime plans from a public /pricing page. The signed-in app has a dashboard, settings and billing, and the admin panel lists real users with bans, impersonation and an audit log. Better Auth, Drizzle on Neon Postgres, Resend, PostHog, Sentry and an MDX blog sit underneath.
Every battery brings its own rules, skills and solution docs. An agent editing src/lib/billing/** already knows to verify webhook signatures and keep price ids out of code. Guard hooks block destructive commands, keep secrets out of logs and commits, and rewrite a bare tsc into the typecheck script. verify:hooks proves each guard still blocks what it claims.
Start with docs/onboard.md: every env var this selection needs, and where to get it. verify names the keys still missing. agentic.config.json records the selection, so the same config regenerates the same repo.
Highlights
- Sign-in with email and password, magic links, and Google, GitHub and Microsoft, each on once its keys are set
- A public
/pricingpage, Stripe Checkout for monthly, yearly and lifetime prices, the customer portal, and a verified webhook that writes to Postgres - A signed-in app: sidebar, account menu, dashboard, profile and security settings, and billing
- An admin panel with real users: search, bans with an expiry, impersonation and an audit log
- Neon Postgres, Drizzle migrations and a
/db-branchskill for a database per branch - MDX blog with RSS, plus PostHog and Sentry with rules that keep PII out
The exact selection
- Stack
- Next.js on Vercel
- Package manager
- bun
- Design system
- Daylight
- Mode
- solo
- Agent targets
- claude
- Admin panel
- included
- AI bundle
- off
Batteries
9 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
Neon
Serverless Postgres you can branch like git, with a driver built for cold starts.
Next.js with Neon
Auth
Better Auth
Own your users table. Passwords, magic links, Google, GitHub and Microsoft, all in your database.
Next.js with Better Auth
Payments
Stripe
Hosted Checkout, a customer portal you never build, and webhooks you test locally.
Next.js with Stripe
Email
Resend
Transactional email with React Email templates you review in a pull request.
Next.js with Resend
Error tracking
Sentry
Stack traces with the release and route attached, scrubbed of user data first.
Next.js with Sentry
Analytics
PostHog
Product analytics, session replay, feature flags and experiments behind one key.
Next.js with PostHog
Admin panel
Admin panel
Users, bans, impersonation and an audit log. Your code, any auth provider.
Next.js with Admin panel
Blog
MDX blog
Posts are files in your repo. Reviewed in a pull request, shipped with the code.
Next.js with MDX blog
The output
Read every file before you download it
The real generated repo for Indie SaaS: 537 files, content hash 0378cb3a9007. The same selection always produces the same bytes.
.claude/74 files
agents/7 files
- db-inspector.md
- designer.md
- documentarian.md
- pr-reviewer.md
- product-analyst.md
- security-auditor.md
- system-manager.md
hooks/8 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
- guard-neon-sql.ts
rules/30 files
- admin-access.md
- admin-mutations.md
- app-shell.md
- auth-server-boundary.md
- billing-core.md
- blog-content.md
- blog-rendering.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
- neon-connections.md
- neon-migrations.md
- roles-are-server-side.md
- security.md
- sentry-capture.md
- sentry-no-pii.md
- server-truth-and-pii.md
- stripe-billing-store.md
- stripe-pricing-source-of-truth.md
- stripe-server-boundary.md
- stripe-webhook-integrity.md
- testing.md
- tokens-only.md
- ui-kit.md
skills/28 files
add-admin-action/1 file
- SKILL.md
add-admin-page/1 file
- SKILL.md
add-app-page/1 file
- SKILL.md
add-email-template/1 file
- SKILL.md
add-event/1 file
- SKILL.md
add-mdx-component/1 file
- SKILL.md
add-oauth-provider/1 file
- SKILL.md
add-plan/1 file
- SKILL.md
add-table/1 file
- SKILL.md
ask-product/1 file
- SKILL.md
db-branch/1 file
- SKILL.md
deploy-to-vercel/1 file
- SKILL.md
edit-pricing/1 file
- SKILL.md
help/1 file
- SKILL.md
landing-copy/1 file
- SKILL.md
migrate/1 file
- SKILL.md
migrate-on-neon/1 file
- SKILL.md
new-component/1 file
- SKILL.md
new-post/1 file
- SKILL.md
preview-and-test-email/1 file
- SKILL.md
protect-route/1 file
- SKILL.md
qa-feature/1 file
- SKILL.md
scrub-pii/1 file
- SKILL.md
security-audit/1 file
- SKILL.md
test-webhook/1 file
- SKILL.md
triage-errors/1 file
- SKILL.md
verify/1 file
- SKILL.md
write-spec/1 file
- SKILL.md
- settings.json
.vscode/2 files
- extensions.json
- settings.json
content/2 files
blog/2 files
- hello-world.mdx
- writing-with-mdx.mdx
docs/78 files
plans/2 files
- README.md
- TEMPLATE.md
solutions/75 files
admin-panel/8 files
- adding-an-audit-log.md
- bans-and-session-revocation.md
- empty-states-that-are-not-sad.md
- first-admin-without-a-backdoor.md
- impersonating-users-safely.md
- paginating-a-users-table.md
- role-checks-that-survive-a-refactor.md
- route-group-vs-path-segment.md
better-auth/10 files
- account-linking-and-email-verification.md
- better-auth-on-the-edge.md
- csrf-and-cookie-flags.md
- guarding-the-auth-api-itself.md
- magic-link-tokens-and-scanners.md
- migrating-an-existing-user-table.md
- oauth-callback-url-mismatches.md
- password-reset-tokens.md
- role-modelling-for-the-admin-panel.md
- session-invalidation-and-logged-out-on-deploy.md
blog-mdx/5 files
- mdx-without-a-runtime.md
- og-images-that-render-your-font.md
- rss-that-validates.md
- sitemap-and-canonical-urls.md
- syntax-highlighting-without-a-huge-bundle.md
daylight/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
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
neon/6 files
- branch-per-preview-deployment.md
- cold-start-latency.md
- connection-exhaustion-in-serverless.md
- local-postgres-without-a-neon-account.md
- migrations-on-vercel.md
- pooled-vs-unpooled-connections.md
nextjs-vercel/15 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
- one-time-purchases-vs-subscriptions.md
- pricing-page-without-a-database.md
- privacy-policy-that-lists-your-real-vendors.md
- rate-limiting-checkout-in-postgres.md
- refunds-disputes-and-entitlements.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
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
sentry/5 files
- grouping-noisy-errors-with-fingerprints.md
- scrubbing-pii-before-it-leaves-the-process.md
- source-maps-on-vercel.md
- telling-a-real-incident-from-a-bot.md
- trace-sample-rates-that-do-not-bankrupt-you.md
stripe/6 files
- free-trials-that-do-not-leak.md
- idempotent-stripe-webhooks.md
- one-time-payments-with-stripe-checkout.md
- proration-when-plans-change.md
- reconciling-a-missed-webhook.md
- stripe-tax-and-when-you-need-it.md
- README.md
- onboard.md
scripts/15 files
admin/2 files
- grant.ts
- load-env.ts
auth/2 files
- make-admin.ts
- seed.ts
billing/3 files
- prune-events.ts
- reconcile.ts
- sync-plans.ts
blog/1 file
- check-posts.ts
email/2 files
- render-samples.ts
- send-test.ts
- db-branch.ts
- neon-local-proxy.ts
- sentry-issues.ts
- verify-hooks.ts
- verify.ts
src/329 files
app/56 files
(admin)/13 files
admin/12 files
audit/2 files
- loading.tsx
- page.tsx
settings/2 files
- loading.tsx
- page.tsx
users/4 files
[id]/2 files
- loading.tsx
- page.tsx
- loading.tsx
- page.tsx
- error.tsx
- loading.tsx
- not-found.tsx
- page.tsx
- layout.tsx
(app)/14 files
billing/4 files
checkout/1 file
- route.ts
- error.tsx
- loading.tsx
- page.tsx
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
(better-auth)/6 files
banned/1 file
- page.tsx
forgot-password/1 file
- page.tsx
reset-password/1 file
- page.tsx
sign-in/1 file
- page.tsx
sign-up/1 file
- page.tsx
- layout.tsx
(legal)/2 files
privacy/1 file
- page.tsx
terms/1 file
- page.tsx
api/4 files
auth/1 file
[...all]/1 file
- route.ts
health/1 file
- route.ts
webhooks/2 files
resend/1 file
- route.ts
stripe/1 file
- route.ts
blog/4 files
[slug]/2 files
- opengraph-image.tsx
- page.tsx
rss.xml/1 file
- route.ts
- page.tsx
ingest/1 file
[...path]/1 file
- route.ts
llms.txt/1 file
- route.ts
pricing/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/138 files
admin/25 files
- admin-header.tsx
- admin-shell.tsx
- admin-shortcut-card.tsx
- admin-sidebar.tsx
- audit-details.tsx
- audit-feed.tsx
- audit-filters.tsx
- audit-table.tsx
- ban-dialog.tsx
- billing-summary.tsx
- confirm-action-dialog.tsx
- grant-admin-form.tsx
- impersonation-banner.tsx
- revoke-session-button.tsx
- sessions-table.tsx
- stat-card.tsx
- stop-impersonating-button.tsx
- use-admin-action.ts
- use-provider-sign-out.ts
- user-actions.tsx
- user-badges.tsx
- user-identity.tsx
- users-filters.tsx
- users-pagination.tsx
- users-table.tsx
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/22 files
settings/6 files
- connected-accounts-card.tsx
- email-card.tsx
- password-card.tsx
- profile-card.tsx
- reauthenticate-alert.tsx
- sessions-card.tsx
- account-settings.tsx
- auth-card.tsx
- auth-setup-notice.tsx
- check-inbox.tsx
- focus-first-error.ts
- forgot-password-form.tsx
- header-actions.tsx
- magic-link-form.tsx
- oauth-buttons.tsx
- password-input.tsx
- provider-icons.tsx
- reset-password-form.tsx
- session-provider.tsx
- sign-in-form.tsx
- sign-out-button.tsx
- sign-up-form.tsx
billing/12 files
- billing-notices.tsx
- checkout-pending.tsx
- current-plan-card.tsx
- plan-card.tsx
- plan-status.tsx
- plan-summary-card.tsx
- portal-button.tsx
- pricing-faq.tsx
- pricing-notice.tsx
- pricing-preview.tsx
- pricing-table.tsx
- purchase-history.tsx
blog/2 files
- callout.tsx
- post-card.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
observability/1 file
- sentry-identity.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
- billing-schema.ts
- client.ts
- connection-url.ts
- direct-url.ts
- driver.ts
- drizzle.ts
- email-schema.ts
- health.ts
- index.ts
- load-env.ts
- migrate.ts
- neon-local.ts
- schema.ts
- tables.ts
- verify.ts
hooks/1 file
- use-mobile.ts
lib/113 files
admin/15 files
- actions.ts
- audit-store.ts
- audit.ts
- contract.ts
- cursor.ts
- format.test.ts
- format.ts
- policy.test.ts
- policy.ts
- provider.ts
- query.test.ts
- query.ts
- types.ts
- user-store.ts
- users.ts
analytics/5 files
- events.ts
- identify.ts
- posthog-client.ts
- posthog-server.ts
- provider.tsx
auth/23 files
- action-limit.test.ts
- action-limit.ts
- actions.ts
- adapter.ts
- auth.ts
- client.ts
- endpoint-guard.test.ts
- endpoint-guard.ts
- errors.ts
- guards.test.ts
- list-sessions.test.ts
- list-sessions.ts
- policy.ts
- providers.test.ts
- providers.ts
- roles.ts
- schema.ts
- schemas.ts
- secret.ts
- session.ts
- setup.ts
- user-agent.test.ts
- user-agent.ts
billing/32 files
- actions.ts
- catalog.test.ts
- catalog.ts
- checkout.ts
- entitlement.test.ts
- entitlement.ts
- entitlements.ts
- errors.ts
- format.ts
- index.ts
- origin.ts
- overview.ts
- price-refs.ts
- provider.ts
- purchase-sql.ts
- rate-limit-rules.test.ts
- rate-limit-rules.ts
- rate-limit-sql.ts
- rate-limit.test.ts
- rate-limit.ts
- receipt.ts
- report.ts
- sign-up-url.ts
- store.ts
- stripe-objects.test.ts
- stripe-objects.ts
- stripe-provider.test.ts
- stripe.ts
- types.ts
- user.ts
- webhook.test.ts
- webhook.ts
blog/2 files
- frontmatter.ts
- posts.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
observability/2 files
- scrub.ts
- sentry.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
- pricing.ts
- routes.ts
- site.test.ts
- site.ts
- validate.ts
- verify.ts
- instrumentation-client.ts
- instrumentation.ts
- mdx-components.tsx
- proxy.ts
tests/19 files
e2e/14 files
- admin.spec.ts
- app-shell.spec.ts
- auth.setup.ts
- auth.spec.ts
- auth.ts
- billing-webhook.spec.ts
- checkout.spec.ts
- fixtures.ts
- impersonation-lock.spec.ts
- landing.spec.ts
- legal.spec.ts
- outbox.ts
- pricing.spec.ts
- users.ts
unit/4 files
- email-outbox.test.ts
- email.test.ts
- env.test.ts
- scrub.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
- sentry.edge.config.ts
- sentry.server.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`), Neon (`neon`), Better Auth (`better-auth`), Stripe (`stripe`), Resend (`resend`), Sentry (`sentry`), PostHog (`posthog`), Admin panel (`admin-panel`), MDX blog (`blog-mdx`)
- **Design:** Daylight (`daylight`). 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/`: 30 rules. 4 load every session, 26 load when you read a file they cover.
- `.claude/agents/`: 7 subagents, listed below.
- `.claude/skills/`: 28 skills, listed below.
- `.claude/hooks/`: 7 guard hooks, wired in `.claude/settings.json` for Claude Code.
- `.mcp.json`: 5 MCP servers (`better-auth`, `neon`, `posthog`, `sentry`, `stripe`). Setup is in [docs/onboard.md](docs/onboard.md).
- `docs/solutions/`: 74 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)
- [No personal data in breadcrumbs, tags or extra](.claude/rules/sentry-no-pii.md)
Loaded when you read a file they cover:
| Rule | Applies to |
|---|---|
| [Admin pages check the role themselves and read data on the server](.claude/rules/admin-access.md) | `src/app/(admin)/**`, `src/components/admin/**`, `src/lib/admin/actions.ts`, `src/app/api/admin/**` |
| [Admin writes go through the provider port, and every one is audited](.claude/rules/admin-mutations.md) | `src/lib/admin/**`, `src/components/admin/**`, `scripts/admin/**` |
| [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` |
| [The auth server boundary and sign-in methods](.claude/rules/auth-server-boundary.md) | `src/lib/auth/**`, `src/app/api/auth/**`, `src/app/(better-auth)/**`, `src/components/auth/**` |
| [Billing is one shared layer with one provider adapter](.claude/rules/billing-core.md) | `src/lib/pricing.ts`, `src/lib/billing/**`, `src/app/pricing/**`, `src/app/(app)/billing/**`, `src/components/billing/**` |
| [Posts are validated content, not free-form files](.claude/rules/blog-content.md) | `content/**` |
| [The blog compiles at build time and styles itself with tokens](.claude/rules/blog-rendering.md) | `src/app/blog/**`, `src/app/sitemap.ts`, `src/lib/blog/**`, `src/components/blog/**`, `src/mdx-components.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/**` |
| [One connection surface, and the right driver for the job](.claude/rules/neon-connections.md) | `src/db/**`, `src/app/api/**` |
| [Schema changes go through ORM migration files on the direct URL](.claude/rules/neon-migrations.md) | `src/db/**` |
| [Roles are decided on the server, every time](.claude/rules/roles-are-server-side.md) | `src/app/**`, `src/components/**`, `src/lib/auth/**` |
| [Every captured exception carries a stable fingerprint hint](.claude/rules/sentry-capture.md) | `src/**`, `sentry.server.config.ts`, `sentry.edge.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/**` |
| [Stripe translates, the shared billing core writes the store](.claude/rules/stripe-billing-store.md) | `src/lib/billing/**`, `scripts/billing/**` |
| [Plans live in pricing.ts, Stripe ids live in env, amounts never come from the client](.claude/rules/stripe-pricing-source-of-truth.md) | `src/lib/pricing.ts`, `src/lib/billing/**`, `scripts/billing/**` |
| [Stripe objects are server-only](.claude/rules/stripe-server-boundary.md) | `src/lib/billing/**`, `src/app/api/webhooks/stripe/**`, `src/app/**` |
| [Verify every Stripe webhook, keep every handler idempotent](.claude/rules/stripe-webhook-integrity.md) | `src/app/api/webhooks/stripe/**`, `src/lib/billing/provider.ts`, `src/lib/billing/webhook.ts` |
| [Testing rules](.claude/rules/testing.md) | `tests/**`, `src/**/*.test.ts`, `src/**/*.test.tsx` |
| [Daylight: 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/**` |
## Skills
| Skill | Use it for |
|---|---|
| `/add-admin-action` | Add an admin action (verify an email, reset a plan, delete an account) as a checked, validated, audited server action with a confirm dialog. |
| `/add-admin-page` | Add a page to /admin with the role check, a sidebar entry, loading and empty states, and data read through the admin ports. |
| `/add-app-page` | Add a page to the signed-in app (sidebar entry, session check, loading state), or a new tab under /settings. |
| `/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-mdx-component` | Add a component that posts can use without importing it, registered in src/mdx-components.tsx and styled with design tokens only. |
| `/add-oauth-provider` | Turn on Google, GitHub or Microsoft sign-in (env keys only), or add another OAuth provider to the list in src/lib/auth/providers.ts. |
| `/add-plan` | Add or change a plan or price on Stripe (monthly, yearly or one-time lifetime). Edit src/lib/pricing.ts, create the Stripe price, wire its env var, and prove checkout and the webhook end to end. |
| `/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. |
| `/db-branch` | Create, use, reset and delete Neon database branches, for a feature branch, a preview deploy, a migration rehearsal or a point-in-time investigation. |
| `/deploy-to-vercel` | Ship my-app to Vercel: local gates, environment variables per scope, preview verification, promotion and rollback. |
| `/edit-pricing` | Add, change or remove a plan or a price (monthly, yearly or one-time lifetime) and wire it to the payment provider. |
| `/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. |
| `/migrate` | Generate, review and apply Drizzle migrations safely, including backfills, destructive changes and the deploy step. |
| `/migrate-on-neon` | Run a schema migration against Neon safely, on the direct URL, rehearsed on a branch first, with a recovery path when it fails halfway. |
| `/new-component` | Add a component to the Daylight kit. Prefer pasting from shadcn/ui, fix the two bridge classes, keep it token-only and verify it in both light and dark. |
| `/new-post` | Draft, validate and publish a new MDX post in content/blog, with frontmatter that passes the checker and a slug that will never change. |
| `/preview-and-test-email` | Diagnose an email problem (not sending, landing in spam, rendering wrong) in the order that finds the cause fastest. |
| `/protect-route` | Put an authentication or role check on a page, a route handler, a server action or a whole route group, at the right layer, without a redirect loop. |
| `/qa-feature` | Exercise a feature end to end (happy path, unhappy paths, auth boundaries, refresh and mobile) before anyone calls it done. |
| `/scrub-pii` | Audit what this app actually sends to Sentry, extend the scrubbing layer for a new field or shape, and respond when something sensitive has already been sent. |
| `/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. |
| `/test-webhook` | Exercise the Stripe webhook endpoint locally. Forward real events with the CLI, buy a subscription and a one-time price, refund one, assert idempotency, and debug signature failures. |
| `/triage-errors` | Work the Sentry issue list: rank by users affected, separate regressions from background noise, find the deploy that caused it, and fix or suppress with a reason. |
| `/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 |
|---|---|
| `db-inspector` | Read-only Neon Postgres inspector. Explains schema, data shape and query plans. Runs SELECT and EXPLAIN only, and refuses every statement that writes or changes schema. |
| `designer` | Owns the Daylight 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/(admin)/**
- src/components/admin/**
- src/lib/admin/actions.ts
- src/app/api/admin/**
---
# Admin pages check the role themselves and read data on the server
## Three checks, each for a different question
| Where | Call | What it stops |
|---|---|---|
| `src/app/(admin)/layout.tsx` | `requireRole("admin", returnTo)` | A non-admin ever seeing the shell |
| Every `page.tsx` | `requireRole("admin", "/admin/<path>")` | A stale layout: layouts do not re-render on client navigation |
| Every server action | `authorise()` in `src/lib/admin/actions.ts` | Anyone with curl: an action is a public POST endpoint |
```tsx
export default async function AdminReportsPage() {
await requireRole("admin", "/admin/reports");
// load data, render
}
```
The page's call is free: each auth battery wraps its session read in React
`cache`. Pass the page's own path so sign-in brings the admin back to it.
Every export of `src/lib/admin/actions.ts` starts with `authorise()`, before it
reads a form field. It runs `requireRole("admin")`, then re-reads the admin
from the provider, because a role removed a minute ago can still sit in a
cached cookie (Better Auth, 5 minutes) or an unexpired token (Clerk, Supabase).
The one exception is `stopImpersonationAction`: the impersonated session is not
an admin session, so it checks `adminProvider.getImpersonation()` instead.
A route handler under `src/app/api/admin/**` uses `requireApiRole("admin")`
(401 or 403, never a redirect) and catches with `authErrorResponse`.
Never:
- rely on the proxy: `/admin/:path*` is in its matcher so signed-out visitors
bounce early, but it has no role check worth trusting;
- compare strings (`user.role === "admin"` misses Clerk's `owner`): use
`requireRole`, or `navItemsFor` for what the UI shows;
- move a page out of `src/app/(admin)/admin/`: it loses the shell and the
layout's check, and nothing warns you;
- render the shell for a refused user: `requireRole` answers with a 404 or the
auth battery's no-access page, and the chrome would tell an outsider the
page exists.
## Pages load, components render
A page is a Server Component. It calls the read helpers (`listUsers`,
`getUser`, `getUserStats` from `@/lib/admin/users`, `listAuditEntries` from
`@/lib/admin/audit`) and passes results down. Load independent data with
`Promise.all`. A slow section goes in its own async component inside
`<Suspense>` with a skeleton, as `/admin` does for its counts.
Components under `src/components/admin/**` take props. Two kinds load their
own data because a slot cannot pass them any: `ImpersonationBanner` (the
`app-banner` fill) and the billing summary (`AdminBillingStats`,
`AdminBillingCard`). Do not add a third without the same reason.
## Keep data on the server
- Modules in `src/lib/admin/` that reach a provider open with
`import "server-only"`: `users.ts`, `audit.ts`, `user-store.ts`,
`provider.ts`, `audit-store.ts`. Client components import only the pure
ones (`types.ts`, `policy.ts`, `format.ts`, `query.ts`, `cursor.ts`) and the
`"use server"` actions.
- Pick fields for client components: `UserActions` gets
`{ id, name, email, role, banned }`, never a database row.
- Never send a session token to the browser. The page sends a session id;
`findSessionToken` turns it into a token on the server.
- Filters and cursors live in the URL, parsed by `parseUserQuery` and
`parseAuditQuery`. A value that does not parse means "no filter", never an
error page.
## Look like the rest of the app
Use the kit (`@/components/ui/*`), `PageHeader` and `EmptyState` from
`@/components/app/*`, and token classes (`bg-surface-card`, `text-muted`,
`border-hairline`). No palette classes, no hex, no `dark:` for colour: the
panel must read well in every design, light and dark.
{
"$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"
},
{
"type": "command",
"command": "bun \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/guard-neon-sql.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 (7)
Subagents in .claude/agents/. Each carries its own system prompt and, where it matters, a tool allowlist.
- Neon
db-inspector
Read-only Neon Postgres inspector. Explains schema, data shape and query plans. Runs SELECT and EXPLAIN only, and refuses every statement that writes or changes schema.
tools: Read, Grep, Glob, Bash, mcp__neon
- Daylight
designer
Owns the Daylight 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
Show all 7Show fewer
- 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
- 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 (28)
Slash commands in .claude/skills/, encoding the repeatable jobs for this selection.
- Admin panel
/add-admin-action
Add an admin action (verify an email, reset a plan, delete an account) as a checked, validated, audited server action with a confirm dialog.
.claude/skills/add-admin-action/SKILL.md
- Admin panel
/add-admin-page
Add a page to /admin with the role check, a sidebar entry, loading and empty states, and data read through the admin ports.
.claude/skills/add-admin-page/SKILL.md
- 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
- 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
Show all 28Show fewer
- MDX blog
/add-mdx-component
Add a component that posts can use without importing it, registered in src/mdx-components.tsx and styled with design tokens only.
.claude/skills/add-mdx-component/SKILL.md
- Better Auth
/add-oauth-provider
Turn on Google, GitHub or Microsoft sign-in (env keys only), or add another OAuth provider to the list in src/lib/auth/providers.ts.
.claude/skills/add-oauth-provider/SKILL.md
- Stripe
/add-plan
Add or change a plan or price on Stripe (monthly, yearly or one-time lifetime). Edit src/lib/pricing.ts, create the Stripe price, wire its env var, and prove checkout and the webhook end to end.
.claude/skills/add-plan/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
- Neon
/db-branch
Create, use, reset and delete Neon database branches, for a feature branch, a preview deploy, a migration rehearsal or a point-in-time investigation.
.claude/skills/db-branch/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
- Drizzle ORM
/migrate
Generate, review and apply Drizzle migrations safely, including backfills, destructive changes and the deploy step.
.claude/skills/migrate/SKILL.md
- Neon
/migrate-on-neon
Run a schema migration against Neon safely, on the direct URL, rehearsed on a branch first, with a recovery path when it fails halfway.
.claude/skills/migrate-on-neon/SKILL.md
- Daylight
/new-component
Add a component to the Daylight 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
- MDX blog
/new-post
Draft, validate and publish a new MDX post in content/blog, with frontmatter that passes the checker and a slug that will never change.
.claude/skills/new-post/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
- Better Auth
/protect-route
Put an authentication or role check on a page, a route handler, a server action or a whole route group, at the right layer, without a redirect loop.
.claude/skills/protect-route/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
- Sentry
/scrub-pii
Audit what this app actually sends to Sentry, extend the scrubbing layer for a new field or shape, and respond when something sensitive has already been sent.
.claude/skills/scrub-pii/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
- Stripe
/test-webhook
Exercise the Stripe webhook endpoint locally. Forward real events with the CLI, buy a subscription and a one-time price, refund one, assert idempotency, and debug signature failures.
.claude/skills/test-webhook/SKILL.md
- Sentry
/triage-errors
Work the Sentry issue list: rank by users affected, separate regressions from background noise, find the deploy that caused it, and fix or suppress with a reason.
.claude/skills/triage-errors/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 (30)
Rules in .claude/rules/. The glob is what loads them only where they apply, instead of one CLAUDE.md an agent skims.
- Admin panel
admin-access
Admin pages check the role themselves and read data on the server
src/app/(admin)/** · src/components/admin/** · src/lib/admin/actions.ts · src/app/api/admin/**
- Admin panel
admin-mutations
Admin writes go through the provider port, and every one is audited
src/lib/admin/** · src/components/admin/** · scripts/admin/**
- 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
- Better Auth
auth-server-boundary
The auth server boundary and sign-in methods
src/lib/auth/** · src/app/api/auth/** · src/app/(better-auth)/** · src/components/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/**
Show all 30Show fewer
- MDX blog
blog-content
Posts are validated content, not free-form files
content/**
- MDX blog
blog-rendering
The blog compiles at build time and styles itself with tokens
src/app/blog/** · src/app/sitemap.ts · src/lib/blog/** · src/components/blog/** · src/mdx-components.tsx
- Next.js on Vercel
code-style
Code style and file conventions
repo-wide
- 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/**
- Neon
neon-connections
One connection surface, and the right driver for the job
src/db/** · src/app/api/**
- Neon
neon-migrations
Schema changes go through ORM migration files on the direct URL
src/db/**
- Better Auth
roles-are-server-side
Roles are decided on the server, every time
src/app/** · src/components/** · src/lib/auth/**
- Next.js on Vercel
security
Security rules
repo-wide
- Sentry
sentry-capture
Every captured exception carries a stable fingerprint hint
src/** · sentry.server.config.ts · sentry.edge.config.ts
- Sentry
sentry-no-pii
No personal data in breadcrumbs, tags or extra
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/**
- Stripe
stripe-billing-store
Stripe translates, the shared billing core writes the store
src/lib/billing/** · scripts/billing/**
- Stripe
stripe-pricing-source-of-truth
Plans live in pricing.ts, Stripe ids live in env, amounts never come from the client
src/lib/pricing.ts · src/lib/billing/** · scripts/billing/**
- Stripe
stripe-server-boundary
Stripe objects are server-only
src/lib/billing/** · src/app/api/webhooks/stripe/** · src/app/**
- Stripe
stripe-webhook-integrity
Verify every Stripe webhook, keep every handler idempotent
src/app/api/webhooks/stripe/** · src/lib/billing/provider.ts · src/lib/billing/webhook.ts
- Next.js on Vercel
testing
Testing rules
tests/** · src/**/*.test.ts · src/**/*.test.tsx
- Daylight
tokens-only
Daylight: 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/**
Hooks (7)
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 7Show 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
- Neon
guard-neon-sql
Blocks raw DDL through psql, migrations that would really run through the Neon pooler, and schema pushes that skip migration files. The repo's own db:migrate scripts pass.
PreToolUse · Bash
Institutional memory
74 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.
Admin panel (8)
- Designing an audit log for an admin panelOne append-only table, namespaced past-tense actions, emails copied in, and a clear rule for when the audit row can share a transaction with the change and when it cannot.docs/solutions/admin-panel/adding-an-audit-log.md
- Bans that actually sign people outSetting banned = true stops the next sign-in, not the session already open. What Better Auth, Clerk and Supabase do on a ban, where caches and tokens let a banned user linger, and how to say so.docs/solutions/admin-panel/bans-and-session-revocation.md
- Empty states that are not sadAn empty state that only says "No data available" is a dead end. It should say what belongs here, why it is missing, and what to do next.docs/solutions/admin-panel/empty-states-that-are-not-sad.md
- Making the first admin without a back doorA fresh deploy has no admin, and the admin page needs one to add one. Use a terminal script with database or API credentials, never an env list of emails or a first-user-wins rule.docs/solutions/admin-panel/first-admin-without-a-backdoor.md
- impersonating-users-safelydocs/solutions/admin-panel/impersonating-users-safely.md
- Paginating admin tables without a client libraryOffset pages with a total for the users table, keyset cursors for the audit log, both in the URL and rendered on the server. When to use which, and the details that make each correct.docs/solutions/admin-panel/paginating-a-users-table.md
- Role checks that survive a layout refactorA check that lives only in a layout disappears the day someone moves the page. Put the boundary where the route is, and check again where the work happens.docs/solutions/admin-panel/role-checks-that-survive-a-refactor.md
- Route group or path segment: how to lay out an admin sectionA route group shares a layout without touching the URL; a path segment is the URL. Admin panels need both, and confusing them produces public pages and 404s.docs/solutions/admin-panel/route-group-vs-path-segment.md
Show all 74Show fewer
Better Auth (10)
- Account linking and email verification without account takeoversWhen "Continue with Google" joins an existing password account, when it refuses, and why an unverified email address or a trusted provider list can hand one person's account to another.docs/solutions/better-auth/account-linking-and-email-verification.md
- Better Auth on the edge: why your session check fails in middlewareEdge runtimes have no TCP sockets and no Node crypto, so a session lookup that works in a page throws in the proxy. Read the cookie there and verify in the render.docs/solutions/better-auth/better-auth-on-the-edge.md
- CSRF, SameSite and the cookie flags that make a session safeWhat each session cookie flag actually defends against, why trustedOrigins is your CSRF check, and the three configuration changes that quietly disable both.docs/solutions/better-auth/csrf-and-cookie-flags.md
- Guard Better Auth's endpoints, not just your settings formsEvery /api/auth endpoint is a public URL. One hook refuses account changes from an impersonation session and asks for a recent sign-in before a password or provider is added.docs/solutions/better-auth/guarding-the-auth-api-itself.md
- The magic-link token: single use, ten minutes, and the scanner that clicks it firstHow long the credential lives, why a corporate mail scanner burns it before the human arrives, and why the rate limiter has to be backed by your database rather than by process memory.docs/solutions/better-auth/magic-link-tokens-and-scanners.md
- Moving an existing user table onto Better Auth without logging everyone outMap your columns to the four required tables, backfill ids and accounts, and let people migrate themselves on next sign-in instead of forcing a password reset.docs/solutions/better-auth/migrating-an-existing-user-table.md
- oauth-callback-url-mismatchesdocs/solutions/better-auth/oauth-callback-url-mismatches.md
- Password reset tokens that cannot be replayed, guessed or leakedOne hour, single use, answered the same way for every address, and kept out of logs, Referer headers and search results. What Better Auth does for you and the four things it cannot.docs/solutions/better-auth/password-reset-tokens.md
- Modelling roles you will not regret when the admin panel growsA role column, a ranked vocabulary in one file, and permission checks that name the action, not a boolean isAdmin scattered across forty components.docs/solutions/better-auth/role-modelling-for-the-admin-panel.md
- Session invalidation, or why everyone got logged out on deployA rotated secret, a changed cookie name or a wiped database invalidates every session at once. Here is what invalidates what, and how to revoke one user on purpose.docs/solutions/better-auth/session-invalidation-and-logged-out-on-deploy.md
MDX blog (5)
- MDX in the App Router without shipping a compilernext-mdx-remote compiles every post on every request. Compile at build time with @next/mdx instead, and keep frontmatter with gray-matter.docs/solutions/blog-mdx/mdx-without-a-runtime.md
- OG images that render your font, not NotoImageResponse has no system fonts and silently falls back. Load a static TTF from disk, and know why woff2 and variable fonts fail.docs/solutions/blog-mdx/og-images-that-render-your-font.md
- An RSS feed that actually validatesUnescaped ampersands, ISO dates and a missing atom:link are why feed readers reject your feed. Generate it as a static route with escaped text.docs/solutions/blog-mdx/rss-that-validates.md
- A sitemap and canonical URLs that do not fight each otherlastModified from new Date() teaches crawlers to ignore your dates, and a canonical built from request headers points at your preview deploys.docs/solutions/blog-mdx/sitemap-and-canonical-urls.md
- Syntax highlighting without a 300kb bundlePrism and highlight.js in a client component ship a parser to every reader. Highlight at build time with a rehype plugin and ship CSS instead.docs/solutions/blog-mdx/syntax-highlighting-without-a-huge-bundle.md
Daylight (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/daylight/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/daylight/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/daylight/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/daylight/tailwind-v4-dark-mode-does-nothing.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
Neon (6)
- A Neon branch per preview deploymentPreview deploys that share the production database corrupt it or lie to you. Give every preview its own copy-on-write Neon branch, wired to the deployment's environment variables.docs/solutions/neon/branch-per-preview-deployment.md
- Neon cold starts, where the half second goes and what to do about itScale-to-zero means an idle branch takes roughly 500 ms to wake, and a serverless function adds its own cold start on top. How to measure the parts and fix the ones that matter.docs/solutions/neon/cold-start-latency.md
- Connection exhaustion on serverless Postgres, and how to actually fix itServerless does not queue requests on a pool, it creates pools. Here is the arithmetic, the four real causes, and the fix for each.docs/solutions/neon/connection-exhaustion-in-serverless.md
- Local Postgres with the Neon serverless driver, no Neon accountThe Neon driver speaks HTTPS and WebSocket, not the Postgres wire protocol. A small local proxy plus two neonConfig settings let it run against a Postgres on your laptop, with no code fork.docs/solutions/neon/local-postgres-without-a-neon-account.md
- Running migrations on Vercel without a half-applied schemaVercel has no migration step, so people add one in the wrong place. Where migrations belong in the build, why the direct URL is mandatory, and how to deploy a breaking change in two safe halves.docs/solutions/neon/migrations-on-vercel.md
- Neon's pooled and unpooled connection strings, and which one to use whereThe -pooler host and the direct host are not interchangeable. Runtime queries need the pooler; migrations, advisory locks and session state need the direct endpoint.docs/solutions/neon/pooled-vs-unpooled-connections.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
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
Sentry (5)
- One issue with 40,000 events: grouping, fingerprints and the helper that ruined themSentry groups by stack trace, so a shared fetch wrapper merges every unrelated failure into one useless issue. Fingerprints put the grouping back where the cause is.docs/solutions/sentry/grouping-noisy-errors-with-fingerprints.md
- Scrubbing PII before it leaves your process, not after it reaches SentryServer-side scrubbing runs after the data has crossed the network. beforeSend runs in your process, and it is the only layer you fully control.docs/solutions/sentry/scrubbing-pii-before-it-leaves-the-process.md
- Source maps on Vercel: why your production stack traces are unreadableA minified trace means the build never uploaded source maps. The auth token, the release name and the preview environment are the three things that are usually wrong.docs/solutions/sentry/source-maps-on-vercel.md
- Telling a real incident from a bot, a browser extension or a stale tabMost of a new project's error feed is not your bug. The four signatures of noise, how to filter each one at the right layer, and the three signals that mean it is real.docs/solutions/sentry/telling-a-real-incident-from-a-bot.md
- Trace sample rates that do not bankrupt youErrors are cheap and spans are not. How to pick tracesSampleRate, why the edge runtime needs a lower one, and how to keep the traces you actually need while dropping 95% of the rest.docs/solutions/sentry/trace-sample-rates-that-do-not-bankrupt-you.md
Stripe (6)
- Free trials in Stripe that do not leak accesstrialing is an entitled status and trial_will_end is not a payment. Where trials are configured, which events actually matter, and how to stop one person taking ten trials.docs/solutions/stripe/free-trials-that-do-not-leak.md
- Idempotent Stripe webhooks, or claim the event id before you do the workStripe delivers at least once, retries for three days and arrives out of order. An event ledger claimed before the handler runs is what stops double grants and lost updates.docs/solutions/stripe/idempotent-stripe-webhooks.md
- One-time payments with Stripe Checkout, from lifetime deal to refundA lifetime deal is a payment-mode Checkout Session, not a subscription. Which id to key it on, when it is really paid, how a receipt goes out, and how a refund or dispute takes the access back.docs/solutions/stripe/one-time-payments-with-stripe-checkout.md
- Proration when a customer changes plan, and why you should let the portal do itUpgrades, downgrades and seat changes each want different proration behaviour. What Stripe actually does, how to preview the amount, and when to build the flow yourself.docs/solutions/stripe/proration-when-plans-change.md
- Reconciling after a missed Stripe webhookA customer paid and the app does not know. How to find the gap, replay or re-sync the affected subscriptions and one-time purchases, and build a reconciliation job so the next outage is boring.docs/solutions/stripe/reconciling-a-missed-webhook.md
- Stripe Tax, and when you legally need to careWith Stripe you are the merchant of record, so sales tax and VAT are your liability. What Stripe Tax does and does not do, the thresholds that trigger obligations, and when a merchant of record is the better answer.docs/solutions/stripe/stripe-tax-and-when-you-need-it.md
Use Indie SaaS
The builder opens with these 9 batteries picked. Change anything, then download the zip.