Complete reference for `oke.config.ts`. Options below match `defineConfig`. Driver maps use three keys: **dev** · **test** · **prod**.

```typescript title="oke.config.ts"
import { defineConfig } from "okengine/config";

export default defineConfig({
  // options below
});
```

<Callout title="The one rule">
  Pin drivers and images in `oke.config.ts` for `dev` · `test` · `prod`. We test on what we deploy:
  Docker-first local stack, PGLite for tests, same protocol ids in production.
</Callout>

## Environments

| Key    | When                       | Typical backends                                      |
| ------ | -------------------------- | ----------------------------------------------------- |
| `dev`  | `oke dev` (Docker Compose) | Same protocols as prod (Postgres, Redis, S3, SMTP, …) |
| `test` | `oke test` / `bun test`    | PGLite for SQL; memory / frozen / console elsewhere   |
| `prod` | Production deploy          | Shared durable backends                               |

Missing `dev` pins fill from `prod` (`fillDevFromProd`). Only `dev` / `test` / `prod` keys are
valid — rename `local` → `dev` and `docker` → `prod` if you still have the old maps.

## Quick start

<Steps>

<Step>
### Pin drivers for three envs

Pin **only the env keys that differ** from the built-in defaults — omitted keys
keep `DRIVER_DEFAULTS`. create-oke templates ship that sparse shape:

```typescript title="oke.config.ts"
import { defineConfig } from "okengine/config";

export default defineConfig({
  drivers: {
    // Default vault.dev is "env"; pin the built-in store for Docker-first apps.
    vault: { dev: "vault" },
  },
});
```

</Step>

<Step>
### Or use a string shorthand

A bare string (or `{ driver, … }` object) expands to all three envs:

```typescript
drivers: {
  signal: "redis", // → { dev: "redis", test: "redis", prod: "redis" }
},
```

Prefer an explicit `{ dev, test, prod }` map (or a partial override) when test
should differ from the default for that driver.

</Step>

<Step>
### Run and test

```bash title="Terminal"
oke dev    # Docker Compose + host Bun · drivers.dev
oke test   # bun test · NODE_ENV=test · OKE_PGLITE_URL=memory://
```

</Step>

</Steps>

## Default driver maps

Built-in defaults (`DRIVER_DEFAULTS`) when a key is omitted:

| Map             | `dev`      | `test`    | `prod`     |
| --------------- | ---------- | --------- | ---------- |
| `store.sql`     | `postgres` | `pglite`  | `postgres` |
| `store.kv`      | `redis`    | `memory`  | `redis`    |
| `store.files`   | `s3`       | `memory`  | `s3`       |
| `signal`        | `redis`    | `memory`  | `redis`    |
| `clock`         | `postgres` | `frozen`  | `postgres` |
| `journal`       | `postgres` | `memory`  | `postgres` |
| `vault`         | `env`      | `memory`  | `vault`    |
| `channel.email` | `smtp`     | `console` | `smtp`     |
| `channel.sms`   | — (opt-in) | —         | —          |
| `runs`          | `files`    | `memory`  | `files`    |

`store.index`, `channel.whatsapp` / `push`, and `ai` have no single three-env default table — set them explicitly when you need them. create-oke templates pin `vault.dev: "vault"` (built-in) because the default `dev` driver is `env`.

## Driver keys and ids

| Key                | Shape          | Driver ids                                                                       |
| ------------------ | -------------- | -------------------------------------------------------------------------------- |
| `store.sql`        | env driver map | `postgres` · `pglite` · `memory`                                                 |
| `store.kv`         | env driver map | `memory` · `redis`                                                               |
| `store.files`      | env driver map | `memory` · `fs` · `s3`                                                           |
| `store.index`      | env driver map | `memory` · `pgvector` · `meilisearch`                                            |
| `signal`           | env driver map | `memory` · `redis` (boot); `postgres` · `nats` fail loud until clients bind      |
| `clock`            | env driver map | `memory` · `postgres` · `file` · `frozen`                                        |
| `journal`          | env driver map | `memory` · `file` · `postgres`                                                   |
| `vault`            | env driver map | `env` · `vault` · `memory` · `managed`                                           |
| `channel.email`    | env driver map | `console` · `smtp` · `resend` · `sndr` · `taqnyat-mail`                          |
| `channel.sms`      | env driver map | `console` · `taqnyat` · `msegat` · `unifonic`                                    |
| `channel.whatsapp` | env driver map | `console` · `wa-cloud`                                                           |
| `channel.push`     | env driver map | `console` · `webpush` · `fcm`                                                    |
| `ai`               | env driver map | `mock` · `anthropic` · `openai-compatible` · `bedrock` · `vertex`                |
| `runs`             | env driver map | `memory` · `files` (Parquet + DuckDB at `.oke/runs`) · `postgres` · `clickhouse` |
| `prod`             | `string[]`     | flat protocol list for the Manifest — nested maps are preferred                  |

### Safety rules

`defineConfig` rejects unsafe pins:

- **`sqlite` is removed** — use `postgres` (dev/prod) or `pglite` (test).
- **`drivers.store.sql.test` must be `pglite`** when set — real Postgres semantics in tests.

### Rich driver objects

```typescript
sql: {
  prod: { driver: "postgres", pool: { max: 20 }, replicas: ["postgres://ro-1/db"] },
},
```

| Field      | Type              | Meaning                                     |
| ---------- | ----------------- | ------------------------------------------- |
| `url`      | connection string | Overrides the env-var resolution            |
| `pool`     | `{ max?, min? }`  | SQL pool sizing                             |
| `replicas` | `string[]`        | Read-only routing targets (read flows only) |

## images

Nested pins by element role — vendor choice lives here, never in driver ids:

```typescript
images: {
  store: {
    sql: "postgres:18-alpine",
    kv: "redis:8-alpine",
    files: "rustfs/rustfs:1.0.0-rc.5",
  },
  channel: { email: "axllent/mailpit:v1.31.1" },
  pgdog: "ghcr.io/pgdogdev/pgdog:v0.1.57",
  // proxy: "caddy:2-alpine",
},
```

Omitted image keys mean no container for that role. When both `store.sql` and `pgdog` are pinned, `DATABASE_URL` points at PgDog — see [Store](/docs/elements/store#multiple-environments).

For `store.kv`, pin Redis (default), Valkey, or Dragonfly — driver id stays `redis`.
`{ durable: true }` KV lives in SQL (`oke_kv` on `DATABASE_URL`), not a second Redis image.

Compose does not manage AI inference — [OpenRouter](/docs/recipes/openrouter) or BYO
`OKE_AI_URL` ([Models](/docs/elements/ai/models)). For `proxy`, see [Caddy](/docs/recipes/caddy),
[Traefik](/docs/recipes/traefik), or [nginx](/docs/recipes/nginx).

## i18n

| Option    | Type       | Default               | Meaning                                     |
| --------- | ---------- | --------------------- | ------------------------------------------- |
| `locales` | `string[]` | `["en"]` when omitted | Supported locales                           |
| `default` | string     | `"en"` when omitted   | Fallback locale (channel templates, `fx.t`) |
| `dir`     | record     | —                     | Per-locale direction: `"ltr"` \| `"rtl"`    |

```typescript
i18n: { locales: ["en"], default: "en" },
// or with Arabic: { locales: ["en", "ar"], default: "en", dir: { ar: "rtl" } }
```

## tenancy

`oke.config.ts` `tenancy` is **isolation posture** (how rows are separated). Identity —
who the tenant is — is `gate.auth.tenant` on [Gate](/docs/elements/gate#tenants-identity-dimension).

| Option      | Type                              | Meaning                                           |
| ----------- | --------------------------------- | ------------------------------------------------- |
| `isolation` | `"row" \| "schema" \| "database"` | How tenants are separated in the store            |
| `resolve`   | string \| function                | Observational resolver (isolation-only manifests) |

## privacy

Presence of this block turns CORE privacy tooling on in the Console — not a `.plug()` call.

```typescript
privacy: {},
```

## runs

Runs retention and redaction — **not** the same key as `drivers.runs`.

| Option   | Type                     | Meaning                                                                    |
| -------- | ------------------------ | -------------------------------------------------------------------------- |
| `keep`   | `string` \| `"forever"`  | Delete Parquet partitions older than this (`7d` in `dev`, `30d` in `prod`) |
| `redact` | `Record<string, string>` | Field → retention duration; presence turns privacy on                      |

## db

Domain schema sync for `oke db push | generate | migrate` (Drizzle). Unrelated to `oke schema generate`.

| Option      | Default                      | Meaning                                                                   |
| ----------- | ---------------------------- | ------------------------------------------------------------------------- |
| `autoPush`  | `true`                       | Auto-run `db push` on schema change under `oke dev`; forced off in `prod` |
| `config`    | `"drizzle.config.ts"`        | Path to the drizzle-kit config                                            |
| `declare`   | `"src/db/schema.decl.ts"`    | Abstract schema module (`store.schema.table` exports)                     |
| `generated` | `"src/db/schema.drizzle.ts"` | Where `oke db` emits dialect Drizzle                                      |
| `entry`     | `src/app.ts`                 | App entry for collecting plugin table contributions                       |

## topology

| Value        | Meaning                                          |
| ------------ | ------------------------------------------------ |
| `"monolith"` | One process serves everything (default posture)  |
| `"services"` | Split deployment units derived from the Manifest |

## ports

| Option    | Default | Surface |
| --------- | ------- | ------- |
| `app`     | `6530`  | Backend |
| `console` | `6533`  | Console |
| `mcp`     | `6535`  | MCP     |

## console

| Option         | Type                                 | Default | Meaning                   |
| -------------- | ------------------------------------ | ------- | ------------------------- |
| `prod.enabled` | boolean                              | —       | Serve the Console in prod |
| `prod.auth`    | `"required" \| "optional" \| "none"` | —       | Access requirement        |

## Troubleshooting

<Accordions>

<Accordion title='oke.config: … uses removed driver "sqlite"'>
  The `sqlite` driver is gone. Pin `postgres` for `dev`/`prod` and `pglite` for `test`. Edit the
  file by hand — rename the env keys to `dev` / `test` / `prod` first.
</Accordion>

<Accordion title='drivers.store.sql.test must be "pglite"'>
  Tests use PGLite so SQL semantics match Postgres. Set `test: "pglite"` (or omit the key and keep
  the default). Do not use `memory` or `postgres` for `store.sql.test`.
</Accordion>

<Accordion title='CLI error: "local" / "sqlite" is no longer valid'>
  There is no soft-compat. Rename env keys to `dev` / `test` / `prod` and replace `sqlite` with
  `postgres` / `pglite`. `oke mode` was removed — `oke dev` always starts Docker Compose.
</Accordion>

<Accordion title="Runs DuckDB / Parquet queries requires @duckdb/node-api">
  The `files` driver needs `@duckdb/node-api`. create-oke templates add it. Install with `bun add
  @duckdb/node-api` if an older app is missing the peer.
</Accordion>

</Accordions>

## Learn more

- [CLI](/docs/reference/cli) — `oke dev` · `oke test` · `oke db`
- [Environment Variables](/docs/reference/environment-variables) — URL and secret resolution
- [Store](/docs/elements/store) — what the store drivers back
- [i18n](/docs/reference/i18n) — locale catalogs beyond the config block

## Next

<Cards>
  <Card
    title="Environment Variables"
    description="URLs and secrets Compose writes."
    href="/docs/reference/environment-variables"
  />
  <Card title="CLI" description="Commands that honor this config." href="/docs/reference/cli" />
  <Card
    title="Security"
    description="Host, Origin, planes, and MCP ports."
    href="/docs/reference/security"
  />
</Cards>
