---
title: Project structure
description: Tour the files in the current create-kovo scaffold and see where to extend them.
order: 5
---

# Project structure

A scaffolded Kovo project is a small authenticated app. Here is the shape `create-kovo` writes:

```txt
my-app/
|-- .env                    # generated local CSRF/auth secret, gitignored
|-- .env.example            # deployment secret template
|-- .github/workflows/ci.yml
|-- .kovo/                  # generated endpoint posture + agent docs mirror
|-- .gitignore
|-- AGENTS.md               # generated Kovo rules for coding agents
|-- CLAUDE.md               # points agent tools at AGENTS.md
|-- README.md
|-- kovo.config.ts          # production build preset
|-- package.json
|-- vite.config.ts          # Kovo project config; the pinned implementation runner stays internal
|-- src/
|   |-- _kovo/
|   |   |-- app-runtime-db-options.ts # validated schema and boot/CLI database options
|   |   `-- app-runtime-db.ts         # framework-owned database and auth boundary
|   |-- app.tsx             # routes, layout, and the single app.assemble() call
|   |-- app.test.ts         # focused app smoke test
|   |-- auth.ts             # typed session, guard, CSRF, and sanitized auth bindings
|   |-- db.ts               # app-facing read-only Drizzle value
|   |-- endpoint-posture.test.ts
|   |-- kovo.ts             # the single defineKovo() provider/config contract
|   |-- mutations.ts        # guarded add-contact mutation
|   |-- queries.ts          # typed contact query
|   |-- schema.ts           # app tables plus Better Auth tables
|   |-- styles.css          # document CSS
|   |-- theme.ts            # typed theme tokens
|   `-- components/
|       |-- auth-forms.tsx  # sign-in/sign-out forms
|       `-- contacts.tsx    # query-backed contact region and add form
`-- tsconfig.json           # TypeScript config written by the scaffold
```

The SQLite dialect swaps in `package.sqlite.json`, `src/db.sqlite.ts`, `src/schema.sqlite.ts`,
`src/auth.sqlite.ts`, and `README.sqlite.md` at scaffold time. The public file names stay the same
inside your generated app.

## The app entry

`src/kovo.ts` captures providers and app-wide config once:

```tsx
import { createMemoryVersionedClientModuleRegistry } from '@kovojs/server/client-modules';
import { defineKovo } from '@kovojs/server';

declare const appCsrf: any;
declare const appRuntimeDbProvider: () => unknown;
declare const appSessionProvider: (request: Request) => unknown;

export const app = defineKovo({
  appId: '61cc6f90-8870-4dcf-977f-2df98af8cd93',
  auth: appSessionProvider,
  clientModules: createMemoryVersionedClientModuleRegistry(),
  csrf: appCsrf,
  db: appRuntimeDbProvider,
});
```

`src/app.tsx` declares routes and closes the exact handle inventory:

```tsx
import { app } from './kovo.js';

declare const addContact: any;
declare const AppLayout: any;
declare const contactsQuery: any;
declare const healthEndpoint: any;
declare const homeRoute: any;
declare const loginRoute: any;
declare const appSignIn: any;
declare const appSignOut: any;

const signInMutation = app.integrateMutation(appSignIn);
const signOutMutation = app.integrateMutation(appSignOut);

export default app.assemble({
  endpoints: [healthEndpoint],
  layouts: [AppLayout],
  mutations: [addContact, signInMutation, signOutMutation],
  queries: [contactsQuery],
  routes: [homeRoute, loginRoute],
});
```

`create-kovo` generates this UUIDv4 once. Keep it stable across replicas and deployments of the same
app; generate a different UUIDv4 for every distinct app. Production apps with live-target renderers
must declare it. Kovo binds live-target reconstruction tokens to this identity so one app cannot
replay a descriptor into another app with the same render plan, including when the apps run in
separate processes or isolates. Keep the CSRF or `KOVO_LIVE_TARGET_SECRET` signing secret unique to
the app as well.

The home route redirects unauthenticated requests to `/login`; the login route renders the auth
form. The full scaffold passes `appSessionProvider` to `defineKovo()`, registers the health endpoint,
and uses the same `app.layout()` and stylesheet declaration on both pages.

## Auth and secrets

`src/auth.ts` wires Better Auth through `@kovojs/better-auth`:

- `betterAuthCsrfFromEnvironment({ field: 'csrf' })` returns an opaque CSRF configuration. The
  signing secret and session-binding callback stay package-owned.
- `createAppAuthBindings()` returns only a sanitized session provider plus sign-in and sign-out
  mutations. The raw Better Auth instance and writable system database never enter app source.
- CSRF tokens bind to a framework-owned anonymous id before login and to the session id after login.
- Boot-only `seedDemoUser()` creates the `demo@example.com` credential with the random
  `KOVO_DEMO_PASSWORD`. It creates no session; sign-in still requires the CSRF-protected mutation.

`create-kovo` writes a fresh `KOVO_CSRF_SECRET` and local-only `KOVO_DEMO_PASSWORD` into `.env` and
refuses to let the app run with the CSRF placeholder. In production, set `BETTER_AUTH_SECRET` or
`KOVO_CSRF_SECRET` through the platform's secret store, set `BETTER_AUTH_URL` to the canonical HTTPS
origin, and leave `KOVO_DEMO_PASSWORD` unset.

## Data, queries, and mutations

`src/schema.ts` declares the app table and the Better Auth tables. The contact table is annotated so
the compiler can connect writes to query refreshes. The generated `_kovo` runtime creates the
database and performs fixed boot-only seeding; `src/db.ts` exposes its read-only app value.
`src/queries.ts` owns the contact read; `src/mutations.ts` owns the guarded add-contact write.

When adding product data, keep the same separation:

1. Add the table and domain metadata in `schema.ts`.
2. Seed or connect storage in `db.ts`.
3. Add typed reads in `queries.ts`.
4. Add guarded writes in `mutations.ts`.
5. Render the data from `components/`.

That path keeps the compiler's query/write extraction readable and gives tests one clear place to
assert each behavior.

## Agent helper files

The scaffold writes `AGENTS.md`, a `CLAUDE.md` pointer, and `.kovo/docs/` so coding agents can read
the same framework rules as the repo maintainers. `.kovo/endpoint-posture.json` is generated by the
endpoint-posture test and checked by `npm run check:endpoint-posture` after the production build.

## The graph workflow

The starter does not write or commit a root `graph.json`. The app facts live in the authored
modules above; derive or construct the graph in tests when a product rule needs CI coverage:

```sh
pnpm --filter @kovojs/example-commerce run build:demo
pnpm --filter @kovojs/example-crm test -- src/interactive-app.test.ts
```

`kovo explain` output is stable and diffable by design. When a product rule matters, assert it in a
test or graph script and run it in CI. The [kovo check & kovo explain guide](/guides/kovo-explain/)
walks through the recipes.

## Styling

StyleX is the default component styling path. Author typed `@kovojs/style` objects in TSX, use
plain document CSS for fonts, page chrome, and theme tokens, and declare stylesheet hints for every
page, mutation fragment, and deferred stream so late HTML arrives styled.

## Deployment shape

A Kovo app deploys as a stateless server: mutation responses are ordinary HTML over the wire and
the server keeps no record of what's on screen. `kovo build ./src/app.tsx` emits `dist/server`, and
`npm start` runs `dist/server/server.mjs` for the Node preset.

The [deployment guide](/guides/deployment/) covers the two real obligations: keeping versioned
`/c/*` client modules published across deploys, and not breaking the stateless-server guarantee.

## Next

- [Quickstart](/getting-started/quickstart/) - make the first schema/query/mutation change.
- [Troubleshooting & upgrading](/getting-started/troubleshooting/) - fix the common onboarding
  snags and keep a starter current.

<details>
<summary>Spec & diagnostics</summary>

Starter auth/session shape and delegated session providers: SPEC §6.6. Graph and explain audit
surfaces: SPEC §11.4. Styling contract and stylesheet hints: SPEC §13.1. Deploy-time client module
retention and stateless server rules: SPEC §9.5.

</details>
