okengine 0.10.3 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (318) hide show
  1. package/package.json +76 -10
  2. package/site/content/docs/deployment/docker-swarm.mdx +48 -111
  3. package/site/content/docs/deployment/docker.mdx +43 -36
  4. package/site/content/docs/deployment/index.mdx +9 -9
  5. package/site/content/docs/deployment/kubernetes.mdx +4 -4
  6. package/site/content/docs/elements/ai.mdx +7 -7
  7. package/site/content/docs/elements/channel.mdx +9 -7
  8. package/site/content/docs/elements/clock.mdx +8 -8
  9. package/site/content/docs/elements/flow.mdx +46 -47
  10. package/site/content/docs/elements/gate.mdx +6 -5
  11. package/site/content/docs/elements/signal.mdx +2 -2
  12. package/site/content/docs/elements/store.mdx +71 -78
  13. package/site/content/docs/elements/vault.mdx +89 -70
  14. package/site/content/docs/get-started/basic-usage.mdx +8 -8
  15. package/site/content/docs/get-started/installation.mdx +36 -36
  16. package/site/content/docs/get-started/introduction.mdx +11 -0
  17. package/site/content/docs/get-started/why.mdx +2 -2
  18. package/site/content/docs/plugins/anonymous.mdx +1 -1
  19. package/site/content/docs/plugins/magic-link.mdx +11 -11
  20. package/site/content/docs/plugins/otp.mdx +2 -2
  21. package/site/content/docs/plugins/passkey.mdx +1 -1
  22. package/site/content/docs/plugins/two-factor.mdx +1 -1
  23. package/site/content/docs/plugins/username.mdx +1 -1
  24. package/site/content/docs/recipes/caddy.mdx +1 -1
  25. package/site/content/docs/recipes/cockroachdb.mdx +2 -2
  26. package/site/content/docs/recipes/dragonfly.mdx +1 -1
  27. package/site/content/docs/recipes/index.mdx +1 -2
  28. package/site/content/docs/recipes/llama-cpp.mdx +21 -23
  29. package/site/content/docs/recipes/mailpit.mdx +11 -11
  30. package/site/content/docs/recipes/meilisearch.mdx +10 -10
  31. package/site/content/docs/recipes/meta.json +0 -1
  32. package/site/content/docs/recipes/ollama.mdx +1 -1
  33. package/site/content/docs/recipes/pgdog.mdx +12 -9
  34. package/site/content/docs/recipes/postgres.mdx +3 -3
  35. package/site/content/docs/recipes/redis.mdx +2 -2
  36. package/site/content/docs/recipes/rustfs.mdx +10 -7
  37. package/site/content/docs/recipes/sglang.mdx +1 -1
  38. package/site/content/docs/recipes/supabase-docker.mdx +1 -1
  39. package/site/content/docs/recipes/timescale.mdx +2 -2
  40. package/site/content/docs/recipes/valkey.mdx +1 -1
  41. package/site/content/docs/recipes/vllm.mdx +1 -1
  42. package/site/content/docs/recipes/yugabytedb.mdx +2 -2
  43. package/site/content/docs/reference/cli.md +61 -38
  44. package/site/content/docs/reference/configuration.mdx +145 -70
  45. package/site/content/docs/reference/environment-variables.mdx +36 -28
  46. package/site/content/docs/reference/errors.mdx +28 -29
  47. package/site/content/docs/reference/fx.mdx +11 -4
  48. package/site/content/docs/reference/i18n.mdx +17 -6
  49. package/site/content/docs/reference/index.mdx +5 -0
  50. package/site/content/docs/reference/meta.json +1 -0
  51. package/site/content/docs/reference/migrating-environments.mdx +158 -0
  52. package/site/content/docs/reference/plugins.mdx +2 -2
  53. package/src/auth/gate-auth.test.ts +12 -10
  54. package/src/cli/ai-setup/ai-setup.test.ts +27 -15
  55. package/src/cli/ai-setup/apply.ts +111 -15
  56. package/src/cli/ai-setup/catalog.ts +3 -3
  57. package/src/cli/ai-setup/index.ts +1 -1
  58. package/src/cli/ai-setup/prompts.ts +1 -1
  59. package/src/cli/ai-setup/recommend.test.ts +1 -1
  60. package/src/cli/ask-seed.test.ts +3 -3
  61. package/src/cli/build.ts +1 -1
  62. package/src/cli/db-auto-push.test.ts +6 -3
  63. package/src/cli/db-auto-push.ts +7 -5
  64. package/src/cli/db-seed.ts +7 -8
  65. package/src/cli/db.test.ts +70 -71
  66. package/src/cli/db.ts +86 -12
  67. package/src/cli/dev-controller.test.ts +35 -0
  68. package/src/cli/dev-controller.ts +298 -0
  69. package/src/cli/dev-controls.ts +11 -65
  70. package/src/cli/dev-db-push.test.ts +47 -15
  71. package/src/cli/dev-schema-sync.test.ts +11 -11
  72. package/src/cli/dev-schema-sync.ts +10 -10
  73. package/src/cli/dev-session-lock.test.ts +106 -0
  74. package/src/cli/dev-session-lock.ts +209 -0
  75. package/src/cli/dev.test.ts +166 -121
  76. package/src/cli/dev.ts +138 -192
  77. package/src/cli/doc-staleness.test.ts +3 -6
  78. package/src/cli/docker-clean.test.ts +33 -1
  79. package/src/cli/docker-clean.ts +28 -33
  80. package/src/cli/docker-cli.test.ts +12 -11
  81. package/src/cli/docker.ts +95 -20
  82. package/src/cli/doctor.test.ts +40 -0
  83. package/src/cli/doctor.ts +55 -15
  84. package/src/cli/drizzle-env.test.ts +11 -13
  85. package/src/cli/drizzle-env.ts +18 -11
  86. package/src/cli/ensure-drizzle-config.ts +11 -14
  87. package/src/cli/hero-meta.test.ts +15 -15
  88. package/src/cli/hero-meta.ts +10 -9
  89. package/src/cli/index.ts +36 -6
  90. package/src/cli/load-config.images.test.ts +16 -12
  91. package/src/cli/load-config.source.test.ts +50 -0
  92. package/src/cli/load-config.ts +34 -28
  93. package/src/cli/meilisearch-local.ts +1 -2
  94. package/src/cli/mode.ts +7 -58
  95. package/src/cli/registry.ts +175 -19
  96. package/src/cli/resolve-dev-sql-env.test.ts +8 -16
  97. package/src/cli/resolve-dev-sql-env.ts +13 -15
  98. package/src/cli/safe-defaults.test.ts +28 -10
  99. package/src/cli/schema.ts +65 -16
  100. package/src/cli/test.ts +58 -0
  101. package/src/cli/tui/App.tsx +252 -0
  102. package/src/cli/tui/DevLive.tsx +116 -0
  103. package/src/cli/tui/components/ConfirmDialog.tsx +48 -0
  104. package/src/cli/tui/components/HeaderBar.tsx +67 -0
  105. package/src/cli/tui/components/LogStream.tsx +47 -0
  106. package/src/cli/tui/components/PanelCard.tsx +39 -0
  107. package/src/cli/tui/components/SelectRow.tsx +37 -0
  108. package/src/cli/tui/components/SlashPalette.tsx +172 -0
  109. package/src/cli/tui/components/Spinner.tsx +32 -0
  110. package/src/cli/tui/components/StatusBar.tsx +51 -0
  111. package/src/cli/tui/components/StatusDot.tsx +37 -0
  112. package/src/cli/tui/components/TabBar.tsx +49 -0
  113. package/src/cli/tui/components/Wordmark.tsx +43 -0
  114. package/src/cli/tui/format.ts +38 -0
  115. package/src/cli/tui/keys.test.ts +18 -0
  116. package/src/cli/tui/keys.ts +26 -0
  117. package/src/cli/tui/launch.test.ts +18 -0
  118. package/src/cli/tui/launch.ts +57 -0
  119. package/src/cli/tui/panels/Dashboard.tsx +201 -0
  120. package/src/cli/tui/panels/Database.tsx +198 -0
  121. package/src/cli/tui/panels/Dev.tsx +257 -0
  122. package/src/cli/tui/panels/Docker.tsx +357 -0
  123. package/src/cli/tui/panels/Navigator.test.ts +22 -0
  124. package/src/cli/tui/panels/Navigator.tsx +213 -0
  125. package/src/cli/tui/slash-catalog.test.ts +31 -0
  126. package/src/cli/tui/slash-catalog.ts +184 -0
  127. package/src/cli/tui/slash-run.ts +54 -0
  128. package/src/cli/tui/state/ports.ts +95 -0
  129. package/src/cli/tui/theme.ts +24 -0
  130. package/src/cli/vault-cmd.test.ts +402 -0
  131. package/src/cli/vault-cmd.ts +727 -69
  132. package/src/cli/vault-secure-input.test.ts +56 -0
  133. package/src/cli/vault-secure-input.ts +124 -0
  134. package/src/compiler/effects-infer.ts +7 -0
  135. package/src/compiler/fixtures/skyport/oke.config.ts +3 -3
  136. package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +1 -1
  137. package/src/compiler/fixtures/skyport.expected.json +1 -1
  138. package/src/compiler-entry.ts +7 -0
  139. package/src/config/define-config.test.ts +111 -47
  140. package/src/config/driver-defaults.test.ts +16 -23
  141. package/src/config/driver-defaults.ts +33 -63
  142. package/src/config/index.ts +169 -144
  143. package/src/config/resolve-driver.test.ts +14 -19
  144. package/src/console/server/app.ts +4 -2
  145. package/src/console/server/console.test.ts +58 -1
  146. package/src/console/server/flows.ts +22 -2
  147. package/src/console/server/index.ts +3 -0
  148. package/src/console/server/operator-db.test.ts +55 -13
  149. package/src/console/server/operator-db.ts +305 -263
  150. package/src/console/server/serve.ts +16 -7
  151. package/src/console/server/state.ts +31 -9
  152. package/src/console/server/vault.test.ts +120 -5
  153. package/src/console/server/vault.ts +208 -12
  154. package/src/console/ui/dist/assets/{index-D-9KFce5.js → index--Y0Uaz-m.js} +2 -2
  155. package/src/console/ui/dist/assets/panel-vault-BkZdJPX8.js +1 -0
  156. package/src/console/ui/dist/assets/style-C3L349iZ.css +3 -0
  157. package/src/console/ui/dist/index.html +2 -2
  158. package/src/console/ui/shell/client.ts +16 -0
  159. package/src/console/ui/shell/panels/vault/VaultPanel.tsx +54 -1
  160. package/src/console/ui/vault/VaultA11yView.tsx +33 -1
  161. package/src/console/ui/vault/backend.test.ts +75 -0
  162. package/src/console/ui/vault/backend.ts +117 -0
  163. package/src/console/ui/vault/fixture.ts +21 -2
  164. package/src/console/ui/vault/index.ts +11 -1
  165. package/src/console/ui/vault/types.ts +26 -0
  166. package/src/docker/compose.ts +164 -61
  167. package/src/docker/derive.ts +106 -12
  168. package/src/docker/docker.test.ts +156 -110
  169. package/src/docker/helpers.ts +98 -0
  170. package/src/docker/images-config.test.ts +3 -8
  171. package/src/docker/index.ts +30 -5
  172. package/src/docker/recipes/index.ts +0 -3
  173. package/src/docker/recipes/llama-cpp.ts +4 -4
  174. package/src/docker/recipes/pgdog.ts +9 -2
  175. package/src/docker/resources.test.ts +71 -0
  176. package/src/docker/resources.ts +176 -0
  177. package/src/docker/stack-id.test.ts +11 -4
  178. package/src/docker/stack-id.ts +42 -32
  179. package/src/docker/stack.integration.test.ts +4 -15
  180. package/src/docker/types.ts +35 -3
  181. package/src/drivers/ai-ollama.integration.test.ts +2 -2
  182. package/src/drivers/conformance.test.ts +11 -12
  183. package/src/drivers/drizzle-dialect.test.ts +0 -4
  184. package/src/drivers/drizzle-dialect.ts +7 -13
  185. package/src/drivers/index.ts +24 -7
  186. package/src/drivers/journal-postgres.test.ts +3 -1
  187. package/src/drivers/journal-postgres.ts +4 -2
  188. package/src/drivers/meilisearch.ts +1 -1
  189. package/src/drivers/pglite.ts +28 -5
  190. package/src/drivers/types.ts +9 -11
  191. package/src/drivers/vault-aws-secrets-manager.test.ts +159 -0
  192. package/src/drivers/vault-aws-secrets-manager.ts +333 -0
  193. package/src/drivers/vault-builtin.test.ts +110 -0
  194. package/src/drivers/vault-builtin.ts +184 -0
  195. package/src/drivers/vault-driver-removal.test.ts +13 -0
  196. package/src/drivers/vault-managed.test.ts +95 -0
  197. package/src/drivers/vault-managed.ts +101 -25
  198. package/src/drivers/vault-types.ts +25 -5
  199. package/src/elements/ai/runtime.ts +1 -1
  200. package/src/elements/clock/chaos.test.ts +4 -1
  201. package/src/elements/clock/durable.ts +57 -4
  202. package/src/elements/clock.test.ts +13 -14
  203. package/src/elements/gate/constants.ts +18 -0
  204. package/src/elements/gate.test.ts +1 -1
  205. package/src/elements/gate.ts +2 -2
  206. package/src/elements/store/domain-ddl.test.ts +5 -12
  207. package/src/elements/store/emit-drizzle.ts +44 -19
  208. package/src/elements/store/index-boot.test.ts +15 -116
  209. package/src/elements/store/load-plugin-tables.ts +1 -1
  210. package/src/elements/store/resource-list-docs.fixture.ts +12 -6
  211. package/src/elements/store/resource-list-docs.test.ts +64 -44
  212. package/src/elements/store/resource.test.ts +17 -10
  213. package/src/elements/store/runtime.ts +9 -12
  214. package/src/elements/store/schema-decl.test.ts +16 -32
  215. package/src/elements/store/seed.test.ts +1 -2
  216. package/src/elements/store/seed.ts +7 -8
  217. package/src/elements/store/sql-condition.test.ts +2 -2
  218. package/src/elements/store/sql-session.test.ts +8 -10
  219. package/src/elements/store/sql-session.ts +10 -2
  220. package/src/elements/store.test.ts +13 -13
  221. package/src/elements/vault/audit.ts +233 -0
  222. package/src/elements/vault/boot-chain.test.ts +78 -0
  223. package/src/elements/vault/boot-chain.ts +43 -39
  224. package/src/elements/vault/builtin-adapter.test.ts +479 -0
  225. package/src/elements/vault/builtin-adapter.ts +1184 -0
  226. package/src/elements/vault/crypto.test.ts +237 -0
  227. package/src/elements/vault/crypto.ts +431 -0
  228. package/src/elements/vault/declare.ts +164 -3
  229. package/src/elements/vault/errors.ts +56 -0
  230. package/src/elements/vault/kms-unseal.test.ts +76 -0
  231. package/src/elements/vault/kms-unseal.ts +181 -0
  232. package/src/elements/vault/path.ts +90 -0
  233. package/src/elements/vault/resilience.test.ts +102 -0
  234. package/src/elements/vault/resilience.ts +107 -0
  235. package/src/elements/vault/runtime.ts +36 -4
  236. package/src/elements/vault/security-checklist.test.ts +130 -0
  237. package/src/elements/vault/storage.ts +448 -0
  238. package/src/elements/vault/types.ts +288 -0
  239. package/src/elements/vault/unseal.ts +150 -0
  240. package/src/elements/vault.test.ts +72 -4
  241. package/src/elements/vault.ts +91 -2
  242. package/src/full.ts +248 -0
  243. package/src/http.ts +41 -0
  244. package/src/i18n/interpolate-simple.ts +37 -0
  245. package/src/i18n/messages.ts +51 -4
  246. package/src/i18n-entry.ts +7 -0
  247. package/src/index.ts +35 -145
  248. package/src/journal-entry.ts +20 -0
  249. package/src/kernel/adopt-barrel-fresh.test.ts +25 -10
  250. package/src/kernel/app.ts +40 -42
  251. package/src/kernel/auto-registry.test.ts +2 -2
  252. package/src/kernel/boot-bind/ai.test.ts +5 -5
  253. package/src/kernel/boot-bind/ai.ts +2 -1
  254. package/src/kernel/boot-bind/channel.test.ts +6 -6
  255. package/src/kernel/boot-bind/channel.ts +3 -10
  256. package/src/kernel/boot-bind/clock.ts +2 -2
  257. package/src/kernel/boot-bind/gate.ts +5 -9
  258. package/src/kernel/boot-bind/honor-config.test.ts +42 -42
  259. package/src/kernel/boot-bind/journal.ts +2 -2
  260. package/src/kernel/boot-bind/store.test.ts +43 -44
  261. package/src/kernel/boot-bind/store.ts +16 -41
  262. package/src/kernel/boot-bind/vault.ts +16 -1
  263. package/src/kernel/boot.test.ts +12 -4
  264. package/src/kernel/boot.ts +31 -19
  265. package/src/kernel/budget-entry.ts +1 -1
  266. package/src/kernel/compensate.test.ts +288 -1
  267. package/src/kernel/compensate.ts +126 -0
  268. package/src/kernel/concurrency.test.ts +10 -9
  269. package/src/kernel/effects-stamping.test.ts +27 -2
  270. package/src/kernel/element-registries.ts +5 -3
  271. package/src/kernel/flow.ts +4 -2
  272. package/src/kernel/fx.test.ts +119 -3
  273. package/src/kernel/fx.ts +215 -40
  274. package/src/kernel/index.ts +14 -0
  275. package/src/kernel/journal.ts +130 -7
  276. package/src/kernel/pipeline.test.ts +20 -6
  277. package/src/kernel/redacted.test.ts +35 -0
  278. package/src/kernel/redacted.ts +24 -2
  279. package/src/kernel/router/create-default.ts +16 -0
  280. package/src/kernel/router/create-edge.ts +18 -0
  281. package/src/kernel/router/create.ts +23 -0
  282. package/src/kernel/router/index.ts +23 -0
  283. package/src/kernel/router/linear.ts +88 -0
  284. package/src/kernel/router/regexp.ts +188 -0
  285. package/src/kernel/router/smart.ts +120 -0
  286. package/src/kernel/router/trie.ts +108 -0
  287. package/src/kernel/router/types.ts +56 -0
  288. package/src/kernel/router.ts +18 -532
  289. package/src/kernel-entry.ts +43 -0
  290. package/src/manifest/fixtures/skyport.excerpt.json +1 -1
  291. package/src/manifest/fixtures/skyport.manifest.json +1 -1
  292. package/src/plugins/config-source.test.ts +7 -7
  293. package/src/release/build-lib.ts +58 -0
  294. package/src/release/exports.test.ts +4 -2
  295. package/src/release/exports.ts +30 -4
  296. package/src/release/measure.ts +101 -22
  297. package/src/runs/duckdb.ts +7 -2
  298. package/src/shared/optional-peer.ts +41 -0
  299. package/src/term.test.ts +3 -44
  300. package/src/test/create-test-app.ts +12 -0
  301. package/src/test/provisions.integration.test.ts +1 -1
  302. package/src/test/reset-element-registries.ts +2 -0
  303. package/src/upgrade/codemods.test.ts +7 -20
  304. package/src/upgrade/codemods.ts +93 -60
  305. package/site/content/docs/recipes/openbao.mdx +0 -141
  306. package/src/cli/ask-dev-mode.ts +0 -40
  307. package/src/cli/dev-mode.test.ts +0 -45
  308. package/src/cli/dev-mode.ts +0 -78
  309. package/src/cli/openbao-bootstrap.test.ts +0 -150
  310. package/src/cli/openbao-bootstrap.ts +0 -321
  311. package/src/cli/openbao-restart.integration.test.ts +0 -145
  312. package/src/console/ui/dist/assets/panel-vault-Za1GyJfM.js +0 -1
  313. package/src/console/ui/dist/assets/style-C8MxEWPd.css +0 -3
  314. package/src/docker/recipes/openbao.ts +0 -47
  315. package/src/drivers/libsql.ts +0 -179
  316. package/src/drivers/sqlite.ts +0 -44
  317. package/src/drivers/vault-openbao.test.ts +0 -97
  318. package/src/drivers/vault-openbao.ts +0 -143
@@ -186,7 +186,7 @@ await fx.store(db).delete(notes).where(lt(notes.createdAt, cutoff));
186
186
 
187
187
  ### Schema — declare once, generate per dialect
188
188
 
189
- The recommended path: declare tables ORM-agnostically, then let `oke db` emit real Drizzle for the active dialect (`sqliteTable` locally, `pgTable` in docker/prod) into `src/schema.generated.ts`.
189
+ The recommended path: declare tables ORM-agnostically, then let `oke db` emit real Drizzle (`pgTable` for Postgres / PGLite) into `src/db/schema.drizzle.ts`.
190
190
 
191
191
  | Field API | Meaning |
192
192
  | -------------------------------------------- | ----------------------------------------------- |
@@ -232,28 +232,28 @@ export const relations = store.schema.relations({ links, daily }, (r) => ({
232
232
  | `oke db generate` | Write versioned SQL under `drizzle/` for review |
233
233
  | `oke db migrate` | Apply those files — human or CI, **never** at boot |
234
234
 
235
- `oke dev` (local) auto-pushes when you save a schema _input_ (`schema.decl.ts`,
235
+ `oke dev` auto-pushes when you save a schema _input_ (`schema.decl.ts`,
236
236
  hand-written `schema.ts`, `app.ts` for plugin tables, or `drizzle.config.ts`) —
237
- not when emit rewrites `schema.generated.ts`.
237
+ not when emit rewrites `schema.drizzle.ts`.
238
238
 
239
- Opt out with `--no-db-push` or `db: { autoPush: false }`. Docker/prod **never**
240
- auto-apply DDL; a missing table fails as **OKE1101** (`oke db migrate`).
239
+ Opt out with `--no-db-push` or `db: { autoPush: false }`. `prod` **never**
240
+ auto-applies DDL; a missing table fails as **OKE1101** (`oke db migrate`).
241
241
 
242
242
  #### Multiple environments
243
243
 
244
- Local can `oke db push` freely — safe to wipe and regenerate.
244
+ `dev` can `oke db push` freely — safe to wipe and regenerate.
245
245
 
246
246
  Staging/prod accumulate versioned SQL under `drizzle/` (`oke db generate`).
247
247
  `oke db migrate` applies only unrecorded files in order — behind DBs catch up.
248
248
 
249
249
  | Environment | Sync |
250
250
  | -------------- | -------------------------------------------------------------- |
251
- | Local | `oke db push` (or auto-push from `oke dev`) |
251
+ | `dev` | `oke db push` (or auto-push from `oke dev`) |
252
252
  | Staging / prod | `oke db generate` → review files → `oke db migrate` on that DB |
253
253
 
254
254
  <Callout title="Connection pooling is infrastructure, not app code" type="info">
255
255
  Bun.SQL defaults to **10** connections per process — fine per instance. Scale out and `N × pool`
256
- can exceed Postgres `max_connections`. docker/prod puts **PgDog** in front; `DATABASE_URL` → port
256
+ can exceed Postgres `max_connections`. `dev`/`prod` put **PgDog** in front; `DATABASE_URL` → port
257
257
  `6432`. No app code changes.
258
258
  </Callout>
259
259
 
@@ -272,15 +272,13 @@ Seed proves existence (bootstrap rows); it does not correct schema-adjacent data
272
272
 
273
273
  <StoreSeeding />
274
274
 
275
- | env | `essential` | `dev` | `prod` |
276
- | -------- | ----------- | ----- | ------ |
277
- | `local` | yes | yes | no |
278
- | `docker` | yes | yes | no |
279
- | `test` | yes | no | no |
280
- | `prod` | yes | no | yes |
275
+ | env | `essential` | `dev` | `prod` |
276
+ | ------ | ----------- | ----- | ------ |
277
+ | `dev` | yes | yes | no |
278
+ | `test` | yes | no | no |
279
+ | `prod` | yes | no | yes |
281
280
 
282
- `docker` is a laptop profile with prod-shaped drivers still development, so `dev` runs.
283
- Outside `test`, exactly one of `dev` / `prod` runs with `essential`.
281
+ Outside `test`, exactly one of seed-category `dev` / `prod` runs with `essential`.
284
282
 
285
283
  **Simple form** — everything inline:
286
284
 
@@ -367,14 +365,14 @@ await fx.store(db).upsert(
367
365
  is for seed-owned rows you intentionally overwrite. Schema-wide data fixes still belong in
368
366
  migrations, not seed.
369
367
 
370
- | Env | Confirm |
371
- | ----------------- | ----------------------------------------------------- |
372
- | `local` / `test` | none |
373
- | `docker` / `prod` | print DB target; type the env name (`--force` for CI) |
368
+ | Env | Confirm |
369
+ | -------------- | ----------------------------------------------------- |
370
+ | `test` | none |
371
+ | `dev` / `prod` | print DB target; type the env name (`--force` for CI) |
374
372
 
375
373
  ```bash
376
- oke db seed # resolve env like other oke db commands (.oke/mode)
377
- oke db seed --env docker # explicit override
374
+ oke db seed # resolve env like other oke db commands
375
+ oke db seed --env dev # explicit override
378
376
  oke db seed --env prod --force
379
377
  ```
380
378
 
@@ -394,27 +392,25 @@ oke db seed --env prod --force
394
392
 
395
393
  Same flow code, different backends — configured once in `oke.config.ts`:
396
394
 
397
- | Facet | Local | Docker / prod | Runs as |
398
- | ----- | -------- | ------------- | --------------------------------------- |
399
- | `sql` | `sqlite` | `postgres` | file on diskcontainer + named volume |
395
+ | Facet | `dev` / `prod` | `test` | Runs as |
396
+ | ----- | -------------- | -------- | --------------------------------------------- |
397
+ | `sql` | `postgres` | `pglite` | Compose / deploy Postgres in-process PGLite |
400
398
 
401
- Defaults: `sqlite` locally, `postgres` in docker/prod, `memory` in test. Container images come from the `images` map — change the vendor by changing the pin, never the driver id.
399
+ Defaults: `postgres` for `dev`/`prod`, `pglite` for `test`. SQLite-family drivers (`sqlite`, `libsql`) are removed. Container images come from the `images` map — change the vendor by changing the pin, never the driver id.
402
400
 
403
401
  #### Opt-in SQL drivers
404
402
 
405
- Two more `store.sql` drivers cover specific parity needs. Both are optional peers — `bun add` them yourself; neither changes the `sqlite` local default.
403
+ | Driver | Install | Wire dialect | Choose for |
404
+ | -------- | -------------------------------------------------------- | ------------ | --------------------------------------------------------- |
405
+ | `pglite` | `@electric-sql/pglite` + `@electric-sql/pglite-pgvector` | postgresql | **required** for `store.sql.test`; also usable in-process |
406
406
 
407
- | Driver | Install | Wire dialect | Choose for |
408
- | -------- | -------------------------------------------------------- | ------------ | ------------------------------------------------------ |
409
- | `libsql` | `@libsql/client` | sqlite | local vector search without Docker (`index: "libsql"`) |
410
- | `pglite` | `@electric-sql/pglite` + `@electric-sql/pglite-pgvector` | postgresql | the real Postgres dialect + pgvector, in-process |
411
-
412
- <Callout title="PGlite is not a latency play">
413
- PGlite pays a one-time ~1 s WASM init, and warm CRUD is roughly 15× slower than `bun:sqlite`.
414
- Choose it for dialect/pgvector parity with prod — never as a faster local default.
407
+ <Callout title="PGlite is the test SQL driver">
408
+ `defineConfig` requires `drivers.store.sql.test` to be `pglite` when set. Use `oke test` (sets
409
+ `OKE_PGLITE_URL=memory://`). Choose PGLite for Postgres correctness in tests not as a Compose
410
+ substitute for `oke dev`.
415
411
  </Callout>
416
412
 
417
- SQL-backed index drivers (`pgvector`, `libsql`) share this facet's connection — see [Advanced — composing facets together](#advanced--composing-facets-together).
413
+ SQL-backed index driver `pgvector` shares this facet's connection — see [Advanced — composing facets together](#advanced--composing-facets-together).
418
414
 
419
415
  ## KV
420
416
 
@@ -470,8 +466,8 @@ Optional third argument on `set` is a duration string: `(\d+)(ms|s|m|h|d)` — e
470
466
 
471
467
  <StoreKvTtl />
472
468
 
473
- **Consequence:** a TTL you rely on in prod does nothing under the local `memory` driver. Test expiry
474
- against `redis` (docker) when lifetime matters.
469
+ **Consequence:** a TTL you rely on in prod does nothing under the `test` `memory` driver. Exercise
470
+ expiry against `redis` under `oke dev` when lifetime matters.
475
471
 
476
472
  ### Examples — what follows from each choice
477
473
 
@@ -498,25 +494,25 @@ your Flows and operator tools stay readable.
498
494
  ```typescript title="oke.config.ts"
499
495
  drivers: {
500
496
  store: {
501
- kv: { local: "memory", docker: "redis", test: "memory", prod: "redis" },
497
+ kv: { dev: "redis", test: "memory", prod: "redis" },
502
498
  },
503
499
  },
504
500
  images: {
505
- "store.kv": "redis:8-alpine",
501
+ store: { kv: "redis:8-alpine" },
506
502
  // or: "valkey/valkey:8-alpine"
507
503
  // or: "docker.dragonflydb.io/dragonflydb/dragonfly"
508
504
  },
509
505
  ```
510
506
 
511
- | Facet | Local | Docker / prod | Runs as |
512
- | ----- | -------- | ------------- | ---------------------- |
513
- | `kv` | `memory` | `redis` | in-process → container |
507
+ | Facet | `dev` / `prod` | `test` | Runs as |
508
+ | ----- | -------------- | -------- | ---------------------- |
509
+ | `kv` | `redis` | `memory` | container → in-process |
514
510
 
515
511
  | Variable | When |
516
512
  | ------------------ | -------------------------------- |
517
513
  | `REDIS_URL` | Required for the `redis` driver |
518
514
  | `OKE_STORE_KV_URL` | Override if `REDIS_URL` is unset |
519
- | `OKE_KV_DRIVER` | Force a driver id (docker) |
515
+ | `OKE_KV_DRIVER` | Force a driver id |
520
516
 
521
517
  Missing Redis URL fails boot loudly: `oke boot: redis driver needs REDIS_URL`.
522
518
 
@@ -613,28 +609,28 @@ Keys are opaque strings the driver stores as-is. Prefer path-like prefixes (`pho
613
609
  ```typescript title="oke.config.ts"
614
610
  drivers: {
615
611
  store: {
616
- files: { local: "fs", docker: "s3", test: "memory", prod: "s3" },
612
+ files: { dev: "s3", test: "memory", prod: "s3" },
617
613
  },
618
614
  },
619
615
  images: {
620
- "store.files": "rustfs/rustfs:1.0.0-beta.11",
616
+ store: { files: "rustfs/rustfs:1.0.0-beta.11" },
621
617
  },
622
618
  ```
623
619
 
624
- | Facet | Local | Docker / prod | Runs as |
625
- | ------- | ----- | ------------- | --------------------------------- |
626
- | `files` | `fs` | `s3` | project folderRustFS container |
620
+ | Facet | `dev` / `prod` | `test` | Runs as |
621
+ | ------- | -------------- | -------- | ------------------------ |
622
+ | `files` | `s3` | `memory` | RustFS / S3 in-process |
627
623
 
628
624
  | Variable | When |
629
625
  | ----------------------------- | -------------------------------------- |
630
626
  | `S3_BUCKET` | Required bucket for the `s3` driver |
631
627
  | `OKE_STORE_FILES_DB` | Override bucket name |
632
628
  | `S3_ENDPOINT` · keys · region | S3-compatible endpoint and credentials |
633
- | `OKE_FILES_DRIVER` | Force a driver id (docker) |
629
+ | `OKE_FILES_DRIVER` | Force a driver id |
634
630
 
635
631
  `memory` is the test default. Local `fs` writes under a temp root when no binding `root` is set.
636
632
 
637
- **Known limit:** `fs` is single-host. Under horizontal scale each replica sees its own filesystem (silently inconsistent object views). Boot prints a one-shot warning when `drivers.store.files` is `fs`. Use `s3` for docker/prod (create-oke templates already do).
633
+ **Known limit:** `fs` is single-host. Under horizontal scale each replica sees its own filesystem (silently inconsistent object views). Boot prints a one-shot warning when `drivers.store.files` is `fs`. Use `s3` for `dev`/`prod` (create-oke templates already do).
638
634
 
639
635
  <Callout title="Operator tools can browse, not edit bytes">
640
636
  Operator tools can list keys and delete them. Direct edit is **KV + SQL only** — blob bodies are
@@ -747,11 +743,10 @@ runtime.
747
743
 
748
744
  ### Vector ANN
749
745
 
750
- | `store.index` id | Shares connection from | Real ANN via |
751
- | ---------------- | -------------------------- | ------------------------------------ |
752
- | `memory` | — (in-process, default) | Full scan cosine |
753
- | `pgvector` | `postgres` or `pglite` sql | HNSW + `cosineDistance` |
754
- | `libsql` | `libsql` sql | `libsql_vector_idx` + `vector_top_k` |
746
+ | `store.index` id | Shares connection from | Real ANN via |
747
+ | ---------------- | -------------------------- | ----------------------- |
748
+ | `memory` | — (in-process, default) | Full scan cosine |
749
+ | `pgvector` | `postgres` or `pglite` sql | HNSW + `cosineDistance` |
755
750
 
756
751
  **Consequence:** a configured SQL-backed index that cannot reach its engine — missing peer, missing
757
752
  `vector` extension, wrong sql driver — fails loudly at first use. It never silently falls back to
@@ -788,29 +783,27 @@ do: async ({ id, doc, q }, fx) => {
788
783
  ```typescript title="oke.config.ts"
789
784
  drivers: {
790
785
  store: {
791
- index: { local: "meilisearch", docker: "meilisearch", prod: "meilisearch" },
786
+ index: { dev: "meilisearch", test: "memory", prod: "meilisearch" },
792
787
  },
793
788
  },
794
789
  images: {
795
- "store.index": "getmeili/meilisearch:v1.37",
790
+ store: { index: "getmeili/meilisearch:v1.37" },
796
791
  },
797
792
  ```
798
793
 
799
- - **Local mode** needs the `meilisearch` binary on `PATH` (a documented prerequisite, like Docker for `--docker` OKE never auto-downloads binaries). `oke dev` spawns it with a generated master key persisted under `.oke/meilisearch/` (`0700`/`0600`).
800
- - **Docker / prod** uses the image recipe; the app gets `OKE_STORE_INDEX_URL` + `OKE_STORE_INDEX_KEY` from Compose — a standalone HTTP service, never the shared `sqlUrl`.
794
+ - **`oke dev` / prod** use the image recipe; the app gets `OKE_STORE_INDEX_URL` + `OKE_STORE_INDEX_KEY` from Compose a standalone HTTP service, never the shared `sqlUrl`.
801
795
  - **Fail-loud:** a configured meilisearch that is unreachable or unhealthy throws `MeilisearchUnavailableError` — never a silent memory fallback.
802
796
  - **Not for vectors:** `ai.embed` / `fx.search` stay vector-only; pointing an embed `into` a meilisearch index fails loud (embeddings don't apply).
803
- - **Alpine caveat:** the raw glibc binary fails on Alpine/musl _hosts_; the official image is musl-clean. Install the binary for your platform (e.g. Homebrew / install script), or use `--docker`.
804
797
 
805
798
  ### Per-environment Index drivers
806
799
 
807
- Unlike KV/Files, an unset `drivers.store.index` map stays **`memory` in every environment** — there is no docker fallback to `pgvector`. Opt in explicitly:
800
+ Unlike KV/Files, an unset `drivers.store.index` map stays **`memory` in every environment** — there is no automatic fallback to `pgvector`. Opt in explicitly:
808
801
 
809
802
  ```typescript title="oke.config.ts"
810
803
  drivers: {
811
804
  store: {
812
- sql: { local: "libsql", docker: "postgres", prod: "postgres" },
813
- index: { local: "libsql", docker: "pgvector", prod: "pgvector" },
805
+ sql: { dev: "postgres", test: "pglite", prod: "postgres" },
806
+ index: { dev: "pgvector", test: "memory", prod: "pgvector" },
814
807
  },
815
808
  },
816
809
  ```
@@ -831,25 +824,25 @@ target meilisearch.
831
824
 
832
825
  Facets are independent declarations. A Flow may touch several in one `do` — for example store a files key on a SQL row, or `upsert` into an index after a SQL write. There is no special sync API: `store.resource` `search:` is SQL substring match, not meilisearch.
833
826
 
834
- One real coupling: a SQL-backed index shares the sql facet's already-open connection, so configure the pair together:
827
+ One real coupling: `pgvector` shares the sql facet's already-open connection, so configure the pair together:
835
828
 
836
829
  ```typescript title="oke.config.ts"
837
830
  drivers: {
838
831
  store: {
839
- sql: { local: "libsql" },
840
- index: { local: "libsql" },
832
+ sql: { dev: "postgres", test: "pglite", prod: "postgres" },
833
+ index: { dev: "pgvector", test: "memory", prod: "pgvector" },
841
834
  },
842
835
  },
843
836
  ```
844
837
 
845
- Same idea for `pgvector` with `postgres` or `pglite`. The full per-environment map looks like this:
838
+ The full per-environment map looks like this:
846
839
 
847
840
  ```typescript title="oke.config.ts"
848
841
  drivers: {
849
842
  store: {
850
- sql: { local: "sqlite", docker: "postgres", test: "memory", prod: "postgres" },
851
- kv: { local: "memory", docker: "redis", test: "memory", prod: "redis" },
852
- files: { local: "fs", docker: "s3", test: "memory", prod: "s3" },
843
+ sql: { dev: "postgres", test: "pglite", prod: "postgres" },
844
+ kv: { dev: "redis", test: "memory", prod: "redis" },
845
+ files: { dev: "s3", test: "memory", prod: "s3" },
853
846
  },
854
847
  },
855
848
  ```
@@ -867,9 +860,9 @@ same PII masking — see the Callout under [Querying by hand](#querying-by-hand)
867
860
  ## Troubleshooting
868
861
 
869
862
  <Accordions>
870
- <Accordion title="OKE1101 — missing table in docker or prod">
863
+ <Accordion title="OKE1101 — missing table in prod">
871
864
 
872
- Schema DDL never runs automatically outside local dev. Run `oke db push` against the target database for a dev-style sync, or `oke db generate` + `oke db migrate` for reviewed, versioned migrations.
865
+ Schema DDL never runs automatically in `prod`. Under `oke dev`, `db.autoPush` (default `true`) runs `oke db push`. For production use `oke db generate` + `oke db migrate`.
873
866
 
874
867
  </Accordion>
875
868
  <Accordion title="I need a join — with: is not supported">
@@ -887,14 +880,14 @@ You are on offset paging. Switch `list` to keyset by setting `cursor` columns wi
887
880
  `count: "exact"` runs `COUNT(*)` per page. Set `count: "none"` in the `list` options to skip it, or use keyset mode where totals are rarely needed.
888
881
 
889
882
  </Accordion>
890
- <Accordion title="KV TTL never expires locally">
883
+ <Accordion title="KV TTL never expires in tests">
891
884
 
892
- The local default is `memory`, which ignores TTL on `set`. Run against `redis` (`oke dev --docker` or `drivers.store.kv.local: "redis"` with `REDIS_URL`) when you need real expiry.
885
+ The `test` default is `memory`, which ignores TTL on `set`. Under `oke dev`, KV is `redis` by default — use that when you need real expiry.
893
886
 
894
887
  </Accordion>
895
888
  <Accordion title="oke boot: redis / meilisearch / S3 URL missing">
896
889
 
897
- Driver-backed facets fail boot when their URL is absent — Redis needs `REDIS_URL`, meilisearch needs `OKE_STORE_INDEX_URL`, S3 needs `S3_BUCKET`. Docker mode expects `oke dev -d` to write these into the compose env.
890
+ Driver-backed facets fail boot when their URL is absent — Redis needs `REDIS_URL`, meilisearch needs `OKE_STORE_INDEX_URL`, S3 needs `S3_BUCKET`. `oke dev` writes these into the Compose env.
898
891
 
899
892
  </Accordion>
900
893
  <Accordion title="vector dims X !== index dims Y">
@@ -902,9 +895,9 @@ Driver-backed facets fail boot when their URL is absent — Redis needs `REDIS_U
902
895
  The vector length must match `store.index(…, { dims })` (runtime default `3` if unset). Align `dims` with your embedder, or fix the vector you pass to `upsert` / `search`.
903
896
 
904
897
  </Accordion>
905
- <Accordion title="Index stays on memory in docker">
898
+ <Accordion title="Index stays on memory under oke dev">
906
899
 
907
- Unlike KV/Files, unset `drivers.store.index` does **not** promote to `pgvector` in docker — it stays `memory`. Set the map explicitly (and pair SQL for `pgvector` / `libsql`).
900
+ Unlike KV/Files, unset `drivers.store.index` does **not** promote to `pgvector` in `dev` — it stays `memory`. Set the map explicitly (and pair SQL for `pgvector`).
908
901
 
909
902
  </Accordion>
910
903
  </Accordions>
@@ -55,7 +55,7 @@ oke vault set STRIPE_KEY sk_test_your_key_here
55
55
  <Step>
56
56
  ### Read it in a Flow
57
57
 
58
- Flows read secrets through `fx.vault` — never through `process.env`. `fx.vault` returns a
58
+ Flows read secrets through `fx.vault.get` — never through `process.env`. It resolves to a
59
59
  `Redacted<string>`: printing, logging, or serializing it yields a placeholder, never the value.
60
60
  Call `.reveal()` once at the boundary that needs the real credential:
61
61
 
@@ -66,7 +66,7 @@ export const charge = on(
66
66
  in: ChargeInput,
67
67
  out: z.object({ id: z.string() }),
68
68
  do: async (input, fx) => {
69
- const key = fx.vault(stripeKey); // Redacted — safe to pass to fx.log
69
+ const key = await fx.vault.get(stripeKey); // Redacted — safe to pass to fx.log
70
70
  const intent = await stripe(key.reveal()).create(input);
71
71
  return { id: intent.id };
72
72
  },
@@ -87,9 +87,9 @@ That's the whole loop: declare → set → read. Everything below is what OKE gu
87
87
 
88
88
  <VaultRedacted />
89
89
 
90
- Pass the wrapper freely — `fx.log`, `String()`, and `JSON.stringify` all yield
91
- `[redacted]`. Call `.reveal()` once at the provider boundary that needs the
92
- credential.
90
+ Pass the wrapper freely — `fx.log`, `String()`, `JSON.stringify`, and
91
+ `console.log` / `util.inspect` all yield `[redacted]` (or a `Redacted<…>` label).
92
+ Call `.reveal()` once at the provider boundary that needs the credential.
93
93
 
94
94
  ## Two kinds of contracts
95
95
 
@@ -100,6 +100,20 @@ credential.
100
100
 
101
101
  `vault(name, …)` is shorthand for `vault.secret(name, …)`.
102
102
 
103
+ ### Plain environment variables
104
+
105
+ `vault.env` reads `process.env` synchronously for values that need no contract — a port, a feature flag, a log level. It never joins the resolution chain, never gets a fingerprint, and never gets redacted.
106
+
107
+ | Call | Returns | On a missing value |
108
+ | -------------------------------- | --------------------- | -------------------------------------- |
109
+ | `vault.env("LOG_LEVEL")` | `string \| undefined` | `undefined` (empty counts as missing) |
110
+ | `vault.env.required("REGION")` | `string` | Throws, and boot lists it as a gap |
111
+ | `vault.env.int("PORT", 3000)` | `number` | The default; throws when none is given |
112
+ | `vault.env.bool("DEBUG", false)` | `boolean` | The default; throws when none is given |
113
+ | `vault.env.json("FLAGS")` | `T \| undefined` | `undefined`; throws on malformed JSON |
114
+
115
+ Every name passed to `vault.env.required` is registered, so boot reports missing variables in the same `VaultBootError` as missing secrets — before it opens a database connection.
116
+
103
117
  ### Contract options
104
118
 
105
119
  | Option | Type | Meaning |
@@ -110,9 +124,9 @@ credential.
110
124
  | `dev` | string | Local-only fallback when no source provides a value (never used in prod) |
111
125
  | `sensitive` | boolean | Override the default (`true` for secrets, `false` for config) |
112
126
 
113
- ### Zero-setup local fallback
127
+ ### Zero-setup Compose fallback
114
128
 
115
- `dev: vault.fromDocker("store.sql")` resolves the connection URL that `oke dev --docker` generated for that role — no manual copying of Postgres URLs:
129
+ `dev: vault.fromDocker("store.sql")` resolves the connection URL that `oke dev` generated for that role — no manual copying of Postgres URLs:
116
130
 
117
131
  ```typescript
118
132
  export const dbUrl = vault.secret("DATABASE_URL", {
@@ -140,85 +154,82 @@ VaultBootError: 1 secret(s) missing:
140
154
 
141
155
  ## Setting and rotating values
142
156
 
143
- | Command | What it does |
144
- | -------------------------- | ---------------------------------------------------------------- |
145
- | `oke vault set NAME value` | Write a value (OpenBao API in docker mode, `.env.local` locally) |
146
- | `oke vault list` | List names present in the active store |
147
- | `oke vault import <file>` | Bulk-import names from a dotenv file |
157
+ | Command | What it does |
158
+ | -------------------------------------------------- | -------------------------------------------------------------------- |
159
+ | `oke vault set NAME value` | Write a value to the env file (`.env.local` by default) |
160
+ | `oke vault list` | List names present in the active store |
161
+ | `oke vault import <file>` | Bulk-import names from a dotenv file |
162
+ | `oke vault init` | Initialize the built-in encrypted store (prints the master key once) |
163
+ | `oke vault status` / `seal` / `unseal` | Built-in seal lifecycle |
164
+ | `oke vault rotate <path>` / `rotate-master` | Re-encrypt a secret, or rewrap every DEK under a new master |
165
+ | `oke vault audit` / `audit verify` / `audit purge` | Hash-chained audit (no secret values) |
166
+ | `oke vault purge-expired [--dry-run] [--before]` | Hard-delete secret rows past `expires_at` (operator cleanup) |
167
+ | `oke vault backup` / `restore` | Encrypted backup under a separate backup KEK |
148
168
 
149
- In docker mode, `oke vault set` writes straight to the running OpenBao no git commit, no rebuild, no restart of the vault.
169
+ Flows can write too, through the same capability gate as reads: `fx.vault.set(path, value, { ttlMs?, metadata? })`, `fx.vault.rotate(path, value)`, `fx.vault.delete(path)`, plus `fx.vault.list(prefix?)` and `fx.vault.status()`. These need the encrypted-at-rest backend (`drivers.vault = "vault"`) and throw without it; `fx.vault.get` works on every app.
150
170
 
151
171
  The **Console** (`:6533`) can also set and rotate values, but it is **write-only**: it shows a salted fingerprint (`sha256:…`) per secret, never the cleartext. When you rotate a key there, the panel shows the **blast radius** — which in-flight durable runs will wake up with the new value.
152
172
 
173
+ <Callout title="Builtin master keys and expired rows">
174
+ Resolve the master key with `--key -` (stdin), `OKE_VAULT_MASTER_KEY`, or a hidden TTY prompt —
175
+ never put keys on argv in shared shells (history risk). Expired rows stay until `oke vault
176
+ purge-expired` (use `--dry-run` first, or schedule via cron).
177
+ </Callout>
178
+
153
179
  <Callout title="Secrets never reach logs">
154
- `fx.vault` returns `Redacted` — `fx.log` shows `[redacted]`. A revealed string that still reaches
155
- a log is scrubbed by the boot redactor to `[redacted:secret]`. Traces and the Console get
180
+ `fx.vault.get` returns `Redacted` — `fx.log` shows `[redacted]`. A revealed string that still
181
+ reaches a log is scrubbed by the boot redactor to `[redacted:secret]`. Traces and the Console get
156
182
  fingerprints, never cleartext.
157
183
  </Callout>
158
184
 
159
185
  ## Per-environment drivers
160
186
 
161
- Which backend holds your values depends on the mode — configured once in `oke.config.ts`.
187
+ Which backend holds your values depends on the environment — configured once in `oke.config.ts`.
162
188
  App boot and the Console share the same resolution-chain builder (`env` layers → backend):
163
189
 
164
190
  ```typescript title="oke.config.ts"
165
- vault: {
166
- local: "env", // .env.local — zero ceremony
167
- docker: "openbao", // real vault in the compose stack
168
- test: "memory", // seeded map — deterministic tests
169
- prod: "openbao", // real vault in production
191
+ drivers: {
192
+ vault: {
193
+ dev: "env", // process env / .env.local
194
+ test: "memory",
195
+ prod: "vault", // built-in encrypted store (Postgres you already run)
196
+ },
170
197
  },
171
198
  ```
172
199
 
173
- | Driver | Backend | Best for |
174
- | --------- | --------------- | ------------------------------------------ |
175
- | `env` | `.env.local` | Local loop — no infra at all |
176
- | `openbao` | OpenBao (KV v2) | Docker + prod — durable, access-controlled |
177
- | `memory` | in-process map | Tests |
178
-
179
- Other drivers (`managed`) implement the same `VaultDriver` interface — adding one is a driver exercise, not an architecture change.
180
-
181
- ## OpenBao in docker and prod
200
+ Defaults are `env` / `memory` / `vault`. create-oke templates pin the built-in store for prod.
182
201
 
183
- When you run `oke dev --docker` (or bring up a `--prod` stack), OKE runs a real OpenBao container with durable Raft storage — not an in-memory dev server.
202
+ | Driver | Backend | Best for |
203
+ | --------- | ------------------------------------------------------------------- | ---------------------------------- |
204
+ | `env` | process env | Local pins — no encryption at rest |
205
+ | `vault` | Postgres/PGlite envelope (AES-256-GCM) | Default prod — no extra container |
206
+ | `managed` | AWS Secrets Manager (`OKE_VAULT_PROVIDER`) or platform-injected env | Cloud SM / Fly / Railway / K8s |
207
+ | `memory` | in-process map | Tests |
184
208
 
185
- <Steps>
186
-
187
- <Step>
188
- ### First boot is automatic
209
+ ### Threat model (honest)
189
210
 
190
- OKE initializes OpenBao (Shamir 1-of-1), unseals it, writes a least-privilege policy that covers **only your declared secrets**, and mints an app token bound to that policy. You never run raw `bao` commands.
211
+ Built-in Vault protects a **database dump** (ciphertext + wrapped DEKs). It does **not** equal a separate process-isolated vault:
191
212
 
192
- </Step>
213
+ | Threat | Built-in `vault` | Managed AWS SM / remote vault |
214
+ | --------------------------------------------- | ---------------------------------- | ----------------------------- |
215
+ | Postgres dump / backup leak | Protected (need master/KEK) | Protected |
216
+ | App RCE / memory dump | Game over (same process) | Better if vault is isolated |
217
+ | Master key only in `.env` next to app secrets | Weak — use KMS auto-unseal in prod | Prefer non-exportable KEK |
218
+ | JS `Buffer.fill(0)` vs remote `mlock` | Hygiene only — not claimed | Stronger outside the app |
193
219
 
194
- <Step>
195
- ### Keys stay on the host
220
+ For production built-in stores, set `vault.encryption.masterKey` to a KMS source (optional peer `@aws-sdk/client-kms`) or use `managed` + AWS Secrets Manager. `oke doctor` warns when prod would take the master key from the environment.
196
221
 
197
- The unseal key and root token are written to `.oke/openbao/` (mode `0600`, gitignored) — never into `docker/.env.docker`, never into YAML. Only the least-privilege app token lands in `.env.docker` as `OKE_VAULT_TOKEN`.
222
+ ## Managed providers (official)
198
223
 
199
- </Step>
224
+ Official managed provider: **AWS Secrets Manager** — set `drivers.vault` to `"managed"` and
225
+ `OKE_VAULT_PROVIDER=aws-secrets-manager` (optional peer `@aws-sdk/client-secrets-manager`).
226
+ Omit the provider when the platform already injects secrets as env vars.
200
227
 
201
- <Step>
202
- ### Restarts just work
228
+ ## Community / plugin
203
229
 
204
- On every later start OKE detects the sealed vault and unseals it with the host key. Secrets set before a container restart are still there after it — proven by an integration test, not assumed.
205
-
206
- </Step>
207
-
208
- </Steps>
209
-
210
- | File | Holds | Used by |
211
- | ------------------------- | ------------------------- | -------------------------------- |
212
- | `.oke/openbao/unseal.key` | The single unseal share | `oke` (every start) |
213
- | `.oke/openbao/root.token` | Root token | `oke` (policy sync / token mint) |
214
- | `.oke/openbao/app.token` | Least-privilege app token | the app + Console |
215
-
216
- <Callout title="Single point of failure — read this once" type="warn">
217
- Back up `.oke/openbao/unseal.key` to a separate safe location. Losing it means losing every secret
218
- permanently, with no recovery.
219
- </Callout>
220
-
221
- This is by design: self-hosted single-node, Shamir 1-of-1, no cloud KMS. If OpenBao is initialized but the key is gone, boot fails with an explicit permanent-loss error — it never silently starts empty.
230
+ OpenBao, HashiCorp Vault, Azure Key Vault, GCP Secret Manager, Doppler, and 1Password are not
231
+ shipped in core. Implement a community adapter for the `managed` driver surface, or keep using
232
+ built-in `vault` / `env`.
222
233
 
223
234
  ## Troubleshooting
224
235
 
@@ -228,30 +239,38 @@ This is by design: self-hosted single-node, Shamir 1-of-1, no cloud KMS. If Open
228
239
  A contract has no value in any layer of the chain. Fix by providing it in exactly one place:
229
240
 
230
241
  - locally: add `NAME=value` to `.env.local` (or run `oke vault set NAME value`)
231
- - docker mode: `oke vault set NAME value` writes to OpenBao directly
232
- - CI / hosting: set a real environment variable
242
+ - built-in store: `oke vault init` then unseal, or set via Console (write-only)
243
+ - CI / hosting: set a real environment variable (or AWS SM when `managed`)
233
244
 
234
245
  </Accordion>
235
- <Accordion title="OpenBao is sealed or unreachable in docker mode">
246
+ <Accordion title="How do I rotate a key safely?">
236
247
 
237
- Re-run `oke dev --docker` the bootstrap unseals with the host key on every start. If it reports the vault is initialized but `.oke/openbao/unseal.key` is missing, restore the key from your backup; the error is explicit because starting empty would look like "no secrets" instead of data loss.
248
+ Set the new value (`oke vault set` or the Console). The Console's rotation view shows the blast radius in-flight durable runs that will resume with the new value before you commit. The `rotate: "90d"` hint on the contract drives the rotation-due signal.
238
249
 
239
250
  </Accordion>
240
- <Accordion title="Where is the root token?">
251
+ <Accordion title="How do I pass the builtin master key without shell history?">
241
252
 
242
- `.oke/openbao/root.token`, mode `0600`, gitignored. It exists only for `oke` to sync policies and mint app tokens the running app never sees it.
253
+ Prefer `oke vault unseal --key -` and pipe the key on stdin, or omit `--key` on a
254
+ TTY and type it at the hidden prompt. Avoid `--key <base64>` on shared hosts.
243
255
 
244
256
  </Accordion>
245
- <Accordion title="How do I rotate a key safely?">
257
+ <Accordion title="Expired secrets still take space in Postgres">
246
258
 
247
- Set the new value (`oke vault set` or the Console). The Console's rotation view shows the blast radius — in-flight durable runs that will resume with the new value — before you commit. The `rotate: "90d"` hint on the contract drives the rotation-due signal.
259
+ `expires_at` fails closed on read, but rows stay until you hard-delete them:
260
+
261
+ ```bash
262
+ oke vault purge-expired --dry-run
263
+ oke vault purge-expired
264
+ ```
265
+
266
+ Schedule the live command with cron when you want automatic cleanup.
248
267
 
249
268
  </Accordion>
250
269
  </Accordions>
251
270
 
252
271
  ## Learn more
253
272
 
254
- - [Flow](/docs/elements/flow) — how `fx.vault` reads secrets inside `do`
273
+ - [Flow](/docs/elements/flow) — how `fx.vault.get` reads secrets inside `do`
255
274
  - [CLI Reference](/docs/reference/cli) — `oke vault set` · `list` · `import`
256
275
 
257
276
  ## Next
@@ -39,7 +39,7 @@ digest Clock, and an AI summarize stub.
39
39
  The standard starter already exports a named health Flow:
40
40
 
41
41
  ```typescript title="flows/main"
42
- import { on, flow, http, gate } from "okengine";
42
+ import { on, flow, http, gate } from "okengine/http";
43
43
  import { z } from "zod";
44
44
 
45
45
  export const health = on(
@@ -63,17 +63,17 @@ same Manifest.
63
63
  Adopted modules become namespaces on the client:
64
64
 
65
65
  ```typescript title="app"
66
- import { oke } from "okengine";
67
- import * as main from "./flows/main";
68
- import * as notes from "./flows/notes";
69
- import { db } from "./core";
66
+ import "@/core";
70
67
 
71
- export const app = oke({ name: "notes", stores: [db] }).adopt({ main, notes });
68
+ import { oke } from "okengine/http";
69
+ import * as routes from "@/flows/generated";
70
+
71
+ export const app = oke({ name: "notes" }).adopt(routes);
72
72
  export type App = typeof app;
73
73
  ```
74
74
 
75
- The scaffolded `src/app.ts` also registers secrets, signals, and channel
76
- templates in the same `oke({ … })` call — no post-construction mutation.
75
+ `import "@/core"` loads stores, secrets, channels, and message catalogs
76
+ (via `locales/index.ts`) so they auto-register before `oke()`.
77
77
 
78
78
  ```typescript title="client"
79
79
  import { createClient } from "okengine/client";