# Configuration

> How config is split between vite.config.ts, src/middleware.ts, and wrangler.jsonc

Package: Koze
Canonical: https://kuratchi.dev/docs/koze/configuration
Markdown: https://kuratchi.dev/docs/koze/configuration.md

Koze has **no project-level config file**. The framework is convention-driven for everything it can be (route discovery, DOs, workflows, queues, pipelines, containers, sandboxes, agents), and where configuration genuinely is needed, it splits cleanly across three honest surfaces:

| File | Owns | Edited by you |
| --- | --- | --- |
| `vite.config.ts` | Build-time options for the Koze plugin (security headers, directory overrides) | Yes |
| `wrangler.jsonc` | Cloudflare deployment (secrets, routes, custom bindings) | Yes, except auto-synced fields (DOs, workflows, queues, pipelines, containers, sandboxes, assets) |
| `src/middleware.ts` | Request-time concerns (auth, ORM auto-migration, custom steps) | Yes |

If you used `kuratchi.config.ts` in a previous version, see [Migrating from `kuratchi.config.ts`](#migrating-from-kuratchi-config-ts) below.

## `vite.config.ts`

Where the Koze plugin lives. Pass options here for things that need to be baked into the build:

```ts
import { defineConfig } from 'vite';
import { cloudflare } from '@cloudflare/vite-plugin';
import { koze } from '@kuratchi/koze/vite';

export default defineConfig({
  plugins: [
    koze({
      // Optional. All defaults are sensible.
      // routesDir: 'src/routes',
      // serverDir: 'src/server',
      // libDir: 'src/lib',

      security: {
        contentSecurityPolicy:
          "default-src 'self'; script-src 'self' 'nonce-{NONCE}'",
        strictTransportSecurity:
          'max-age=31536000; includeSubDomains',
        permissionsPolicy:
          'camera=(), microphone=(), geolocation=()',
      },
    }),
    cloudflare({ viteEnvironment: { name: 'ssr' } }),
  ],
});
```

Available options:

| Option | Default | Purpose |
| --- | --- | --- |
| `routesDir` | `'src/routes'` | Where `.koze` route files live |
| `serverDir` | `'src/server'` | Where DOs / workflows / containers / queues / pipelines / sandboxes / agents are auto-discovered |
| `libDir` | `'src/lib'` | Resolution root for `$lib/*` imports |
| `security.contentSecurityPolicy` | `null` | CSP header value. Use `{NONCE}` to opt into per-request nonce stamping on injected `<script>` tags. |
| `security.strictTransportSecurity` | `null` | HSTS header value |
| `security.permissionsPolicy` | `null` | Permissions-Policy header value |
| `apiShield` | `true` | Generate `_cloudflare/api-shield/openapi.json` from route-adjacent `*.api-shield.ts` files and legacy API route manifests. Pass `{ title, version, servers, include, outputPath }` to customize, or `false` to disable. |

## Server convention suffixes

Files under `serverDir` use singular `<name>.<kind>.ts` suffixes. Plural forms such as `*.agents.ts`, `*.workflows.ts`, or `*.queues.ts` fail dev/build with a fix-it error.

| Suffix | Purpose | Wrangler handling |
| --- | --- | --- |
| `*.do.ts` | Durable Object class | Auto-synced |
| `*.workflow.ts` | Cloudflare Workflow class | Auto-synced |
| `*.queue.ts` | Queue consumer | Auto-synced consumer dispatch |
| `*.pipeline.ts` | Cloudflare Pipelines binding | Auto-synced |
| `*.container.ts` | Cloudflare Container class | Auto-synced |
| `*.sandbox.ts` | Cloudflare Sandbox class | Auto-synced |
| `*.agent.ts` | Agents SDK class | Re-exported; add the Durable Object binding/migration in Wrangler |

## `src/middleware.ts`

Where everything request-time composes. Each export is a `MiddlewareStep` with `request`, `route`, `response`, and `error` hooks.

```ts
import { defineMiddleware } from 'koze:middleware';
import { autoMigrate } from '@kuratchi/kunii';
import { kyzenAuthMiddleware } from '@kuratchi/kyzen/middleware';
import { adminSchema } from './server/schemas/admin';
import { authConfig } from './server/auth-config';

export default defineMiddleware({
  // Apply pending D1 migrations on cold start.
  // Idempotent — runs once per worker isolate.
  migrate: autoMigrate({ DB: adminSchema }),

  // Sessions, guards, OAuth, rate-limit, turnstile.
  auth: kyzenAuthMiddleware(authConfig),

  // Your own steps:
  logging: {
    async request(ctx, next) {
      const start = Date.now();
      const res = await next();
      console.log(`${ctx.request.method} ${ctx.url.pathname} ${Date.now() - start}ms`);
      return res;
    },
  },
});
```

Both `autoMigrate` and `kyzenAuthMiddleware` are ordinary middleware steps — there are no special framework slots. The same shape lets you swap in any third-party auth / ORM / observability tool. See [Middleware](/docs/koze/middleware) for the full lifecycle.

## `wrangler.jsonc`

Cloudflare deployment config. You own everything except the fields the Koze compiler auto-syncs at build time:

| Auto-synced | Don't edit |
| --- | --- |
| `durable_objects.bindings` | Yes |
| `migrations[].new_sqlite_classes` | Yes |
| `workflows[]` | Yes |
| `containers[]` | Yes |
| `pipelines[]` | Yes |
| `queues.consumers` | Yes |
| `assets.directory`, `assets.binding` | Yes |
| Everything else (routes, secrets, env, KV, R2, D1, custom bindings) | No |

Drop binding names into your code (`env.DB`, `env.MY_KV`) and add the matching wrangler entry. Secrets go through `wrangler secret put` or `.dev.vars`.

## CSS and global stylesheets

There is no CSS option in any config — CSS is a convention. Drop a file at `src/app.css` and Koze registers it as a Vite client entry, runs every CSS plugin (Tailwind, PostCSS, Lightning CSS), and injects `<link rel="stylesheet">` into the app shell before `</head>`.

```css
/* src/app.css */
@import 'tailwindcss';
@plugin 'daisyui';
@source './routes/**/*.koze';
```

For `kuzan`, import its theme stylesheet from `src/app.css`:

```css
@import '@kuratchi/kuzan/styles/theme.css';
```

See [Styling](/docs/koze/styling) for Tailwind, DaisyUI, and the static-assets path.

## `kuzan` integration

`kuzan` is a regular package — there is no framework-level wiring. Three steps:

1. Install: `npm install @kuratchi/kuzan`.
2. Import the theme stylesheet from `src/app.css` (above).
3. Add `<ThemeInit />` to your layout's `<head>` to prevent flash-of-wrong-theme on hydration:

```html
<!-- src/routes/layout.koze -->
<script>
  import ThemeInit from '@kuratchi/kuzan/theme-init.koze';
</script>

<!doctype html>
<html lang="en" class="dark">
  <head>
    <ThemeInit />
  </head>
  <body>
    <slot></slot>
  </body>
</html>
```

Then import components freely:

```html
<script>
  import Card from '@kuratchi/kuzan/card.koze';
  import Badge from '@kuratchi/kuzan/badge.koze';
</script>

<Card>
  <Badge variant="success">Active</Badge>
  <p>Card content</p>
</Card>
```

## Migrating from `kuratchi.config.ts`

Earlier versions of Koze had a project-level `kuratchi.config.ts`. It was deleted. Each block has a clean home in the new model:

| Old config block | New home |
| --- | --- |
| `orm.databases` | `autoMigrate({ DB: schema })` step in `src/middleware.ts` |
| `auth.*` | `kyzenAuthMiddleware({...})` step in `src/middleware.ts` |
| `ui.*` | `import 'kuzan/styles/theme.css'` in `src/app.css` + `<ThemeInit />` in your layout |
| `css.*` | Standard PostCSS / Tailwind / Vite CSS plugins in `src/app.css` |
| `security.*` | `koze({ security: {...} })` Vite plugin option |
| `durableObjects` | Auto-discovered from `*.do.ts` files. Filename derives the binding name (`auth.do.ts` → `AUTH_DO`) or override via `static binding = '...'` on the class. |
| `containers` / `workflows` / `queues` / `pipelines` / `sandboxes` | Auto-discovered from singular `*.container.ts` / `*.workflow.ts` / `*.queue.ts` / `*.pipeline.ts` / `*.sandbox.ts` files |
| `agents` | Place classes in singular `*.agent.ts` files; Koze re-exports them and you keep the matching Durable Object binding/migration in `wrangler.jsonc` |

Migration steps for an existing project:

1. Delete `kuratchi.config.ts`.
2. Move the `auth:` block into `src/middleware.ts` via `kyzenAuthMiddleware(authConfig)`.
3. Add `migrate: autoMigrate({ DB: yourSchema })` to `src/middleware.ts`.
4. If you used `ui.theme`, add `@import '@kuratchi/kuzan/styles/theme.css'` to `src/app.css` and `<ThemeInit />` to your layout's `<head>`.
5. If you had `security:` headers, move them to `koze({ security: {...} })` in `vite.config.ts`.
6. Remove the `kuratchi.config.ts` line from `tsconfig.json`'s `include` array.

That's the whole migration. The framework is a smaller mental model now — three honest config surfaces instead of one config file pretending to own everything.
