Skip to main content
The control plane keeps its operational state in one SQLite file: Better Auth users and sessions, API-key hashes, apps and their repository assignments, agents and their jobs, codebases and worktrees, builds and their logs, AI runs, workflows, telemetry, the GitHub and Jira caches, credential metadata, and — when the database credential backend is active — credential payloads and their connection settings. Vault and Keychain keep those payloads outside SQLite. There is no separate application cache, queue, or object store to operate. Data access uses Prisma 7 with the prisma-client generator — a TypeScript query compiler, with no native query-engine binary — and the better-sqlite3 driver adapter. That combination is what lets the generated client bundle cleanly into the Next.js standalone output the Homebrew service runs.

Location

The database defaults to a SQLite file at prisma/dev.db. Set DATABASE_URL to another file: URL to change its location; other database URL schemes are rejected at startup with an explicit error rather than being coerced. Each install type picks its own default: Relative file: URLs resolve against the process working directory, so prefer an absolute path anywhere the app might be started from more than one place:
SQLite is the only supported engine. src/data/prisma-client.ts rejects every non-file: scheme before it ever constructs an adapter, so pointing DATABASE_URL at Postgres fails fast instead of half-working.

Connection settings

The application applies its SQLite settings on the first database operation rather than at module load, so next build never creates or mutates the configured runtime database. WAL mode means the database is three files, not one: production.db, production.db-wal, and production.db-shm. That matters for backups and for anything that copies the file.

Schema

prisma/schema.prisma defines roughly 150 models. They fall into a few families: There are no Prisma enums — SQLite has no native enum type — so status and kind columns are strings constrained in application code.

Migrations

Migrations are versioned in prisma/migrations/, each in a timestamped directory such as 20260723020322_add_plans_sessions_drafts/, and applied with prisma migrate deploy. The Homebrew service and the ai-development-environment npm command both apply pending migrations on start, so upgrading an install is just restarting it. From source, use npm run db:migrate to create and apply a development migration and npm run db:deploy to apply committed ones.
1

Change the schema

Edit prisma/schema.prisma.
2

Create the migration

Prisma writes the SQL into a new timestamped directory and applies it to your development database.
3

Regenerate the client

npm run db:migrate already regenerates the Prisma client, but running the full generator also refreshes the bundled SDL and resolver types if you changed the GraphQL schema alongside it.
4

Commit the migration directory

The generated SQL is part of the change. Migrations that only exist on your machine will not apply on anyone else’s.
Several migrations have dedicated tests beside the schema — prisma/credential-migration.test.ts, prisma/workflow-migration.test.ts, and others — that assert existing rows survive the transformation. Add one when a migration rewrites data rather than just adding columns. npm run db:studio opens Prisma Studio against the configured database for ad-hoc inspection.

Backups

Copying production.db alone can capture a torn database, because recent commits may still live in the WAL. Either stop the application and copy all three files, or use SQLite’s own online backup:
The database file holds user identities, active sessions, API-key hashes, device UDIDs, IP history, integration metadata, and operational history. With the database credential backend, it also holds every credential-backed setting, including private keys and certificate bundles. Treat backups as sensitive as the original.
When the database backend holds credential rows, a backup is only usable alongside the APP_SECRET those rows were encrypted under. Store it separately from the backups, but do not lose it — a restore paired with a different APP_SECRET needs the original supplied as APP_SECRET_PREVIOUS to be readable. See credential storage and Environment variables.

Growth and cleanup

Nothing is pruned automatically. The tables that grow fastest are the ones fed by streaming or polling: Each of those has a clearing action in the dashboard: GitHub cache, Jira cache, Build data, and Clear audit on the Tools page. Deleting rows shrinks the working set but not the file — see below.

Reclaiming space

Deleted SQLite pages are reused automatically but do not reduce the database file’s size. To return unused pages to the filesystem:
1

Stop everything touching the database

Stop the application and any database clients. npm run db:vacuum checkpoints the WAL first and refuses to continue if SQLite reports the database is busy.
2

Check free space

The volume needs enough temporary free space to rebuild the database — plan for roughly the current file size again.
3

Vacuum

The command uses DATABASE_URL when set, and otherwise vacuums prisma/dev.db. It prints the before and after sizes in megabytes.
Vacuuming rewrites the whole database. On a multi-gigabyte file that takes a while and holds an exclusive lock throughout, so run it during a maintenance window rather than while agents are working.

Local development

The migration and generation commands in context.

Build data

Measuring and deleting the largest build-related data.

Quickstart

Credential storage backends and the encryption key.

Hosting and networking

Where the server runs and what it exposes.