Database

database is one target named default. targets replaces it.

database is one target named default. targets replaces it. Set one of them. The name does not set protection. NODE_ENV is not read.

Both

Set database or targets, not both.

FieldResult
databaseOne target. Its name is default. A string is the URL, and protected is false. An object is { url, protected }.
targetsNamed targets, in sorted order. Replaces database. A string leaves protected false.
urlThe connection string for that target.
protectedOptional. true blocks contract work. Omitted or false leaves the target open.
NameResult
Any keyThat string is the target name. Dev is not dev.
databaseThe name is default. Pass --target default.
A key named devokm dev uses that target.
No key named devokm dev opens PGlite at <out>/dev-db. The default out is .okm.
targets: {}, or neither fieldNo target is configured.
URLResult
Missing or ""OKM1845. Target <name> has no url. The environment is not read.
Does not parseOKM1845. The target URL is not a Postgres URL. A URL that parses is accepted. The fix asks for postgres://.
HostLowercased.
Port omitted5432.
PathThe database name, decoded. It is not lowercased.
primary, replicas, weight, or pool on the objectOKM1845. Target <name> is a replica. Migrations run on the primary only.
Host contains pooler or pgbouncer, port 6543 or 6432, or query pgbouncer=trueRefused when a command connects. Refusing pooler host <host>. Apply needs a direct connection.
allowPooler: true or --allow-poolerThat host check is skipped.

okm migrate apply, okm push when it connects, provision(), okm seed, okm migrate check, and okm check when it compares a database run the host check. okm migrate plan and okm migrate status do not. Naming the URL does not refuse it.

okm migrate apply, okm push when it connects, provision(), and that same okm check comparison also read pg_backend_pid() twice. A change is OKM1854. The same opt-in skips the probe.

CaseResult
One target--target is optional.
Several targets and no --target, on okm migrate apply, okm push, okm seed, okm migrate check, okm ext, or okm doctor when roles is setOKM1853. Several targets are configured and --target was not passed.
--target with no nameOKM1853. --target needs a name.
A name that is not configuredOKM1845. Target <name> is not configured.
No target, on those commands or on okm migrate statusOKM1845. No target is configured.
okm migrate statusPrints every target, in name order. The columns are target, version, catalog, state, and protected (true or false). --target keeps one name. Several targets do not need --target.
okm check, several targets, no --targetSkips the database comparison. An unknown --target is still OKM1845. A protection mismatch is still OKM1852.
okm check or okm migrate plan, no targetDoes not throw OKM1845. Plan prints without a database.
okm migrate plan, several targets, no --targetPrints the plan. It does not throw OKM1853. A protection mismatch is still OKM1852.
okmodel.config.ts
import { defineConfig } from "okmodel/migrate";

export default defineConfig({
  schema: "./schema.ts",
  database: process.env.DATABASE_URL!, 
});
okmodel.config.ts
import { defineConfig } from "okmodel/migrate";

export default defineConfig({
  schema: "./schema.ts",
  targets: {
    dev: process.env.DEV_URL!,
    prod: { url: process.env.PROD_URL!, protected: true }, 
  },
});

OKM1845

No target is configured. Target <name> is not configured. Target <name> has no url. The target URL is not a Postgres URL. Target <name> is a replica. Migrations run on the primary only.

OKM1853

Several targets are configured and --target was not passed. --target needs a name.

OKM1854

The backend pid changed from <first> to <second>. A pooler is handing out a new session between statements.

Protection

--allow-protected is this invocation only. It does not change the config. connect() does not read it.

WorkProtected target
plan, status, check, drift, verify, pull, catalog-export, inspectAllowed.
expand, reference, provisionAllowed. A target that is not empty is still OKM1851.
contract, unclassified, push, backfill, seed, history-repairOKM1850 until --allow-protected.
okm migrate checkOKM1850. --allow-protected does not lift it. Point it at a throwaway Postgres.
drop, rollbackRefused on every target. The code is invalid. --allow-protected does not lift it.

verify, pull, catalog-export, and inspect have no command. okm migrate plan is plan. okm migrate status is status. okm check is check and drift. okm doctor, when roles is set, and okm ext are check. okm push is push. okm seed is seed. provision() is provision.

On okm migrate apply, the unit is backfill when it contains a backfill step. Otherwise the last contract or unclassified step sets the class. With neither, the unit is expand. A history repair rewrites a stored catalog hash. A protected target needs --allow-protected when a row changes.

okm migrate apply --target prod --allow-protected

OKM1850

<operation> is blocked on protected target <name>. okm migrate check says okm migrate check writes a scratch schema and refuses protected target <name>. Point it at a throwaway Postgres.

No down migration

drop and rollback throw There is no down migration. Recovery is a new forward migration.

The same database

The match is the host, the port, and the database name. The user, the password, and the query string are left out. The host is lowercased, and an omitted port is 5432, so those spellings match. The database name is not lowercased. okm check and okm migrate plan report a mismatch.

PairResult
One protected, one notOKM1852. <open> and <protected> resolve to the same database and disagree about protection.
Both unprotectedAllowed. They may share a database.
Both protectedAllowed.

OKM1852

<open> and <protected> resolve to the same database and disagree about protection.

An empty database

okm migrate apply installs or replays when a head snapshot exists. With no migration files, or no head snapshot, it prints nothing to apply and does not connect. The check looks at the current schema. okm_meta, okm_history, and okm_backfill do not count as objects. A table, partitioned table, view, materialized view, sequence, foreign table, enum, domain, or extension does.

Stateokm migrate applyprovision()
EmptyInstalls the head snapshot and the reference rows. The history id is provisioned@ plus the migration id.The same install.
History presentReplays the pending files, then inserts reference rows that are missing.OKM1851. Target <name> already has migration history and cannot be provisioned.
Objects present, no historyOKM1851. Target <name> is not empty (<object>) and has no migration history.OKM1851. The same message.
A provision that stopped part-wayOKM1851. Target <name> is not empty (<object>): a previous provision stopped part-way; drop the schema or database and run againOKM1851. The same message.

OKM1851

Target <name> is not empty (<object>) and has no migration history. Target <name> already has migration history and cannot be provisioned. Target <name> is not empty (<object>): a previous provision stopped part-way; drop the schema or database and run again