Capability pack
Brochure site
The smallest useful fork, and the one a real business asks for first: pages an owner can edit without a deploy, a form that reaches somebody, and enough structured data that a search engine understands who is behind it. No third-party provider is needed to run it — mail prints to the console until a key is set — but it is not a static site: pages, media, and the inbox live in a Postgres database, and editing them takes one admin account. A fork that wants no database at all keeps the blog (MDX in git) and drops the CMS.
Capabilities
11
All of them are built.
Watched in production
0
11 have never been watched against a real provider.
Provider variables for a fresh project
9
Including the packs it sits on. 9 unset here, each running its documented fallback.
Needed regardless of providers
- A Postgres database — pages, media, inquiries, and revisions live there.
- One admin account, to edit pages and read the inbox (the accounts pack's sign-in, without offering sign-up to visitors).
What is in it
Each row links to where it lives, and carries the state it has in THIS deployment.
- Ready
A non-developer writes Markdown in the admin area and it is served at /<slug>. Drafts are 404 until published or opened through a day-long signed link. The sanitizer, not the author, decides what HTML exists; a reserved-slug test stops a page shadowing a route.
Page table, Markdown through rehype-sanitize, signed preview links
- Ready
Posts are files in the repo, validated and synced into Postgres at deploy; drafts are excluded in the query, not the template.
next-mdx-remote + a deploy-time sync
- Fallback
An admin uploads images once and pastes a snippet into any page. Several images on one line render as a gallery grid; a quote with an attribution line renders as a testimonial card — conventions in CSS, not a page builder. The sanitizer admits same-origin image paths alongside https; a test keeps protocol-relative hosts out.
Needs
R2_ACCOUNT_ID, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, R2_BUCKET, R2_PUBLIC_BASE_URL. Without it: Images are stored locally under .uploads/.The upload flow with an admin-only purpose, a list, a Markdown snippet, delete
- Fallback
A visitor describes a job and attaches up to six photos that go straight to storage; the request is stored before it is mailed, then acknowledged to the sender and announced to the owner. A guest's photo is bound to an httpOnly cookie, claimable only by their own submission, and swept if nothing claims it in a day. Contact messages land in the same inbox.
Needs
RESEND_API_KEY. Without it: The inquiry is stored and the notification prints to the console.Inquiry table, guest-owned presigned photos, an admin inbox, two templated mails
- Ready
One schema that validates on the client and re-validates on the server.
Zod
Transactional email
FallbackOne spec renders both an HTML email and its plain-text alternative; rich by default, MAIL_FORMAT=text for plain only. Two transports; real delivery is a key in .env. Previews at /admin/mail.
Needs
RESEND_API_KEY. Without it: Messages print to the server console in full, links included.Resend (or console transport)
SEO surface
Readyrobots.txt, a database-driven sitemap.xml, a web manifest, favicons, and a per-post and per-product OpenGraph image — all generated from code, no binary assets to keep in sync.
Next metadata routes + next/og
Every page names its publisher: an Organization, or the configured LocalBusiness type with address, phone, hours, and service area when SITE_BUSINESS is filled in. Posts carry Article, products carry Product with an Offer and stock state, admin pages carry WebPage, all with breadcrumbs. The serializer escapes anything that could close the script element, and a test proves it.
schema.org builders in src/lib/seo, one <script type="application/ld+json"> component
- Fallback
One beacon per navigation counts a view per (day, path) and a distinct visitor per day from a salted hash that cannot be linked across days. No cookie, no ip stored, crawlers and private pages excluded, Global Privacy Control honored. /admin/analytics charts real rows.
Needs
NEXT_PUBLIC_VERCEL_ANALYTICS. Without it: First-party cookieless page views are recorded and charted at /admin/analytics.First-party page views (cookieless); Vercel Web Analytics beside it when enabled
Bot protection
FallbackSign-up, forgot-password, and contact carry a hidden field a person never fills and a script always does. With Turnstile keys set, an invisible challenge is verified server-side too; the CSP admits the widget by derivation. One generic message for every failure.
Needs
NEXT_PUBLIC_TURNSTILE_SITE_KEY, TURNSTILE_SECRET_KEY. Without it: The honeypot field runs alone, which catches naive scripts.Honeypot always; Cloudflare Turnstile when configured
Rate limiting
ReadyKeyed per caller, so an abuser is throttled and not the site. Durable by default: one atomic upsert per hit holds the limit across serverless instances; RATE_LIMIT_STORE=memory keeps the per-process limiter, which is also the fallback if the database call fails.
Postgres fixed windows (atomic upsert), or in-memory
First steps in a fork
- 1Edit src/lib/site.ts — the name, tagline, canonical URL, and the local-business facts structured data is built from.
- 2Replace the palette in src/app/globals.css, keeping one flavor and deleting the rest.
- 3Write the first pages at /admin/pages; give the ones that belong in the footer a nav label.
- 4Set CONTACT_TO, then RESEND_API_KEY when you want the inbox to reach a real mailbox.
What to delete
- The shop, forum, game, community, organizations, AI, API-key, newsletter, notification, realtime, i18n, dashboard, Lab, and inventory routes, libraries, and components.
- Their models and enums in prisma/schema.prisma — 40 of 58 — and every relation field pointing at them; then one squashed migration with the search triggers carried by hand.
- Sixteen places those domains reached into the shell this pack keeps: the layout's cart and unread counts, the header's cart and bell, the footer's inventory line, the sweep's commerce steps, the sitemap, the export, health. docs/knowledge-base/extraction.md lists each.
Provider variables a fresh project would set
NEXT_PUBLIC_TURNSTILE_SITE_KEYNEXT_PUBLIC_VERCEL_ANALYTICSR2_ACCESS_KEY_IDR2_ACCOUNT_IDR2_BUCKETR2_PUBLIC_BASE_URLR2_SECRET_ACCESS_KEYRESEND_API_KEYTURNSTILE_SECRET_KEY
Shaded green: set on this deployment.
The full inventory, with the tests that cover each capability and what has actually been watched working.
Capability inventory