kitcn
Version:
kitcn - React Query integration and CLI tools for Convex
813 lines (653 loc) • 38.2 kB
Markdown
# kitcn Setup (Canonical, Greenfield)
Use this runbook to set up a project from scratch so agents can ship features end-to-end without additional setup context.
## 1. Purpose and Scope
This document is the canonical setup reference for kitcn in `.claude`.
In scope:
- Greenfield setup from empty/new app
- ORM-first backend (`ctx.orm`)
- cRPC setup, auth setup, client setup, framework setup
- Optional modules and plugin setup gates
Out of scope:
- Migrations from existing Convex/Ents/legacy data-layer projects
If migration is needed, stop and use migration docs separately. Do not mix migration steps into this runbook.
## 2. Agent Decision Intake
This is the **mandatory first prompt** for agents helping users set up kitcn.
Ask these questions before editing files.
### 2.1 Ask These First
#### Required choices
| Feature | Options | Default |
| --------------- | --------------------------------------------------- | ------------------ |
| Bootstrap | CLI (`kitcn init` / `add`), Docs by section | CLI |
| React Framework | Next.js App Router, TanStack Start, Other | Next.js App Router |
| Database | ORM (`ctx.orm`) | ORM |
#### Optional features
| Feature | Options | When to include |
| ------------- | --------------------------------- | --------------------------------- |
| Auth | Better Auth, Custom, None | Most apps need auth |
| SSR/RSC | Yes, No | Next.js App Router apps |
| Triggers | Yes, No | Auto side effects on data changes |
| Aggregates | Yes, No | Counts, sums, leaderboards |
| Rate Limiting | Yes, No | API protection |
| Scheduling | Yes, No | Background jobs, delayed tasks |
| HTTP router | Yes, No | REST/webhook style endpoints |
| RLS | Yes, No | Runtime row-level access control |
| Auth plugins | admin, organizations, polar, none | Only when product requires them |
### 2.2 First Prompt Template
Use this exact structure:
1. Bootstrap: CLI (`kitcn init` / `add`) or docs by section?
2. Framework: Next.js App Router, TanStack Start, or other?
3. Database: ORM (`ctx.orm`) or other?
4. Auth: Better Auth, custom auth, or no auth?
5. Need SSR/RSC?
6. Enable triggers?
7. Enable aggregates?
8. Enable rate limiting?
9. Enable scheduling?
10. Need HTTP router endpoints?
11. Enable RLS?
12. Any auth plugins (admin/organizations/polar)?
### 2.3 Decision Mapping
Map answers to setup execution in this order:
1. Build base setup first (non-auth only).
2. Pass the non-auth baseline gate (Section 11.2) before starting auth work.
3. If auth is enabled: add auth core.
4. If auth is enabled: pass the auth sign-in gate (Section 11.3) before optional modules/plugins.
5. Add framework branch (Next.js or TanStack Start).
6. Add optional modules/plugins only when selected.
7. If framework is `Other`, stop this runbook and route to non-setup docs (`react`, `server/*`) instead of guessing.
## 3. Base Bootstrap
### 3.1 Preferred bootstrap path
Quickstart path:
```bash
mkdir my-app
cd my-app
npx kitcn@latest init -t next --yes
```
Then start the long-running backend with `bunx kitcn dev`, run the
framework dev server (`bun dev` for the Next starter), and open `/convex` for
the scaffolded messages demo. In `--yes` mode, Convex uses anonymous local
deployment setup when no account is linked yet, so the starter path does not
stop on a login prompt.
Use the CLI first:
```bash
# Adopt the current supported app in place
npx kitcn@latest init --yes
# Adopt the current supported app on Concave
npx kitcn@latest --backend concave init --yes
# New Next.js app with deterministic shadcn bootstrap + first local Convex bootstrap
npx kitcn@latest init -t next --yes
# New Expo app with the official create-expo-app shell + first local Convex bootstrap
npx kitcn@latest init -t expo --yes
# New TanStack Start app with the official shadcn Start shell + first local Convex bootstrap
npx kitcn@latest init -t start --yes
# New Vite app with the React baseline + first local Convex bootstrap
npx kitcn@latest init -t vite --yes
# Nested app target
npx kitcn@latest init -t next --yes --cwd apps --name web
```
Then add only the features you want:
```bash
bunx kitcn add auth
bunx kitcn add ratelimit
bunx kitcn add resend
```
`kitcn init -t next --yes` owns the kitcn integration layer for:
- `app/convex/page.tsx`
- `package.json`
- `tsconfig.json`
- `.env.local`
- `components/providers.tsx`
- `lib/convex/*`
- `convex/functions/messages.ts`
- `convex/functions/generated/messages.runtime.ts`
- `convex/functions/schema.ts`
- `convex/functions/http.ts`
- `convex/functions/generated/server.ts`
- `convex/lib/crpc.ts`
- `convex/lib/get-env.ts`
- `convex/shared/api.ts`
- `kitcn.json`
Template-mode `init -t next` preserves the shadcn-owned shell (`app/layout.tsx`, `app/page.tsx`, `app/globals.css`, `components/theme-provider.tsx`, `lib/utils.ts`, `components.json`, `eslint.config.mjs`, `next.config.mjs`, `postcss.config.mjs`) and only patches:
- `app/layout.tsx` to mount `Providers`
- `tsconfig.json` to add `@convex/*`
- `components.json` when `tailwind.css` needs to follow the resolved app root
- `package.json` to add `kitcn codegen` as `codegen` (or `convex:codegen` when `codegen` is already taken)
`init -t` also runs the first kitcn codegen pass so `convex/lib/crpc.ts` can import `../functions/generated/server` immediately.
Template-mode `init -t` also seeds a live messages demo route plus starter schema and
procedures, so the scaffold has one working query/mutation flow out of the box.
Template-mode `init -t` infers `src/` vs root app layouts and writes the Next client scaffold into the matching tree. Conflicting `src` + root layouts should fail instead of guessing.
Backend resolves from `--backend`, then `backend` in `kitcn.json`, then `convex`.
Universal scaffold rule:
1. `init -t next` owns the stack everyone needs:
- typed `get-env`
- client core
- server core
- root + Convex tsconfig wiring
- `.gitignore` entries for `.convex/` and `.concave/`
- baseline scripts
- fixed `/convex` messages demo
2. `kitcn init` bootstraps the app in both modes: with `-t` for fresh scaffold, without `-t` for in-place adoption.
3. Optional capability belongs in `add`, not the base scaffold:
- `add auth`
- `add ratelimit`
- `add resend`
4. `add auth` is the minimal auth bundle. It owns auth server/client files,
auth-aware provider wiring, auth env fields, and auth cRPC families.
5. `add auth` does not own role/admin policy, orgs, or other app-policy layers.
Public template keys stay concrete:
- `next`
- `expo`
- `start`
- `vite`
Internal scaffold modes stay broad:
- `next-app`
- `react`
Framework mapping:
- `expo` -> `expo`
- `next-app` -> `next-app`
- `next-pages`, `vite`, `react-router`, `tanstack-start`, `manual` -> `react`
React-mode notes:
1. `init -t vite` scaffolds the universal baseline plus the React client core.
2. It does not scaffold RSC helpers.
3. It does not scaffold the `/convex` messages demo in v1.
4. `add auth` on the React baseline follows the Better Auth React/Vite SPA shape and skips auth page/router UX in v1.
### 3.2 Manual app creation and baseline packages
```bash
bunx create-next-app@latest my-app --typescript --tailwind --eslint --app --src-dir
cd my-app
bun add convex kitcn zod @tanstack/react-query
```
### 3.3 Create baseline folders
```bash
mkdir -p convex/functions convex/lib convex/shared src/lib/convex
```
If the goal is full template-level backend parity, also scaffold:
```bash
mkdir -p convex/functions/items convex/lib/auth convex/lib/emails convex/routers
```
Recommended monolithic structure:
```text
src/ # app/client
convex/functions/ # deployed Convex functions
convex/lib/ # backend helpers (not deployed as API)
convex/shared/ # shared types/meta imported by client
```
### 3.4 Configure Convex functions path and static codegen
**Create:** `convex.json`
```json
{
"functions": "convex/functions",
"codegen": {
"staticApi": true,
"staticDataModel": true
}
}
```
### 3.5 Configure TypeScript aliases and strict function typing
**Edit:** `tsconfig.json`
```json
{
"compilerOptions": {
"strict": true,
"strictFunctionTypes": false,
"types": ["node"],
"paths": {
"@/*": ["./src/*"],
"@convex/*": ["./convex/shared/*"]
}
}
}
```
Type-clean baseline notes:
1. Keep app/runtime node globals available (`types: ["node"]`) so `process.env` and server modules typecheck.
2. Add test-only globals (for example `vitest/globals`) in a test-specific tsconfig instead of the main app tsconfig.
3. If third-party declaration noise blocks setup, temporarily set `"skipLibCheck": true` and remove it once dependency versions are stabilized.
4. In backend Convex files, import `./_generated/*` relatively; `@convex/*` is for shared generated surface (`convex/shared/*`).
### 3.6 Enforce import boundaries (recommended)
**Edit:** `biome.jsonc`
```jsonc
{
"extends": ["ultracite/core", "ultracite/react", "ultracite/next"],
"overrides": [
{
"includes": ["src/**/*.ts*"],
"linter": {
"rules": {
"style": {
"noRestrictedImports": {
"level": "error",
"options": {
"paths": {
"convex/values": {
"importNames": ["ConvexError"],
"message": "Use CRPCError from 'kitcn/crpc' instead.",
},
"convex/react": "Use useCRPC from '@/lib/convex/crpc' instead.",
"convex/nextjs": "Use caller from '@/lib/convex/rsc' instead.",
},
"patterns": [
{
"group": ["**/../convex/**"],
"message": "Use @convex/* alias instead of relative convex imports.",
},
],
},
},
},
},
},
},
{
"includes": ["convex/**/*.ts*"],
"linter": {
"rules": {
"style": {
"noRestrictedImports": {
"level": "error",
"options": {
"patterns": [
{
"group": ["@/*", "**/src/**"],
"message": "Convex files cannot import from src/.",
},
],
},
},
},
},
},
},
{
"includes": ["convex/shared/**/*.ts*"],
"linter": {
"rules": {
"style": {
"noRestrictedImports": {
"level": "error",
"options": {
"patterns": [
{
"group": ["**/convex/lib/**"],
"message": "convex/shared cannot import from convex/lib.",
},
],
},
},
},
},
},
},
],
}
```
## 4. Environment Variables
### 4.1 Local
**Create:** `.env.local`
```bash
# Convex WebSocket API
NEXT_PUBLIC_CONVEX_URL=http://localhost:3210
# Convex HTTP site URL
NEXT_PUBLIC_CONVEX_SITE_URL=http://localhost:3211
# App URL for Better Auth client
NEXT_PUBLIC_SITE_URL=http://localhost:3000
```
### 4.2 Cloud
```bash
# Generated by Convex
NEXT_PUBLIC_CONVEX_URL=https://your-project.convex.cloud
# Must be set manually
NEXT_PUBLIC_CONVEX_SITE_URL=https://your-project.convex.site
NEXT_PUBLIC_SITE_URL=http://localhost:3000
```
Rule: real-time URL uses `.cloud`; HTTP/router/caller URL uses `.site`.
Local auth contract:
1. Default app origin is `http://localhost:3000`.
2. If you move the app to another local port, update `.env.local`
`NEXT_PUBLIC_SITE_URL`, `convex/.env` `SITE_URL`, and the app dev script
together.
### 4.3 Typed env helper (recommended for full backend parity)
When multiple Convex functions and libs share env values (auth, billing, dev guards), create one typed helper:
**Create:** `convex/lib/get-env.ts`
```ts
import { createEnv } from "kitcn/server";
import { z } from "zod";
export const getEnv = createEnv({
schema: z.object({
DEPLOY_ENV: z.string().default("production"),
SITE_URL: z.string().default("http://localhost:3000"),
BETTER_AUTH_SECRET: z.string(),
JWKS: z.string().optional(),
ADMIN: z
.string()
.default("")
.transform((s) => (s ? s.split(",") : []))
.pipe(z.array(z.string())),
RESEND_API_KEY: z.string().optional(),
POLAR_ACCESS_TOKEN: z.string().optional(),
POLAR_SERVER: z.enum(["production", "sandbox"]).default("sandbox"),
POLAR_PRODUCT_PREMIUM: z.string().optional(),
POLAR_WEBHOOK_SECRET: z.string().optional(),
}),
});
```
Then prefer `getEnv()` in Convex code instead of scattered `process.env`.
## 11. Dev Scripts and CLI Workflow
### 11.0 Plugin scaffold inspection
Use the plugin CLI as a plan engine, not a blind writer:
1. `bunx kitcn add <plugin> --dry-run` → compact full-plan summary.
2. `bunx kitcn add <plugin> --diff [path]` → unified diffs for planned file changes.
3. `bunx kitcn add <plugin> --view [path]` → rendered planned file contents.
4. `bunx kitcn view <plugin>` → inspect resolved preset, selection source, docs link, files, operations.
5. `bunx kitcn info --json` → project paths, versions, installed plugins, schema/lockfile mismatch, missing deps, scaffold drift.
6. `bunx kitcn docs <topic...>` → local/public docs links for `cli`, `plugins`, `auth`, `orm`, `migrations`, `resend`, `ratelimit`.
Rules:
1. `kitcn add <plugin>` bootstraps baseline files first when they are missing.
2. `--diff [path]` / `--view [path]` match workspace-relative output path by exact match first, substring fallback.
3. Preview scope includes scaffold files, env bootstrap, `kitcn.json`, schema registration, lockfile write, dependency install status, codegen/hooks, env reminders.
### 11.1 Dev bootstrap functions (example parity mode)
If you want the same operational model as the scaffolded baseline, use these canonical snippets.
**Create:** `convex/functions/init.ts`
```ts
import { z } from "zod";
import { createUser } from "../lib/auth/auth-helpers";
import { privateMutation } from "../lib/crpc";
import { getEnv } from "../lib/get-env";
import { createSeedHandler } from "./generated/seed.runtime";
export default privateMutation
.meta({ dev: true })
.mutation(async ({ ctx }) => {
const env = getEnv();
const adminEmails = env.ADMIN;
if (!adminEmails || adminEmails.length === 0) {
return null;
}
let isFirstInit = true;
for (const adminEmail of adminEmails) {
const existingUser = await ctx.orm.query.user.findFirst({
where: { email: adminEmail },
});
if (existingUser) {
isFirstInit = false;
continue;
}
await createUser(ctx, {
email: adminEmail,
name: "Admin",
role: "admin",
});
}
if (isFirstInit && getEnv().DEPLOY_ENV === "development") {
const handler = createSeedHandler(ctx);
await handler.seed({});
}
return null;
});
```
**Create:** `convex/functions/reset.ts`
```ts
/** biome-ignore-all lint/suspicious/noExplicitAny: dev */
import { eq } from "kitcn/orm";
import { CRPCError } from "kitcn/server";
import { z } from "zod";
import { privateAction, privateMutation } from "../lib/crpc";
import { getEnv } from "../lib/get-env";
import type { TableNames } from "./_generated/dataModel";
import { createResetCaller } from "./generated/reset.runtime";
import schema, { tables } from "./schema";
const DELETE_BATCH_SIZE = 64;
const excludedTables = new Set<TableNames>();
const assertDevOnly = () => {
if (getEnv().DEPLOY_ENV === "production") {
throw new CRPCError({
code: "FORBIDDEN",
message: "This function is only available in development",
});
}
};
export const reset = privateAction.action(async ({ ctx }) => {
assertDevOnly();
const caller = createResetCaller(ctx);
for (const tableName of Object.keys(schema.tables)) {
if (excludedTables.has(tableName as TableNames)) {
continue;
}
await caller.schedule.now.deletePage({
cursor: null,
tableName,
});
}
return null;
});
export const deletePage = privateMutation
.input(
z.object({
cursor: z.union([z.string(), z.null()]),
tableName: z.string(),
}),
)
.mutation(async ({ ctx, input }) => {
assertDevOnly();
const caller = createResetCaller(ctx);
const table = (tables as Record<string, any>)[input.tableName];
if (!table) {
throw new CRPCError({
code: "BAD_REQUEST",
message: `Unknown table: ${input.tableName}`,
});
}
const query = (ctx.orm.query as Record<string, any>)[input.tableName];
if (!query || typeof query.findMany !== "function") {
throw new CRPCError({
code: "BAD_REQUEST",
message: `Unknown query table: ${input.tableName}`,
});
}
const results = await query.findMany({
cursor: input.cursor,
limit: DELETE_BATCH_SIZE,
});
for (const row of results.page) {
try {
await ctx.orm.delete(table).where(eq(table.id, (row as any).id));
} catch {
// Can already be deleted by trigger or concurrent process.
}
}
if (!results.isDone) {
await caller.schedule.now.deletePage({
cursor: results.continueCursor,
tableName: input.tableName,
});
}
return null;
});
```
`convex/functions/seed.ts` stays project-specific, but should expose a `privateMutation` entrypoint used by `init.ts`.
Recommended scripts:
```json
{
"scripts": {
"convex:dev": "kitcn dev",
"verify": "kitcn verify",
"reset": "kitcn reset --yes --after init",
"seed": "convex run seed:seed",
"inspect": "kitcn run --inline-query 'await ctx.db.query(\"messages\").take(5)'",
"env:push": "kitcn env push",
"env:push:rotate": "kitcn env push --rotate"
}
}
```
Set `dev.preRun = "init"` in `kitcn.json` when the
app owns an `init.ts` preflight.
CLI commands:
Convex lane:
```bash
bunx kitcn dev
# deterministic one-shot local runtime proof:
# stop any long-running local backend first
bunx kitcn verify
# optional fallback only if dev cannot run and backend is already active:
bunx kitcn codegen
bunx kitcn env push
bunx kitcn env push --prod
bunx kitcn env push --rotate
bunx kitcn env default list --type dev
bunx kitcn env default set SITE_URL https://app.example.com --type prod
bunx kitcn auth jwks
bunx kitcn auth jwks --rotate
# ad-hoc readonly database inspection:
bunx kitcn run --inline-query 'await ctx.db.query("messages").take(5)'
# create/select a branch-scoped cloud dev deployment:
bunx kitcn deployment create "$(git branch --show-current)" --type dev --select
# deploy with automatic aggregate backfill:
bunx kitcn deploy --prod
bunx kitcn deploy --prod --message "Release $(git rev-parse --short HEAD)"
bunx kitcn deploy --preview-name "$(git branch --show-current)"
# aggregate index management:
bunx kitcn aggregate rebuild --prod
bunx kitcn aggregate backfill --prod
# bundle analysis:
bunx kitcn analyze
```
Concave lane:
```bash
bunx kitcn --backend concave auth jwks --url http://localhost:3210
bunx kitcn --backend concave auth jwks --rotate --url http://localhost:3210
```
On backend `convex`, `kitcn dev` watches `convex/.env` during a local
dev session and auto-pushes later edits. Keep `env push` for `--prod`,
`--rotate`, or explicit repair against an already active deployment.
Use `env default` when new dev, preview, or production deployments should start
with the same project-level values.
Unknown Convex commands pass through `kitcn`, so use `kitcn run --inline-query`
for quick readonly inspection and `kitcn deployment create ... --select` for
branch-scoped cloud dev deployments. `kitcn deploy` forwards Convex deployment
flags such as `--message` and `--preview-name`, then runs kitcn migrations and
aggregate backfill against the selected deployment.
Convex `ctx.meta` APIs belong inside app functions; do not invent a kitcn
wrapper for them.
On backend `concave`, `kitcn env` is not the right command. Use `auth jwks`
for manual static `JWKS` export instead.
### 11.2 Phase A gate: non-auth baseline (required before auth work)
Run these after base setup (Sections 3-5) and before starting Section 6:
```bash
# stop any long-running local backend first
bunx kitcn verify
bunx convex run internal.seed.seed
bunx convex run internal.init.default
# run project checks after bootstrap smoke:
bun run typecheck || bunx tsc --noEmit
bun test
bun run build
```
Then sanity-check runtime paths (non-auth only):
1. Run one public query endpoint.
2. Run one public mutation endpoint.
3. Run one public HTTP route endpoint.
4. Do not proceed to Section 6 until this gate is green.
### 11.3 Phase B gate: auth sign-in working (required before optional modules/plugins)
Run this after Section 6 and before Sections 7-10:
```bash
# stop any long-running local backend first
bunx kitcn verify
bun run typecheck || bunx tsc --noEmit
bun test
bun run build
```
Then sanity-check auth runtime paths:
1. Sign in successfully from `/auth` in headed browser.
2. Run one protected query/mutation in signed-in context and confirm success.
3. Run one protected endpoint in signed-out context and confirm `UNAUTHORIZED`.
4. Do not proceed to optional modules/plugins until this gate is green.
## 12. Final From-Scratch Execution Checklist
1. `convex.json` configured with `functions: convex/functions` and static codegen enabled.
2. `tsconfig.json` has `strictFunctionTypes: false` and `@convex/*` alias.
3. `.env.local` has `NEXT_PUBLIC_CONVEX_URL` and `NEXT_PUBLIC_CONVEX_SITE_URL`.
4. `schema.ts` + `relations` + generated `initCRPC` wiring are in place.
5. `crpc.ts` builders exported and app procedures use `ctx.orm`.
6. `kitcn dev` runs and generates `_generated` + `api.ts`.
7. If auth enabled: `auth.config.ts`, `auth.ts`, `http.ts`, and env bootstrap
are complete.
8. Client `CRPCProvider` + QueryClient + Convex provider are mounted.
9. Framework branch is complete (Next.js or TanStack Start).
10. If using typed envs: `convex/lib/get-env.ts` exists and Convex code reads through `getEnv()`.
11. Optional modules/plugins added only if selected.
12. If multiple components enabled: `convex/functions/convex.config.ts` composes all `app.use(...)` calls in one file.
13. If organizations + invite mail enabled: `email.tsx` + invite template + resend component are wired.
14. If dev bootstrap mode enabled: `init.ts`, `seed.ts`, `reset.ts` exist and scripts call them.
15. If `Aggregates: No`, no aggregate helper/import/config references remain.
16. Phase A gate (Section 11.2) passes before any auth implementation.
17. If auth enabled: Phase B auth sign-in gate (Section 11.3) passes before optional modules/plugins.
18. No legacy Ents patterns in setup code.
19. NEVER use `@ts-nocheck` in app/convex source files.
## 13. Troubleshooting
See the [Troubleshooting Reference](#troubleshooting-reference) at the bottom of this document for the full symptom/cause/fix matrix.
## Coverage Matrix
Source coverage mapping used to build this runbook:
| Source | Mapped In Setup |
| ------------------------------------------------- | -------------------- |
| `www/content/docs/cli/registry.mdx` | Sections 3, 11 |
| `www/content/docs/quickstart.mdx` | Sections 3, 4, 11, 12 |
| `www/content/docs/server/setup.mdx` | Section 5.3 |
| `www/content/docs/auth/server.mdx` | Sections 6.1 - 6.10 |
| `www/content/docs/auth/client.mdx` | Section 7.1 |
| `www/content/docs/auth/server.mdx#triggers` | Section 6.3, 9.2 |
| `www/content/docs/react/index.mdx` | Sections 7.2 - 7.4 |
| `www/content/docs/nextjs/index.mdx` | Section 8.A |
| `www/content/docs/tanstack-start.mdx` | Section 8.B |
| `www/content/docs/server/http.mdx` | Sections 6.6, 9.6 |
| `www/content/docs/server/server-side-calls.mdx` | Section 8.A.1, 8.B.3 |
| `www/content/docs/plugins/ratelimit.mdx` | Section 9.4 |
| `www/content/docs/orm/queries/aggregates.mdx` | Section 9.3 |
| `www/content/docs/server/scheduling.mdx` | Section 9.5 |
| `www/content/docs/orm/queries/index.mdx` | Sections 5, 12 |
| `www/content/docs/orm/mutations/index.mdx` | Sections 5, 12 |
| `www/content/docs/orm/triggers.mdx` | Sections 9.2, 9.3 |
| `www/content/docs/orm/rls.mdx` | Section 9.1 |
| `www/content/docs/auth/plugins/admin.mdx` | Section 10.1 |
| `www/content/docs/auth/plugins/organizations.mdx` | Section 10.2 |
| `www/content/docs/auth/plugins/polar.mdx` | Section 10.3 |
| `www/content/docs/cli/backend.mdx` | Section 11 |
### Template Coverage (Recreation Target)
This runbook + references map to the canonical template shape as follows:
| Example Group | Primary Setup Section | Additional Reference |
| --------------------------------------------------------------------------------------------------------- | ------------------------------- | ---------------------------------------- |
| Core infra (`schema.ts`, `functions/generated/`, `crpc.ts`, `http.ts`) | Sections 5, 6.6, 9.6 | `orm.md`, `http.md` |
| Shared contracts (`shared/api.ts`, `shared/auth-shared.ts`, `shared/polar-shared.ts`) | Sections 5.4, 6.3.2, 10.2, 10.3 | `auth-organizations.md` |
| Auth core (`auth.config.ts`, `auth.ts`) | Section 6 | `auth.md` |
| Auth plugins (`admin.ts`, `organization.ts`, `polar*`) | Section 10 | `auth-admin.md`, `auth-organizations.md` |
| Feature modules (`user.ts`, `projects.ts`, `tags.ts`, `todoComments.ts`, `public.ts`, `items/queries.ts`) | Sections 5, 6.3.1, 9 | core `SKILL.md`, `orm.md` |
| HTTP routers (`routers/health.ts`, `routers/todos.ts`, `routers/examples.ts`) | Section 9.6 | `http.md` |
| Aggregates + rate limits (`aggregates.ts`, `lib/plugins/ratelimit/plugin.ts`) | Sections 9.3, 9.4 | `aggregates.md`, `orm.md` |
| Scheduling + internals (`todoInternal.ts`, delayed jobs) | Sections 9.5, 11.1 | `scheduling.md` |
| Email + Resend (`functions/plugins/email.tsx`, `lib/plugins/resend/*`) | Section 9.7 | `auth-organizations.md` |
| Dev bootstrap (`init.ts`, `seed.ts`, `reset.ts`) | Section 11.1 | `testing.md` (for verification) |
| Generated outputs (`functions/_generated/*`, `functions/generated/`, `shared/api.ts`) | Section 5.5 | n/a (generated by CLI) |
## Troubleshooting Reference
| Symptom | Likely Cause | Fix |
| ------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `@convex/api` not found | `kitcn dev` not run | Run `bunx kitcn dev` and regenerate API metadata |
| `Cannot prompt for input in non-interactive terminals` during bootstrap | Convex deployment targeting is ambiguous | Use `bunx kitcn verify` for local proof, or pass `--env-file` / deployment-target args so Convex can resolve the deployment without prompting |
| Can't find new local backend files under `~/.convex` | Convex now stores new local deployment state per project | Check `.convex/local/default/` in the current project root; treat `~/.convex/**` as legacy storage |
| `kitcn env push` fails to set auth vars | Target deployment is not active or not initialized | For local proof, run `bunx kitcn verify`. For remote targets, resolve deployment targeting, then rerun `bunx kitcn env push` |
| `Failed to analyze auth.js` with `Unexpected token` / `map is not a function` on JWKS | Static `JWKS` value is malformed JSON | Unset/fix `JWKS`; use `getAuthConfigProvider()` fallback or repush with `bunx kitcn env push` |
| `Local backend isn't running` during manual `kitcn codegen` | Convex local deployment not active | Prefer `bunx kitcn dev` (it already codegens); use manual `codegen` only as fallback with active backend |
| HTTP calls fail but queries work | `.site` URL missing or wrong | Set `NEXT_PUBLIC_CONVEX_SITE_URL` correctly |
| Auth works locally but fails in prod | JWKS not pushed | Run `bunx kitcn env push --prod` |
| Sign-in fails on `/auth` (loop, no session, or immediate sign-out) | Auth route/env/provider wiring mismatch | Recheck Sections 6.5-6.7 (`authMiddleware`, route registration, env push), verify provider credentials/URLs, then rerun Section 11.3 |
| `UNAUTHORIZED` on protected procedures | auth middleware not attaching `userId` | Ensure `getAuth(ctx)` + `getHeaders(ctx)` session lookup is in middleware |
| `ctx.orm` missing in handlers | Generated `initCRPC` not used | Use `initCRPC` from `../functions/generated/server` — ORM context is pre-wired |
| `Property 'insert'/'update' does not exist on type 'OrmReader'` | Using query context for mutations | Ensure mutation handlers use `publicMutation` / `protectedMutation` builders |
| `useCRPC must be used within CRPCProvider` | Provider chain not mounted around route tree | Wrap app with `AppConvexProvider` and verify `CRPCProvider` is inside QueryClientProvider (Section 7.4 / 8.A.4) |
| Route auth cookies not set | Missing CORS auth headers | Add `Better-Auth-Cookie` allow/expose headers + credentials |
| TanStack Start auth helper import errors | Using `kitcn/auth/nextjs` in Start app | Use the TanStack Start helper from `kitcn/auth/start` |
| `Returned promise will never resolve` from internal function | Trigger path is recursively querying/updating related rows or stale component wiring still runs | Isolate failing write with logs, disable/move trigger-side sync into explicit mutation helper, rerun `bunx kitcn verify`, then retry the runtime proof |
| Better Auth secret mismatch/warnings in setup flows | `BETTER_AUTH_SECRET` manually set inconsistently or low entropy | Let `bunx kitcn dev` manage local bootstrap, or regenerate/push via `bunx kitcn env push` for active non-local targets |
| `Invalid orderBy value. Use a column or asc()/desc()` | Wrong `orderBy` shape (`[{ field, direction }]`) | Use object form only, e.g. `orderBy: { updatedAt: "desc" }` |
| `Invalid argument id for db.get` while testing `NOT_FOUND` | Fabricated Convex document ID | Use real inserted IDs or non-ID lookup keys (slug/name/email) for not-found tests |
| Trigger side effects too slow | Heavy sync work inside trigger | Move heavy work to scheduled actions via `ctx.scheduler` |
| Rate limiter throws missing-table/setup guidance | local ratelimit scaffold missing or not registered | Run `bunx kitcn add ratelimit`, then ensure `convex/functions/schema.ts` imports `../lib/plugins/ratelimit/schema` |
| fallback `kitcn codegen` fails after disabling aggregates | Aggregate helper/import references still exist | Remove `app.use(aggregate...)`, `defineTriggers` aggregate handlers, and aggregate helper modules in the same change; prefer rerunning `kitcn verify` |
| Aggregate counts drift | trigger not registered in `defineTriggers` | Register `aggregate.trigger` in `defineTriggers` `change:` handler |
| Invite emails never send | Resend schema scaffold missing or not registered | Run `bunx kitcn add resend`, ensure `convex/functions/schema.ts` imports `../lib/plugins/resend/schema`, and wire `internal.plugins.email.sendTemplatedEmail` |
| Dev reset/seed commands do nothing | `init.ts`/`seed.ts`/`reset.ts` missing or not wired | Add dev bootstrap functions and scripts from Section 11.1 |