# 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                                                                                                                                   |
