Roadmap
What the kit is missing and in what order. Read it before proposing an addition — the answer may already be here, ranked, with a reason.
This is a queue, not a promise. Items leave it by being built or by being ruled out; both are progress. The bar every entry answers is the one in ABOUT-THE-KIT.md: does this make it easier to build a better custom site?
The shape of the list
Most of Tier 1 is not new features. It is code the docs already describe as existing — handleError sending a throttled alert, a submit-timing check on the contact form. Those decisions were made and written down; only the implementation is missing. They need no "prove it on three projects" justification, and they rank above everything else because a doc that lies is worse than a doc that is silent.
Everything below Tier 1 is a real judgement call and is treated as one.
Tier 1 — build next
Site identity and discoverability shipped on 2026-07-22, and the basic security headers followed the same day — one list in $lib/server/security-headers.ts, applied by the securityHeaders handle for SSR routes and mirrored in vercel.json for prerendered pages, kept identical by a spec. What remains is CSP, the error path, and the contact form's timing check.
Fails safely
| # | Item | Why |
|---|---|---|
| 1 | Content-Security-Policy via kit.csp | The basic headers shipped 2026-07-22 on both delivery paths. CSP is the piece still missing, and it cannot ship the same way — see below. |
| 2 | $lib/server/email.ts + handleError | HANDBOOK.md describes the error path in full — errorId, safe message, throttled Resend alert. hooks.server.ts has no handleError. A shared send path also removes the inline new Resend() from the contact action and is what auth emails would use later. |
| 3 | Contact form submit-timing check | STACK.md promises "a honeypot field plus a submit-timing check," and the ponytail: comment in the action asks for it. Never reject on one signal alone — that rule is in STACK.md and is not optional. |
CSP is not a vercel.json header. Four of the five public pages prerender to static HTML, so hooks.server.ts never runs for them in production, and a static header cannot carry the per-build hash of SvelteKit's inline bootstrap script — it would force 'unsafe-inline' onto script-src, the one directive actually worth having. Use kit.csp with mode: 'auto' in vite.config.ts: it bakes hashes into prerendered pages as a <meta> tag and uses nonces plus real headers for SSR routes. Dev mode misleads here, because everything is SSR there.
Three consequences follow. Meta-delivered CSP ignores frame-ancestors, report-uri and sandbox, so clickjacking protection still needs a real header — that part does belong in vercel.json. style-src needs 'unsafe-inline' in practice. And Vercel's preview toolbar injects scripts from vercel.live, so allow it or previews break. Ship it with an e2e that asserts hydration ran: progressive enhancement masks a script-blocking CSP, because the contact form degrades to a native POST and its existing tests would stay green.
Tier 2 — candidates, not commitments
Legitimate, unbuilt, and waiting for a project to prove the shape.
JSON-LD structured data. Ship the pattern and an Organization block fed from site.ts, which already exists — not a schema catalogue. LocalBusiness versus Organization versus Person is a per-project call, and a kit that guesses wrong makes the guess harder to undo than to write. Three details that cost real time to rediscover: the serializer must escape < (JSON.stringify(schema).replace(/</g, '\\u003c')) or content can break out of the script tag; emitting <script type="application/ld+json"> from <svelte:head> needs {@html} with a split literal; and Organization.logo wants a square brand mark, not the 1200×630 OG banner.
Typed content, list and detail, plus RSS. "Content is local TypeScript" is a rule in HANDBOOK.md with no worked example, which makes it a rule people follow by improvisation. A blog or case-studies index and its detail route would fix that. It is not universal across clients, but it is the largest recurring build cost on the sites that do need it.
Ruled out for now
Each of these has a reason and a trigger. See "Not baseline" in STACK.md for the same treatment of dependencies.
| Item | Why not, and what would change it |
|---|---|
| Rate limiting on content forms | Honeypot plus timing covers the traffic we have seen on the contact form. Add it when a form is actually being hammered. Admin login is different — credential guessing, not spam — and is already throttled: a fixed-window counter in the login action plus Better Auth's database-backed limiter on /api/auth. |
| Cookie consent | Vercel Analytics is cookieless and aggregated. A client adding GA4 or a chat widget brings the banner with it, per project. |
| Password reset / email verification | Admins are seeded and public signup is off, so nobody can be locked out who cannot run the seed script. A site that turns signup on needs this the same day. |
| Speed Insights | One line and no code to design. It is a plan cost, so it is a per-project switch rather than a kit default. |
| Web manifest and maskable icon set | <meta name="theme-color"> already covers Android's address bar and apple-touch-icon covers the iOS home screen. A manifest and the maskable icons it wants are scaffolding for an installable app, and this kit ships neither a service worker nor an offline story. A project that actually wants a home-screen app brings the service worker with it, and the manifest comes along for that reason. |
Adding to this list
Same bar as the kit itself. A gap between the docs and the code goes straight to Tier 1 — that is a defect, not a feature request. Anything else starts in Tier 2 and needs a real project behind it, not a hypothetical one.
Removing an item counts as progress. If something has sat in Tier 2 across several projects without any of them needing it, that is the answer.