UNPKG

kitcn

Version:

kitcn - React Query integration and CLI tools for Convex

813 lines (653 loc) 38.2 kB
# 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 |