Search

Search Kavel...

Documentation menu

Footguns

The short list of things NOT to do with Kavel, in severity order. Every scaffold ships this same list as a FOOTGUNS.md that your AI coding agent reads.

Blockers

These break the build, the deploy, or behaviour if you get them wrong.

Do not use npm, pnpm, yarn or npx

(base) This is a Bun workspace; run scripts with bun run <script>. Other package managers ignore the Bun lockfile and workspace protocol and break the install.

Do not hand-write database migrations

(base) Edit the Drizzle schema in apps/api/src/db/schema, then run bun db:generate && bun db:migrate. Hand-edited migrations drift from the schema and get overwritten.

Do not add ad-hoc REST routes for app data

(base) The API is oRPC, contract-first: declare in packages/backend-contract, implement in apps/api/src/orpc, and call via the typed backend client. Plain Hono routes lose end-to-end types (webhooks are the exception).

Do not commit or log secrets

(base) .dev.vars is gitignored for local development; use wrangler secret put for production, and never log a secret.

Do not ship OAuth buttons without credentials

(auth) Set the Google/GitHub client id and secret in apps/api/.dev.vars, or remove the socialProviders block and the social buttons. Otherwise the buttons render but error.

Do not skip the auth migration

(auth) auth adds tables; run bun db:generate && bun db:migrate or every auth call fails.

Do not skip the email migration

(email) email adds the email_queue table; run bun db:generate && bun db:migrate.

Never hardcode user-facing text

(i18n) Add strings as Paraglide keys in messages/*.json, use them via m.*, and run bun i18n:compile. Hardcoded text never translates.

Do not commit or import paraglide output early

(i18n) apps/web/src/paraglide is generated and gitignored. Run bun i18n:compile once after install; importing from it before the first compile breaks typecheck.

Do not select payments without auth

(payments) Handlers use requireAuth and the user table; payments requires auth.

Do not skip the payments migration

(payments) payments adds the subscription table; run bun db:generate && bun db:migrate.

Do not select marketing-pages without ui

(marketing-pages) The landing, pricing and contact pages use the ui module's components and design tokens.

Gotchas

Easy to get wrong; they silently waste time or ship subtle bugs.

Do not reach for `any` or skip validation

(base) TypeScript is strict and all external input is validated with Zod; an any or an unvalidated request body is a runtime bug waiting to happen.

Do not add ESLint or Prettier

(base) Biome does all linting and formatting; a second formatter fights it and fails CI.

Do not assume email was sent in dev

(email) Without RESEND_API_KEY, sending no-ops with a warning so flows do not fail before email is set up. Nothing is delivered until you add the key.

Do not expect instant delivery

(email) Queued mail is flushed by a 5 minute cron, not on send. Test locally with wrangler dev --test-scheduled.

Do not require email verification before email works

(auth) requireEmailVerification ships false; flip it to true only after RESEND_API_KEY is set, or new users cannot get past signup.

Do not assume auth cookies cross origins

(auth) Credentialed auth needs WEB_URL to match the browser origin exactly; for different subdomains of one apex domain, enable advanced.crossSubDomainCookies.

Do not hardcode `/en` paths

(i18n) The base locale is served unprefixed (/), not /en; only non-base locales are path-prefixed.

Register every new route for localization

(i18n) Add each route to routeLocalizations in apps/web/src/lib/i18n.ts; the type is exhaustive, so the compiler reminds you.

Do not name animation utilities `animate-*` (Tailwind v4)

(ui) The animate- namespace is reserved; a custom @utility animate-* is silently dropped. Name it something else.

Do not remove the `shadcn` devDependency

(ui) It is needed at build time: globals.css imports shadcn/tailwind.css, the utilities the components rely on.

Do not route the Stripe webhook through oRPC

(payments) It is a plain Hono route at /api/stripe/webhook that needs the raw body for signature verification; do not parse the body before verifying it.

Set the Stripe webhook secret in dev

(payments) Run stripe listen --forward-to localhost:8787/api/stripe/webhook and set the printed whsec_... as STRIPE_WEBHOOK_SECRET, or subscription state never updates.

Replace the test Turnstile key before production

(marketing-pages) The contact form ships a Cloudflare test site key. Set your own site key in apps/web/src/routes/contact.tsx and TURNSTILE_SECRET in apps/api/.dev.vars.

Auth CTAs 404 without auth

(marketing-pages) Landing CTAs link to /sign-up and /sign-in via plain <a>; select the auth module or repoint them.

Nits

Conventions that keep you aligned with the kit.

Do not strip the pre-paint theme script

(base) apps/web/src/routes/__root.tsx sets the theme class before first paint; remove it and users see a flash of the wrong theme.

Edit the ui components, they are yours

(ui) Components in packages/ui/src/components follow the shadcn philosophy: they are your code, not a locked dependency. Add more with bunx shadcn@latest add <component>.