Configuration & environment
Use this page when you are wiring a new app, moving from local PGlite to deployed Postgres, or trying to answer "which env var does this surface actually read?" The smallest path is to validate your own required env at boot and let the framework refuse early.
Validate your env at boot#
Start with the app-owned vars you actually need:
import { defineKovo, s } from '@kovojs/server';
const app = defineKovo({
env: s.object({
STRIPE_SECRET_KEY: s.string().pattern(/.+/),
SENTRY_DSN: s.string().url().optional(),
}),
});
export default app.assemble({});That check happens at boot, not on the first live request. In production, invalid env blocks app assembly. In development, the framework warns instead of bricking localhost.
Run it#
You can see the same failure mode with a missing framework secret by starting the app in production without a real CSRF secret:
NODE_ENV=production pnpm devThe boot path reports a typed assembly error instead of failing later from inside a route or mutation.
Understand the production shape#
Boot mode comes from NODE_ENV unless an adapter overrides it. The practical differences are:
- Production refuses weak or missing framework secrets.
- Development warns about advisory issues instead of throwing.
- The default outbound egress floor is stricter in production.
The secret floor applies to the framework-owned signing material you wire through defineKovo, not
every random string in your process env.
Use the app-facing variables#
These are the env vars you will usually touch in app code, deploy config, or CI.
Every Postgres URL must explicitly name its login, database, and decimal port. Non-local runtime,
admin, and system URLs must also use a DNS hostname and exact sslmode=verify-full. Kovo validates
this before opening a connection and does not fill destination or identity fields from ambient
PG* variables. Only exact 127.0.0.1, an exact query-host ::1, and validated Unix-socket targets
may omit TLS for local development. Write the IPv6 control as
postgres://app@localhost:5432/db?host=%3A%3A1; Kovo rejects bracketed authority [::1] because
pinned pg passes those brackets to DNS. A process booted with NODE_TLS_REJECT_UNAUTHORIZED=0
cannot use a non-local managed Postgres URL.
| Variable | Used by | What it does |
|---|---|---|
KOVO_CSRF_SECRET |
defineKovo({ csrf }), starter auth |
Framework signing secret for browser mutation CSRF when you wire it through app config. |
KOVO_DATABASE_URL |
runtime DB, egress bootstrap | Ordinary least-privilege app login. |
KOVO_RUNTIME_DATABASE_URL |
kovo db provision / kovo db migrate / kovo db check |
Runtime witness used for grants and posture. Usually the same login as KOVO_DATABASE_URL. |
KOVO_ADMIN_DATABASE_URL |
kovo db generate / kovo db migrate / kovo db provision / fallback check |
Privileged setup and fallback-check authority. Keep it out of the app process. |
KOVO_DB_SYSTEM_URL |
app boot, replay/auth storage, boot posture, kovo db check |
Dedicated system login and preferred check authority on the same writable primary. |
KOVO_DB_DRIVER |
Postgres/PGlite runtime and kovo db |
Chooses pglite, pg, or node-postgres. |
KOVO_DATA_DIR |
PGlite runtime and starter template | Overrides the local PGlite directory. |
KOVO_DB_READER_ROLE |
Postgres provision/check | Reader role name. Defaults to kovo_reader. |
KOVO_DB_WRITER_ROLE |
Postgres provision/check, durable tasks | Writer role name. Defaults to kovo_writer. |
KOVO_DB_ADMIN_ROLE |
audited crossOwnerRead(...) posture |
Admin role for the narrower cross-owner read path. |
KOVO_DB_SYSTEM_ROLE |
framework replay/auth storage | System role name. Defaults to kovo_system. |
KOVO_PRESET |
kovo build |
Forces node, vercel, or cloudflare instead of host autodetection. |
KOVO_PARANOID |
build/dev diagnostics | Turns on advisory extra security auditing. |
KOVO_SQL_GUARD |
raw SQL migration escape hatch | Temporary fail-open escape for unmanaged raw SQL sinks. |
KOVO_DEVTOOL_BASE |
devtool mount | Prefixes emitted devtool URLs when you serve it under a subpath. |
Two more names matter even though they are not KOVO_*:
NODE_ENVcontrols boot posture.BETTER_AUTH_SECRETis the common auth-layer secret the starter also accepts in place ofKOVO_CSRF_SECRET.
Handle failure#
There are three common failure classes:
- Your app env schema rejects a missing or malformed app-owned variable.
- Production boot rejects a weak framework signing secret.
- The runtime and admin/system URLs resolve to different databases, clusters, or primary instances.
When that happens, fix the source env or the app wiring. Do not paper over it by catching the boot error and continuing anyway.
Next#
- Database lifecycle — wire the DB URLs and roles into real migrations.
- Deployment — turn the same config into a production artifact.
Spec & diagnostics
Boot validation and CreateAppBootError: packages/server/src/env.ts and packages/server/src/app.ts.
DB/runtime env resolution: packages/server/src/postgres-runtime.ts, packages/cli/src/commands/db.ts,
and packages/create-kovo/src/index.ts. Build preset override and paranoid mode:
packages/cli/src/commands/build-export.ts and packages/server/src/vite.ts. CSP report endpoint:
packages/server/src/csp.ts. The main boot refusal path surfaces through CreateAppBootError; the
database posture family is the same KV433 set surfaced by kovo db check.
API reference: @kovojs/server.