okengine 0.18.4 → 0.19.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 (512) hide show
  1. package/AGENTS.md +18 -10
  2. package/README.md +2 -2
  3. package/package.json +51 -50
  4. package/site/content/docs/ai/index.mdx +25 -6
  5. package/site/content/docs/ai/llms-txt.mdx +1 -1
  6. package/site/content/docs/ai/meta.json +1 -1
  7. package/site/content/docs/ai/skills.mdx +4 -1
  8. package/site/content/docs/ai/try-it.mdx +58 -0
  9. package/site/content/docs/client/auth.mdx +208 -0
  10. package/site/content/docs/client/calling.mdx +451 -0
  11. package/site/content/docs/client/index.mdx +209 -0
  12. package/site/content/docs/client/live.mdx +234 -0
  13. package/site/content/docs/client/meta.json +5 -0
  14. package/site/content/docs/client/react.mdx +249 -0
  15. package/site/content/docs/elements/ai/agents.mdx +232 -0
  16. package/site/content/docs/elements/ai/index.mdx +341 -0
  17. package/site/content/docs/elements/ai/mcp.mdx +275 -0
  18. package/site/content/docs/elements/ai/meta.json +5 -0
  19. package/site/content/docs/elements/ai/models.mdx +283 -0
  20. package/site/content/docs/elements/ai/prompts.mdx +256 -0
  21. package/site/content/docs/elements/channel/email.mdx +266 -0
  22. package/site/content/docs/elements/channel/index.mdx +344 -0
  23. package/site/content/docs/elements/channel/meta.json +5 -0
  24. package/site/content/docs/elements/channel/push.mdx +218 -0
  25. package/site/content/docs/elements/channel/receipts.mdx +206 -0
  26. package/site/content/docs/elements/channel/sms.mdx +264 -0
  27. package/site/content/docs/elements/channel/whatsapp.mdx +220 -0
  28. package/site/content/docs/elements/clock/index.mdx +300 -0
  29. package/site/content/docs/elements/clock/meta.json +5 -0
  30. package/site/content/docs/elements/clock/schedules.mdx +508 -0
  31. package/site/content/docs/elements/clock/sleep.mdx +445 -0
  32. package/site/content/docs/elements/flow/consumers.mdx +707 -0
  33. package/site/content/docs/elements/flow/http.mdx +1080 -0
  34. package/site/content/docs/elements/flow/index.mdx +618 -0
  35. package/site/content/docs/elements/flow/meta.json +5 -0
  36. package/site/content/docs/elements/flow/routing.mdx +559 -0
  37. package/site/content/docs/elements/flow/workflows.mdx +640 -0
  38. package/site/content/docs/elements/gate/auth.mdx +371 -0
  39. package/site/content/docs/elements/gate/authorization.mdx +298 -0
  40. package/site/content/docs/elements/gate/index.mdx +418 -0
  41. package/site/content/docs/elements/gate/meta.json +5 -0
  42. package/site/content/docs/elements/gate/rate-limits.mdx +365 -0
  43. package/site/content/docs/elements/gate/rls.mdx +303 -0
  44. package/site/content/docs/elements/gate/tenancy.mdx +372 -0
  45. package/site/content/docs/elements/index.mdx +52 -11
  46. package/site/content/docs/elements/signal/broadcast.mdx +517 -0
  47. package/site/content/docs/elements/signal/index.mdx +352 -0
  48. package/site/content/docs/elements/signal/live.mdx +590 -0
  49. package/site/content/docs/elements/signal/meta.json +5 -0
  50. package/site/content/docs/elements/signal/once.mdx +596 -0
  51. package/site/content/docs/elements/store/files.mdx +659 -0
  52. package/site/content/docs/elements/store/index.mdx +310 -0
  53. package/site/content/docs/elements/store/kv.mdx +629 -0
  54. package/site/content/docs/elements/store/meta.json +5 -0
  55. package/site/content/docs/elements/store/search.mdx +946 -0
  56. package/site/content/docs/elements/store/sql.mdx +937 -0
  57. package/site/content/docs/elements/vault/config.mdx +256 -0
  58. package/site/content/docs/elements/vault/index.mdx +356 -0
  59. package/site/content/docs/elements/vault/meta.json +5 -0
  60. package/site/content/docs/elements/vault/rotation.mdx +273 -0
  61. package/site/content/docs/elements/vault/secrets.mdx +285 -0
  62. package/site/content/docs/index.mdx +78 -30
  63. package/site/content/docs/meta.json +11 -8
  64. package/site/content/docs/plugins/cors.mdx +2 -1
  65. package/site/content/docs/plugins/csrf.mdx +4 -1
  66. package/site/content/docs/plugins/headers.mdx +2 -1
  67. package/site/content/docs/plugins/ip-allowlist.mdx +2 -1
  68. package/site/content/docs/plugins/magic-link.mdx +6 -0
  69. package/site/content/docs/plugins/maintenance-mode.mdx +2 -1
  70. package/site/content/docs/plugins/oauth.mdx +1 -1
  71. package/site/content/docs/plugins/otp.mdx +12 -1
  72. package/site/content/docs/plugins/passkey.mdx +39 -15
  73. package/site/content/docs/plugins/two-factor.mdx +91 -34
  74. package/site/content/docs/plugins/username.mdx +1 -1
  75. package/site/content/docs/recipes/caddy.mdx +3 -3
  76. package/site/content/docs/recipes/index.mdx +18 -28
  77. package/site/content/docs/recipes/mailpit.mdx +1 -1
  78. package/site/content/docs/recipes/meilisearch.mdx +3 -3
  79. package/site/content/docs/recipes/meta.json +1 -4
  80. package/site/content/docs/recipes/nginx.mdx +2 -2
  81. package/site/content/docs/recipes/openrouter.mdx +283 -0
  82. package/site/content/docs/recipes/pgdog.mdx +3 -3
  83. package/site/content/docs/recipes/postgres.mdx +1 -2
  84. package/site/content/docs/recipes/rustfs.mdx +2 -2
  85. package/site/content/docs/recipes/traefik.mdx +3 -3
  86. package/site/content/docs/reference/cli.mdx +231 -0
  87. package/site/content/docs/reference/configuration.mdx +30 -10
  88. package/site/content/docs/reference/environment-variables.mdx +93 -20
  89. package/site/content/docs/reference/errors.mdx +132 -29
  90. package/site/content/docs/reference/fx.mdx +137 -73
  91. package/site/content/docs/reference/index.mdx +13 -21
  92. package/site/content/docs/reference/meta.json +4 -5
  93. package/site/content/docs/reference/okid.mdx +42 -17
  94. package/site/content/docs/reference/plugins.mdx +4 -3
  95. package/site/content/docs/reference/security.mdx +197 -0
  96. package/site/content/docs/understand/meta.json +5 -0
  97. package/site/content/docs/understand/the-anatomy.mdx +132 -0
  98. package/site/content/docs/understand/the-model.mdx +32 -0
  99. package/site/content/docs/understand/the-problem.mdx +74 -0
  100. package/site/content/docs/understand/the-vocabulary.mdx +26 -0
  101. package/src/auth/api-keys.ts +2 -1
  102. package/src/auth/bindings.ts +64 -16
  103. package/src/auth/gate-auth.test.ts +6 -1
  104. package/src/auth/identity.ts +152 -5
  105. package/src/auth/index.ts +29 -0
  106. package/src/auth/invites.ts +2 -1
  107. package/src/auth/method-context.ts +41 -0
  108. package/src/auth/oauth-as/crypto.ts +2 -1
  109. package/src/auth/oauth-as/stores.ts +2 -1
  110. package/src/auth/operator.ts +2 -1
  111. package/src/auth/sessions-jwt.test.ts +77 -0
  112. package/src/auth/sessions.ts +20 -4
  113. package/src/auth/tenants.ts +3 -2
  114. package/src/auth/two-factor-challenge.test.ts +61 -0
  115. package/src/auth/two-factor-challenge.ts +284 -0
  116. package/src/auth/verification.ts +5 -0
  117. package/src/bench/REPORT.md +49 -0
  118. package/src/bench/g03-signal-once.bench.ts +1 -1
  119. package/src/bench/g10-observability-contention.bench.ts +1 -1
  120. package/src/bench/g17-hybrid-search.bench.ts +572 -0
  121. package/src/bench/load-app.ts +2 -10
  122. package/src/cli/ai-setup/ai-setup.test.ts +331 -42
  123. package/src/cli/ai-setup/apply.ts +294 -52
  124. package/src/cli/ai-setup/catalog.ts +238 -1342
  125. package/src/cli/ai-setup/index.ts +14 -99
  126. package/src/cli/ai-setup/prompts.ts +60 -443
  127. package/src/cli/ask-seed.test.ts +96 -0
  128. package/src/cli/ask-seed.ts +82 -0
  129. package/src/cli/ask-vault-gaps.test.ts +84 -0
  130. package/src/cli/ask-vault-gaps.ts +146 -0
  131. package/src/cli/build.ts +2 -2
  132. package/src/cli/client-add.test.ts +26 -1
  133. package/src/cli/client-add.ts +106 -27
  134. package/src/cli/competitor-mention-removal.test.ts +1 -1
  135. package/src/cli/db-seed.ts +2 -0
  136. package/src/cli/db.ts +150 -11
  137. package/src/cli/dev.ts +119 -211
  138. package/src/cli/docker-clean.ts +2 -2
  139. package/src/cli/docker-cli.test.ts +1 -1
  140. package/src/cli/doctor-diff.ts +4 -2
  141. package/src/cli/doctor-pii.test.ts +1 -1
  142. package/src/cli/doctor.ts +14 -1
  143. package/src/cli/hero-meta.test.ts +4 -2
  144. package/src/cli/load-config.images.test.ts +8 -10
  145. package/src/cli/load-config.ts +1 -1
  146. package/src/cli/registry.ts +22 -10
  147. package/src/cli/replay.ts +3 -1
  148. package/src/cli/start.ts +1 -1
  149. package/src/cli/tui/keys.ts +2 -3
  150. package/src/client/auth/cookies.ts +74 -0
  151. package/src/client/auth/create-auth-client.ts +636 -0
  152. package/src/client/auth/denials.ts +99 -0
  153. package/src/client/auth/session.ts +283 -0
  154. package/src/client/auth.test.ts +251 -0
  155. package/src/client/auth.ts +39 -114
  156. package/src/client/create-with-session.ts +245 -0
  157. package/src/client/create.ts +51 -5
  158. package/src/client/index.ts +11 -1
  159. package/src/client/live.ts +11 -79
  160. package/src/client/notes-contract.test.ts +5 -10
  161. package/src/client/sse.ts +137 -0
  162. package/src/client/stream.ts +147 -0
  163. package/src/client/transport.test.ts +32 -0
  164. package/src/client/transport.ts +123 -26
  165. package/src/client/types.ts +118 -14
  166. package/src/client-react/index.ts +185 -24
  167. package/src/client-react/use-live-query.ts +13 -3
  168. package/src/compiler/aot.test.ts +7 -4
  169. package/src/compiler/effects-embed.test.ts +56 -0
  170. package/src/compiler/effects-fetch.test.ts +43 -0
  171. package/src/compiler/effects-infer.ts +58 -2
  172. package/src/compiler/extract.test.ts +258 -44
  173. package/src/compiler/extract.ts +566 -48
  174. package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -4
  175. package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +2 -8
  176. package/src/compiler/fixtures/skyport.expected.json +4 -4
  177. package/src/compiler/fixtures/triggers/five-triggers.ts +14 -10
  178. package/src/compiler/response.ts +2 -2
  179. package/src/compiler/search-writer-isolation.test.ts +40 -0
  180. package/src/config/index.ts +11 -0
  181. package/src/console/server/app.ts +17 -23
  182. package/src/console/server/bind.ts +4 -0
  183. package/src/console/server/console.test.ts +2 -2
  184. package/src/console/server/flows-invoke.test.ts +50 -34
  185. package/src/console/server/flows.ts +436 -255
  186. package/src/console/server/runs-ingest.test.ts +6 -3
  187. package/src/console/server/serve.ts +1 -1
  188. package/src/console/server/signals.test.ts +1 -5
  189. package/src/console/server/signals.ts +8 -6
  190. package/src/console/server/state.ts +6 -1
  191. package/src/console/server/store.ts +1 -1
  192. package/src/console/ui-next/dist/assets/FileExportIcon-Ck-5od4R.js +1 -0
  193. package/src/console/ui-next/dist/assets/MoreHorizontalCircle01Icon-gMNGsE37.js +1 -0
  194. package/src/console/ui-next/dist/assets/PlusSignIcon-CwG3nxfu.js +1 -0
  195. package/src/console/ui-next/dist/assets/UnavailableIcon-D9cvHVPr.js +1 -0
  196. package/src/console/ui-next/dist/assets/UserIcon-DaE2PB5_.js +1 -0
  197. package/src/console/ui-next/dist/assets/access-page-DFeymU07.js +4 -0
  198. package/src/console/ui-next/dist/assets/agent-disclosure-U1rdfblp.js +1 -0
  199. package/src/console/ui-next/dist/assets/cache-glyph-BeFJeqBG.js +1 -0
  200. package/src/console/ui-next/dist/assets/call-pii-button-CVAONPii.js +1 -0
  201. package/src/console/ui-next/dist/assets/collapsible-D2A6NJ-3.js +1 -0
  202. package/src/console/ui-next/dist/assets/copy-inline-button-CAYD18cr.js +1 -0
  203. package/src/console/ui-next/dist/assets/dagre.esm-B1_XeuLP.js +1 -0
  204. package/src/console/ui-next/dist/assets/detail-header-DVWNjWjg.js +1 -0
  205. package/src/console/ui-next/dist/assets/dropdown-menu-4h2LOVXM.js +1 -0
  206. package/src/console/ui-next/dist/assets/duration-tone-Cgk_h5ja.js +9 -0
  207. package/src/console/ui-next/dist/assets/element-icons-BI8cJgdh.js +1 -0
  208. package/src/console/ui-next/dist/assets/explorer-empty-CJs5A-wm.js +1 -0
  209. package/src/console/ui-next/dist/assets/flows-page-Dss7941e.js +1 -0
  210. package/src/console/ui-next/dist/assets/highlighted-json-MYZQtRnw.js +154 -0
  211. package/src/console/ui-next/dist/assets/http-method-Jrh39p7A.js +1 -0
  212. package/src/console/ui-next/dist/assets/index-D0zS5rKO.css +2 -0
  213. package/src/console/ui-next/dist/assets/index-DXP2dBIF.js +63 -0
  214. package/src/console/ui-next/dist/assets/observability-page-HvolXxTI.js +4 -0
  215. package/src/console/ui-next/dist/assets/react-dom-Ddte4I-Q.js +9 -0
  216. package/src/console/ui-next/dist/assets/replica-lag-CSh2dzrb.js +18 -0
  217. package/src/console/ui-next/dist/assets/request-meta-BatF8KrK.js +1 -0
  218. package/src/console/ui-next/dist/assets/shortcut-keys-3ILd8oGn.js +1 -0
  219. package/src/console/ui-next/dist/assets/{sql-BskegiFM.js → sql-BsFa4tDR.js} +1 -1
  220. package/src/console/ui-next/dist/assets/store-page-eiKiHnNe.js +41 -0
  221. package/src/console/ui-next/dist/assets/trace-detail-sheet-CZkMeKS-.js +2 -0
  222. package/src/console/ui-next/dist/assets/tree-expand-toggle-CW8y5A2h.js +55 -0
  223. package/src/console/ui-next/dist/assets/units-page-B_RWJrEO.js +1 -0
  224. package/src/console/ui-next/dist/assets/{use-vault-list-uk4WVboC.js → use-vault-list-CT4-gajj.js} +1 -1
  225. package/src/console/ui-next/dist/assets/vault-page-CWrg-A68.js +2 -0
  226. package/src/console/ui-next/dist/assets/xyflow-CSyC6ryz.css +1 -0
  227. package/src/console/ui-next/dist/assets/xyflow-yApv7D4e.js +7 -0
  228. package/src/console/ui-next/dist/index.html +6 -13
  229. package/src/console/ui-next/seed-invoke-host.ts +21 -18
  230. package/src/console/ui-next/src/client.ts +7 -2
  231. package/src/console/ui-next/src/features/flows/graph/element-map.ts +2 -0
  232. package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +12 -2
  233. package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +4 -0
  234. package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +8 -0
  235. package/src/console/ui-next/src/features/flows/traces/waterfall-bars.ts +4 -0
  236. package/src/console/ui-next/src/features/flows/traces/waterfall-tooltip.ts +9 -3
  237. package/src/console/ui-next/src/features/store/detail/reveal-cell.tsx +7 -11
  238. package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +2 -2
  239. package/src/console/ui-next/src/features/store/lib/fields-from-table.ts +2 -0
  240. package/src/console/ui-next/src/features/store/lib/grid-model.test.ts +25 -1
  241. package/src/console/ui-next/src/features/store/lib/grid-model.ts +36 -1
  242. package/src/console/ui-next/src/features/store/query/query-results.tsx +3 -3
  243. package/src/console/ui-next/src/features/units/detail/effects-summary.tsx +2 -0
  244. package/src/console/ui-next/src/features/units/lib/call-read-safe.ts +3 -1
  245. package/src/console/ui-next/src/features/units/lib/fields-from-schema.ts +6 -0
  246. package/src/docker/ai-model-status.test.ts +1 -12
  247. package/src/docker/ai-model-status.ts +10 -38
  248. package/src/docker/compose-up.test.ts +100 -0
  249. package/src/docker/compose-up.ts +292 -0
  250. package/src/docker/compose.ts +4 -14
  251. package/src/docker/derive.ts +9 -34
  252. package/src/docker/docker.test.ts +16 -261
  253. package/src/docker/images-config.test.ts +13 -25
  254. package/src/docker/index.ts +1 -24
  255. package/src/docker/recipes/index.ts +0 -23
  256. package/src/docker/recipes/pgdog.ts +6 -4
  257. package/src/docker/stack-id.test.ts +2 -2
  258. package/src/docker/stack-id.ts +0 -1
  259. package/src/docker/types.ts +2 -2
  260. package/src/drivers/ai-anthropic.ts +10 -0
  261. package/src/drivers/ai-openai-compatible.ts +34 -7
  262. package/src/drivers/ai-providers.test.ts +0 -107
  263. package/src/drivers/ai-stream.test.ts +0 -34
  264. package/src/drivers/ai-types.ts +17 -8
  265. package/src/drivers/channel-fcm.ts +6 -1
  266. package/src/drivers/channel-msegat.ts +6 -1
  267. package/src/drivers/channel-resend.ts +5 -1
  268. package/src/drivers/channel-smtp.ts +5 -1
  269. package/src/drivers/channel-sndr.ts +10 -1
  270. package/src/drivers/channel-taqnyat-mail.ts +5 -1
  271. package/src/drivers/channel-taqnyat-whatsapp.ts +6 -1
  272. package/src/drivers/channel-taqnyat.ts +6 -1
  273. package/src/drivers/channel-types.ts +10 -0
  274. package/src/drivers/channel-unifonic.ts +6 -1
  275. package/src/drivers/channel-wa-cloud.ts +6 -1
  276. package/src/drivers/channel-webpush.ts +6 -1
  277. package/src/drivers/external.ts +33 -0
  278. package/src/drivers/index.ts +0 -10
  279. package/src/drivers/meilisearch.ts +6 -0
  280. package/src/drivers/oauth-types.ts +4 -0
  281. package/src/drivers/postgres.test.ts +74 -3
  282. package/src/drivers/postgres.ts +175 -6
  283. package/src/drivers/signal-types.ts +5 -5
  284. package/src/drivers/types.ts +7 -0
  285. package/src/drivers/vault-types.ts +5 -0
  286. package/src/elements/ai/declare.ts +17 -4
  287. package/src/elements/ai/eval.ts +1 -1
  288. package/src/elements/ai/mcp-http.ts +1 -1
  289. package/src/elements/ai/pii.ts +1 -1
  290. package/src/elements/ai/providers.test.ts +289 -0
  291. package/src/elements/ai/providers.ts +169 -0
  292. package/src/elements/ai/runtime.ts +68 -5
  293. package/src/elements/ai/schema.ts +2 -2
  294. package/src/elements/ai.test.ts +7 -7
  295. package/src/elements/ai.ts +17 -1
  296. package/src/elements/channel/runtime.ts +105 -12
  297. package/src/elements/clock/cron-fields.test.ts +144 -0
  298. package/src/elements/clock/cron-fields.ts +185 -0
  299. package/src/elements/clock/declare.ts +247 -3
  300. package/src/elements/clock.ts +21 -1
  301. package/src/elements/index.ts +15 -0
  302. package/src/elements/signal/chaos-child.ts +1 -2
  303. package/src/elements/signal/declare.ts +65 -37
  304. package/src/elements/signal/delivery-modes.test.ts +11 -31
  305. package/src/elements/signal/dry-run-replay.test.ts +1 -7
  306. package/src/elements/signal/dry-run-write-isolation.test.ts +1 -7
  307. package/src/elements/signal/key-ordering.test.ts +6 -26
  308. package/src/elements/signal/lease-reclaim.test.ts +2 -6
  309. package/src/elements/signal/optional-emit.test.ts +6 -9
  310. package/src/elements/signal/order-lifecycle.test.ts +4 -17
  311. package/src/elements/signal/orphan-messages.test.ts +4 -15
  312. package/src/elements/signal/reconcile.test.ts +2 -2
  313. package/src/elements/signal/runtime.ts +1 -1
  314. package/src/elements/signal/schema-emit.test.ts +4 -8
  315. package/src/elements/signal.test.ts +24 -49
  316. package/src/elements/signal.ts +9 -2
  317. package/src/elements/store/domain-ddl.test.ts +1 -1
  318. package/src/elements/store/live-http.test.ts +9 -4
  319. package/src/elements/store/prepare-row.test.ts +123 -0
  320. package/src/elements/store/resource.ts +43 -12
  321. package/src/elements/store/schema-decl.ts +117 -0
  322. package/src/elements/store/search-backfill.ts +205 -0
  323. package/src/elements/store/search-bind.ts +66 -0
  324. package/src/elements/store/search-bm25.ts +78 -0
  325. package/src/elements/store/search-ddl.ts +156 -0
  326. package/src/elements/store/search-embed-flow.ts +141 -0
  327. package/src/elements/store/search-errors.ts +34 -0
  328. package/src/elements/store/search-fusion.ts +112 -0
  329. package/src/elements/store/search-lsh.ts +179 -0
  330. package/src/elements/store/search-runtime.ts +300 -0
  331. package/src/elements/store/search.test.ts +144 -0
  332. package/src/elements/store/sql-session.test.ts +1 -0
  333. package/src/elements/store/sql-session.ts +83 -3
  334. package/src/elements/store/table.ts +23 -2
  335. package/src/elements/store/upsert-app.test.ts +1 -3
  336. package/src/elements/store.ts +50 -0
  337. package/src/full.ts +4 -2
  338. package/src/http.ts +4 -1
  339. package/src/i18n/catalogs/ar.ts +14 -6
  340. package/src/i18n/catalogs/en.ts +14 -6
  341. package/src/index.ts +6 -1
  342. package/src/kernel/adopt-barrel-fresh.test.ts +7 -7
  343. package/src/kernel/adopt-routes.ts +9 -1
  344. package/src/kernel/app-auth.ts +5 -0
  345. package/src/kernel/app.ts +102 -9
  346. package/src/kernel/auto-cache.test.ts +6 -12
  347. package/src/kernel/auto-registry.test.ts +3 -3
  348. package/src/kernel/boot-bind/ai.test.ts +22 -19
  349. package/src/kernel/boot-bind/ai.ts +5 -43
  350. package/src/kernel/boot-bind/clock.ts +23 -5
  351. package/src/kernel/boot-bind/honor-config.test.ts +4 -4
  352. package/src/kernel/boot.test.ts +11 -6
  353. package/src/kernel/boot.ts +66 -16
  354. package/src/kernel/boundary-contract.ts +93 -0
  355. package/src/kernel/budget.test.ts +1 -1
  356. package/src/kernel/call.ts +89 -0
  357. package/src/kernel/capability.ts +6 -0
  358. package/src/kernel/client-descriptor.ts +123 -0
  359. package/src/kernel/clock-timezone.test.ts +80 -0
  360. package/src/kernel/correlation.test.ts +1 -1
  361. package/src/kernel/dry-run.ts +11 -8
  362. package/src/kernel/effects-stamping.test.ts +42 -9
  363. package/src/kernel/effects.test.ts +4 -2
  364. package/src/kernel/effects.ts +51 -9
  365. package/src/kernel/errors-channel.ts +17 -0
  366. package/src/kernel/errors-live-resume.ts +3 -2
  367. package/src/kernel/errors-tenant.ts +7 -4
  368. package/src/kernel/errors.registry-helpers.ts +96 -0
  369. package/src/kernel/errors.registry.test.ts +139 -25
  370. package/src/kernel/errors.ts +102 -23
  371. package/src/kernel/external-effects.test.ts +196 -0
  372. package/src/kernel/flow.test.ts +13 -4
  373. package/src/kernel/flow.ts +69 -71
  374. package/src/kernel/fx-ask-telemetry.test.ts +2 -2
  375. package/src/kernel/fx-dead-letters.test.ts +3 -3
  376. package/src/kernel/fx-fetch.ts +55 -0
  377. package/src/kernel/fx-live-stream.ts +2 -2
  378. package/src/kernel/fx-live.test.ts +8 -8
  379. package/src/kernel/fx.test.ts +17 -11
  380. package/src/kernel/fx.ts +180 -57
  381. package/src/kernel/horizontal-child.ts +1 -1
  382. package/src/kernel/http-query.test.ts +3 -6
  383. package/src/kernel/index.ts +13 -3
  384. package/src/kernel/instance-id.ts +1 -1
  385. package/src/kernel/instances.test.ts +40 -0
  386. package/src/kernel/instances.ts +47 -9
  387. package/src/kernel/live-http.test.ts +6 -7
  388. package/src/kernel/live-http.ts +6 -2
  389. package/src/kernel/live-resume.test.ts +1 -1
  390. package/src/kernel/mcp-tool.test.ts +3 -3
  391. package/src/kernel/on.ts +98 -4
  392. package/src/kernel/pipeline.test.ts +12 -15
  393. package/src/kernel/plugin-elements.test.ts +1 -1
  394. package/src/kernel/stamp-http.test.ts +3 -3
  395. package/src/kernel/triggers.ts +210 -95
  396. package/src/kernel-entry.ts +0 -1
  397. package/src/manifest/diff.ts +11 -1
  398. package/src/manifest/fixtures/skyport.excerpt.json +1 -1
  399. package/src/manifest/types.ts +46 -2
  400. package/src/mcp/docs-index.ts +3 -3
  401. package/src/mcp/docs-mcp.test.ts +2 -2
  402. package/src/mcp/docs-tools.ts +1 -1
  403. package/src/okid.test.ts +56 -0
  404. package/src/okid.ts +55 -11
  405. package/src/plugins/anonymous.ts +8 -4
  406. package/src/plugins/auth/shared.ts +27 -3
  407. package/src/plugins/auth-delivery.mailpit.integration.test.ts +1 -1
  408. package/src/plugins/auth-methods.security.test.ts +561 -23
  409. package/src/plugins/config-source.ts +1 -1
  410. package/src/plugins/csrf.test.ts +65 -0
  411. package/src/plugins/index.ts +2 -0
  412. package/src/plugins/magic-link.ts +36 -16
  413. package/src/plugins/oauth/shared.ts +3 -1
  414. package/src/plugins/oauth.ts +22 -14
  415. package/src/plugins/otp.ts +37 -21
  416. package/src/plugins/passkey-webauthn.ts +9 -6
  417. package/src/plugins/passkey.ts +98 -26
  418. package/src/plugins/pre-account-hijack.test.ts +223 -0
  419. package/src/plugins/two-factor.ts +466 -37
  420. package/src/plugins/username.ts +19 -7
  421. package/src/release/build-lib.ts +1 -0
  422. package/src/release/limits.ts +2 -2
  423. package/src/release/measure.ts +1 -1
  424. package/src/runs/collect.test.ts +0 -2
  425. package/src/runtime/json-code-block.test.ts +250 -20
  426. package/src/runtime/json-code-block.ts +1261 -41
  427. package/src/term.test.ts +1 -1
  428. package/src/test/create-test-app.test.ts +12 -12
  429. package/src/test/live-signals.test.ts +6 -6
  430. package/src/test/provisions.integration.test.ts +10 -14
  431. package/src/test/reset-element-registries.ts +1 -1
  432. package/src/test/tenant-isolation.test.ts +12 -6
  433. package/site/content/docs/deployment/docker-swarm.mdx +0 -164
  434. package/site/content/docs/deployment/docker.mdx +0 -227
  435. package/site/content/docs/deployment/index.mdx +0 -83
  436. package/site/content/docs/deployment/kubernetes.mdx +0 -176
  437. package/site/content/docs/deployment/meta.json +0 -5
  438. package/site/content/docs/deployment/reverse-proxy.mdx +0 -234
  439. package/site/content/docs/elements/ai.mdx +0 -385
  440. package/site/content/docs/elements/channel.mdx +0 -346
  441. package/site/content/docs/elements/clock.mdx +0 -244
  442. package/site/content/docs/elements/flow.mdx +0 -420
  443. package/site/content/docs/elements/gate.mdx +0 -436
  444. package/site/content/docs/elements/signal.mdx +0 -380
  445. package/site/content/docs/elements/store.mdx +0 -1099
  446. package/site/content/docs/elements/vault.mdx +0 -405
  447. package/site/content/docs/get-started/basic-usage.mdx +0 -173
  448. package/site/content/docs/get-started/index.mdx +0 -43
  449. package/site/content/docs/get-started/installation.mdx +0 -220
  450. package/site/content/docs/get-started/introduction.mdx +0 -132
  451. package/site/content/docs/get-started/meta.json +0 -13
  452. package/site/content/docs/get-started/project-structure.mdx +0 -925
  453. package/site/content/docs/get-started/testing.mdx +0 -328
  454. package/site/content/docs/get-started/why.mdx +0 -162
  455. package/site/content/docs/recipes/llama-cpp.mdx +0 -151
  456. package/site/content/docs/recipes/ollama.mdx +0 -142
  457. package/site/content/docs/recipes/sglang.mdx +0 -105
  458. package/site/content/docs/recipes/vllm.mdx +0 -106
  459. package/site/content/docs/reference/cli.md +0 -232
  460. package/site/content/docs/reference/client.mdx +0 -469
  461. package/site/content/docs/reference/security.md +0 -74
  462. package/src/cli/ai-setup/detect-ollama.ts +0 -213
  463. package/src/cli/ai-setup/recommend.test.ts +0 -225
  464. package/src/cli/ai-setup/recommend.ts +0 -225
  465. package/src/cli/dev-controls.test.ts +0 -122
  466. package/src/cli/dev-controls.ts +0 -164
  467. package/src/cli/tui/DevLive.tsx +0 -126
  468. package/src/console/ui-next/dist/assets/access-page-De7Lc2JC.js +0 -4
  469. package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +0 -1
  470. package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +0 -1
  471. package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +0 -1
  472. package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +0 -1
  473. package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +0 -1
  474. package/src/console/ui-next/dist/assets/dagre.esm-ZwcdTuZZ.js +0 -1
  475. package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +0 -1
  476. package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +0 -1
  477. package/src/console/ui-next/dist/assets/duration-tone-sC3lGABz.js +0 -9
  478. package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +0 -1
  479. package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +0 -1
  480. package/src/console/ui-next/dist/assets/flows-page-RGy7VEA_.js +0 -1
  481. package/src/console/ui-next/dist/assets/highlighted-json-Awq7gYdu.js +0 -154
  482. package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +0 -1
  483. package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +0 -2
  484. package/src/console/ui-next/dist/assets/index-_rgpdVzo.js +0 -66
  485. package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +0 -1
  486. package/src/console/ui-next/dist/assets/observability-page-Ds6pcnh-.js +0 -4
  487. package/src/console/ui-next/dist/assets/preload-helper-oH4irX4C.js +0 -1
  488. package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +0 -1
  489. package/src/console/ui-next/dist/assets/react-dom-Bph1y7z7.js +0 -9
  490. package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +0 -18
  491. package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +0 -1
  492. package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +0 -1
  493. package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +0 -1
  494. package/src/console/ui-next/dist/assets/skeleton-D-czQJT6.js +0 -1
  495. package/src/console/ui-next/dist/assets/store-page-02xOiqIK.js +0 -41
  496. package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +0 -2
  497. package/src/console/ui-next/dist/assets/tree-expand-toggle-BtyhmWb4.js +0 -55
  498. package/src/console/ui-next/dist/assets/units-page-4rHOePuE.js +0 -1
  499. package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +0 -1
  500. package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +0 -1
  501. package/src/console/ui-next/dist/assets/vault-page-DISPgxLM.js +0 -2
  502. package/src/console/ui-next/dist/assets/xyflow-D7n4g6go.js +0 -7
  503. package/src/console/ui-next/dist/assets/xyflow-DZ0Ws1xk.css +0 -1
  504. package/src/docker/ollama-pull.ts +0 -232
  505. package/src/docker/recipes/llama-cpp.ts +0 -298
  506. package/src/docker/recipes/ollama.ts +0 -44
  507. package/src/docker/recipes/sglang.ts +0 -55
  508. package/src/docker/recipes/vllm.ts +0 -44
  509. package/src/drivers/ai-ollama-tools.integration.test.ts +0 -109
  510. package/src/drivers/ai-ollama.integration.test.ts +0 -184
  511. package/src/drivers/ai-ollama.ts +0 -389
  512. package/src/drivers/ollama.ts +0 -14
@@ -0,0 +1,629 @@
1
+ ---
2
+ title: "KV"
3
+ description: "Namespaced key-value storage with duration TTL — sessions, caches, and short-lived locks via fx.store."
4
+ icon: "Key"
5
+ source: "docs/spec/unified-theory.md"
6
+ ---
7
+
8
+ The KV facet is a typed namespace for sessions, cache entries, OTPs, and other short-lived keys.
9
+ Declare with `store.kv(name)`, then read and write through `fx.store(decl)` inside Flows.
10
+
11
+ For developers caching or locking on okengine — one get/set surface; Redis in Docker, memory in tests.
12
+
13
+ <Callout title="The one rule">
14
+ Pass the **declaration** into `fx.store(sessions)`. There is no `fx.store.kv`
15
+ namespace. TTL is a duration **string** (`"15m"`), not `{ ttl: "15m" }`.
16
+ </Callout>
17
+
18
+ ## Smallest Example
19
+
20
+ <Steps>
21
+
22
+ <Step>
23
+ ### Declare a namespace
24
+
25
+ ```typescript title="src/core.ts"
26
+ import { store } from "okengine";
27
+
28
+ export const sessions = store.kv("sessions");
29
+ ```
30
+
31
+ </Step>
32
+
33
+ <Step>
34
+ ### Set and get in a Flow
35
+
36
+ ```typescript title="src/flows/sessions/create.ts"
37
+ import { on, flow, http } from "okengine";
38
+ import { z } from "zod";
39
+ import { sessions } from "@/core";
40
+
41
+ export const create = on(
42
+ http.post(),
43
+ flow({
44
+ in: z.object({ token: z.string(), userId: z.string() }),
45
+ do: async ({ token, userId }, fx) => {
46
+ await fx.store(sessions).set(`session:${token}`, userId, "1h");
47
+ const cached = await fx.store(sessions).get(`session:${token}`);
48
+ return { userId: cached };
49
+ },
50
+ }),
51
+ );
52
+ ```
53
+
54
+ </Step>
55
+
56
+ <Step>
57
+ ### Call the endpoint
58
+
59
+ ```bash
60
+ curl -X POST http://localhost:6530/sessions \
61
+ -H "content-type: application/json" \
62
+ -d '{"token":"abc","userId":"u1"}'
63
+ ```
64
+
65
+ Response:
66
+
67
+ ```json
68
+ {
69
+ "data": { "userId": "u1" },
70
+ "error": null
71
+ }
72
+ ```
73
+
74
+ </Step>
75
+
76
+ </Steps>
77
+
78
+ <Callout title="Import the declaration">
79
+ `store.kv(…)` must run when the app loads — export it from a module Flows import (starters:
80
+ `@/core`). A Flow that only types the name as a string never opens a namespace; the handle needs
81
+ the decl object.
82
+ </Callout>
83
+
84
+ ## Progressive Patterns
85
+
86
+ From a plain set to TTL, list/delete, and durable SQL-backed namespaces:
87
+
88
+ <Tabs items={["Set / get", "TTL", "List / delete", "Durable"]}>
89
+
90
+ <Tab value="Set / get">
91
+
92
+ Values may be strings, numbers, or JSON-serializable objects:
93
+
94
+ ```typescript title="src/flows/prefs/[userId]/get.ts"
95
+ import { on, flow, http } from "okengine";
96
+ import { z } from "zod";
97
+ import { sessions } from "@/core";
98
+
99
+ export const get = on(
100
+ http.get(),
101
+ flow({
102
+ in: z.object({ userId: z.string() }),
103
+ do: async ({ userId }, fx) => {
104
+ const prefs = await fx.store(sessions).get(`user:${userId}:prefs`);
105
+ return { prefs: prefs ?? null };
106
+ },
107
+ }),
108
+ );
109
+ ```
110
+
111
+ Missing keys return `undefined` / driver nullish — treat as a cache miss.
112
+
113
+ </Tab>
114
+
115
+ <Tab value="TTL">
116
+
117
+ Third argument to `set` is optional. Format: `^(\d+)(ms|s|m|h|d)$`:
118
+
119
+ ```typescript title="src/flows/otp/create.ts"
120
+ import { on, flow, http } from "okengine";
121
+ import { z } from "zod";
122
+ import { sessions } from "@/core";
123
+
124
+ export const create = on(
125
+ http.post(),
126
+ flow({
127
+ in: z.object({ userId: z.string() }),
128
+ do: async ({ userId }, fx) => {
129
+ const code = "482193";
130
+ await fx.store(sessions).set(`otp:${userId}`, code, "5m");
131
+ const remaining = await fx.store(sessions).ttlMs(`otp:${userId}`);
132
+ return { code, ttlMs: remaining };
133
+ },
134
+ }),
135
+ );
136
+ ```
137
+
138
+ **Consequence:** redis honors TTL; the `memory` driver records expiry for
139
+ `ttlMs` but **does not delete** expired keys on `get` — see [TTL Physics](#ttl-physics).
140
+
141
+ </Tab>
142
+
143
+ <Tab value="List / delete">
144
+
145
+ ```typescript title="src/flows/sessions/route.ts"
146
+ import { on, flow, http } from "okengine";
147
+ import { sessions } from "@/core";
148
+
149
+ export const clear = on(
150
+ http.delete(),
151
+ flow({
152
+ do: async (_, fx) => {
153
+ const keys = await fx.store(sessions).list("session:");
154
+ for (const key of keys) {
155
+ await fx.store(sessions).delete(key);
156
+ }
157
+ return { removed: keys.length };
158
+ },
159
+ }),
160
+ );
161
+ ```
162
+
163
+ `list` is for Console browsing and admin Flows — not a substitute for a SQL
164
+ index when you need relational queries.
165
+
166
+ </Tab>
167
+
168
+ <Tab value="Durable">
169
+
170
+ `durable: true` persists the namespace in SQL (`oke_kv` on `DATABASE_URL`) —
171
+ not Redis. Distinct from Flow `durable: true`:
172
+
173
+ ```typescript title="src/core.ts"
174
+ import { store } from "okengine";
175
+
176
+ export const featureFlags = store.kv("feature-flags", {
177
+ durable: true,
178
+ description: "Flags that must survive Redis flushes",
179
+ });
180
+ ```
181
+
182
+ Requires a configured SQL driver. Durable namespaces do **not** support
183
+ driver-level `eval` (Gate rate Lua stays on Redis). See
184
+ [Durable Namespaces](#durable-namespaces).
185
+
186
+ </Tab>
187
+
188
+ </Tabs>
189
+
190
+ ## Method Reference
191
+
192
+ `fx.store(kvDecl)`:
193
+
194
+ | Method | Signature | Purpose | Side effect |
195
+ | -------- | ----------------------- | ------------------------------- | ----------- |
196
+ | `get` | `get(key)` | Read value (or miss) | Read |
197
+ | `set` | `set(key, value, ttl?)` | Write; optional duration string | Write |
198
+ | `delete` | `delete(key)` | Remove; returns whether deleted | Write |
199
+ | `list` | `list(prefix?)` | List keys (optional prefix) | Read |
200
+ | `ttlMs` | `ttlMs(key)` | Remaining ms, or `null` | Read |
201
+
202
+ Handle also exposes `ref` (`kv:name`) and `driverId`
203
+ (`memory` · `redis` · `postgres` · `pglite` when durable).
204
+
205
+ There is **no** public `incr`, `setNx`, `del` alias, or `eval` on the fx handle.
206
+ Gate rate strategies use Redis `eval` internally — not application Flows.
207
+
208
+ ## Namespaces
209
+
210
+ Every `store.kv(name)` registers a Manifest resource `kv:name`. The string you
211
+ pass is the namespace id — keys inside it are relative to that namespace.
212
+
213
+ **Cache namespace** (default) — opens on `drivers.store.kv` (`redis` / `memory`):
214
+
215
+ ```typescript
216
+ export const sessions = store.kv("sessions");
217
+ // ref → "kv:sessions"
218
+ ```
219
+
220
+ **Durable namespace** — opens on the shared SQL connection (`oke_kv` table):
221
+
222
+ ```typescript
223
+ export const drafts = store.kv("drafts", {
224
+ durable: true,
225
+ description: "Compose drafts that survive Redis recreate",
226
+ });
227
+ ```
228
+
229
+ **Global under tenancy** — skip the `{tenantId}:` prefix when `gate.auth.tenant`
230
+ is on:
231
+
232
+ ```typescript
233
+ export const sharedFlags = store.kv("shared-flags", {
234
+ tenantScoped: false,
235
+ });
236
+ ```
237
+
238
+ ## Methods
239
+
240
+ Each verb binds through `fx.store(decl)` inside `flow({ do })`:
241
+
242
+ <Tabs items={["get", "set", "delete", "list", "ttlMs"]}>
243
+
244
+ <Tab value="get">
245
+
246
+ Read a key. Misses return `undefined` (or driver nullish):
247
+
248
+ ```typescript title="src/flows/sessions/[token]/get.ts"
249
+ import { on, flow, http } from "okengine";
250
+ import { z } from "zod";
251
+ import { sessions } from "@/core";
252
+
253
+ export const get = on(
254
+ http.get(),
255
+ flow({
256
+ in: z.object({ token: z.string() }),
257
+ errors: { NotFound: z.object({ token: z.string() }) },
258
+ do: async ({ token }, fx) => {
259
+ const userId = await fx.store(sessions).get(`session:${token}`);
260
+ if (userId === undefined || userId === null) {
261
+ return fx.fail("NotFound", { token });
262
+ }
263
+ return { userId };
264
+ },
265
+ }),
266
+ );
267
+ ```
268
+
269
+ </Tab>
270
+
271
+ <Tab value="set">
272
+
273
+ Write a value. Pass a duration string as the third argument for TTL:
274
+
275
+ ```typescript title="src/flows/sessions/create.ts"
276
+ import { on, flow, http } from "okengine";
277
+ import { z } from "zod";
278
+ import { sessions } from "@/core";
279
+
280
+ export const create = on(
281
+ http.post(),
282
+ flow({
283
+ in: z.object({ token: z.string(), userId: z.string() }),
284
+ do: async ({ token, userId }, fx) => {
285
+ await fx.store(sessions).set(`session:${token}`, userId, "1h");
286
+ return { ok: true as const };
287
+ },
288
+ }),
289
+ );
290
+ ```
291
+
292
+ Omit the third argument for a key with no expiry. Redis JSON-stringifies
293
+ objects; memory keeps the value as-is.
294
+
295
+ </Tab>
296
+
297
+ <Tab value="delete">
298
+
299
+ Remove a key. Returns `true` when a key was deleted:
300
+
301
+ ```typescript title="src/flows/sessions/[token]/remove.ts"
302
+ import { on, flow, http } from "okengine";
303
+ import { z } from "zod";
304
+ import { sessions } from "@/core";
305
+
306
+ export const remove = on(
307
+ http.delete(),
308
+ flow({
309
+ in: z.object({ token: z.string() }),
310
+ do: async ({ token }, fx) => {
311
+ const removed = await fx.store(sessions).delete(`session:${token}`);
312
+ return { removed };
313
+ },
314
+ }),
315
+ );
316
+ ```
317
+
318
+ </Tab>
319
+
320
+ <Tab value="list">
321
+
322
+ List keys, optionally filtered by prefix. Results are sorted; the returned
323
+ strings are the **logical** keys (no driver prefix, no tenant prefix):
324
+
325
+ ```typescript title="src/flows/drafts/list.ts"
326
+ import { on, flow, http } from "okengine";
327
+ import { drafts } from "@/core";
328
+
329
+ export const list = on(
330
+ http.get(),
331
+ flow({
332
+ do: async (_, fx) => {
333
+ const keys = await fx.store(drafts).list();
334
+ const items = [];
335
+ for (const key of keys) {
336
+ const value = await fx.store(drafts).get(key);
337
+ items.push({ id: key, value });
338
+ }
339
+ return items;
340
+ },
341
+ }),
342
+ );
343
+ ```
344
+
345
+ On Redis, `list` uses `SCAN` — never `KEYS *`. A client without SCAN throws
346
+ rather than scanning the whole instance.
347
+
348
+ </Tab>
349
+
350
+ <Tab value="ttlMs">
351
+
352
+ Remaining lifetime in milliseconds, or `null` when the key has no expiry
353
+ (or the backend cannot report one). Does not create or delete the key:
354
+
355
+ ```typescript title="src/flows/otp/[userId]/ttl.ts"
356
+ import { on, flow, http } from "okengine";
357
+ import { z } from "zod";
358
+ import { sessions } from "@/core";
359
+
360
+ export const ttl = on(
361
+ http.get(),
362
+ flow({
363
+ in: z.object({ userId: z.string() }),
364
+ do: async ({ userId }, fx) => {
365
+ const ttlMs = await fx.store(sessions).ttlMs(`otp:${userId}`);
366
+ return { ttlMs };
367
+ },
368
+ }),
369
+ );
370
+ ```
371
+
372
+ </Tab>
373
+
374
+ </Tabs>
375
+
376
+ ## Declare Options
377
+
378
+ Second argument to `store.kv(name, options)`:
379
+
380
+ | Option | Type | Default | Meaning |
381
+ | -------------- | --------- | ------------------------- | ----------------------------------------------------------------- |
382
+ | `description` | `string` | omitted | Console / Manifest label |
383
+ | `durable` | `true` | omitted | Persist in SQL `oke_kv` (not Redis) |
384
+ | `tenantScoped` | `boolean` | `true` when tenancy is on | Prefix keys with `{tenantId}:`; set `false` for global namespaces |
385
+
386
+ ## Durable Namespaces
387
+
388
+ <Callout title="Detailed section">
389
+ If you only need a Redis/memory cache, skip this section. `durable: true` is a different backend —
390
+ SQL table `oke_kv` on the shared store.sql connection — not a Redis persistence mode.
391
+ </Callout>
392
+
393
+ Use durable KV when values must survive Redis flushes or Compose recreate
394
+ (feature flags, compose drafts, webhook registrations). Cache namespaces stay
395
+ on `redis` / `memory`.
396
+
397
+ ```typescript title="src/core.ts"
398
+ import { store } from "okengine";
399
+
400
+ export const drafts = store.kv("drafts", {
401
+ durable: true,
402
+ description: "Compose drafts",
403
+ });
404
+ ```
405
+
406
+ ```typescript title="src/flows/drafts/[id]/save.ts"
407
+ import { on, flow, http } from "okengine";
408
+ import { z } from "zod";
409
+ import { drafts } from "@/core";
410
+
411
+ export const save = on(
412
+ http.put("/drafts/:id"),
413
+ flow({
414
+ in: z.object({
415
+ id: z.string().min(1),
416
+ title: z.string().min(1),
417
+ body: z.string().optional(),
418
+ }),
419
+ do: async ({ id, title, body }, fx) => {
420
+ await fx.store(drafts).set(id, { title, body: body ?? "" }, "7d");
421
+ return { id };
422
+ },
423
+ }),
424
+ );
425
+ ```
426
+
427
+ <Accordions>
428
+
429
+ <Accordion title="Storage model">
430
+ Rows live in engine-owned table `oke_kv` (`namespace`, `key`, `value` JSONB,
431
+ `expires_at`, `updated_at`). Schema is ensured at open — not part of
432
+ `oke db push` domain migrations.
433
+
434
+ | Fact | Detail |
435
+ | ------------------------ | ------------------------------------------------------------------------------------- |
436
+ | Connection | Shared primary SQL URL (`DATABASE_URL` / project SQL) |
437
+ | `driverId` on the handle | `postgres` or `pglite` (the SQL driver), not `redis` |
438
+ | TTL | `expires_at` filtered on `get` / `list` / `ttlMs`; expired rows purged on write paths |
439
+ | Distinct from | Flow `durable: true` (step journaling) |
440
+
441
+ </Accordion>
442
+
443
+ <Accordion title="What durable does not do">
444
+ Durable namespaces reject Lua/`eval`. Gate rate buckets and any Redis-only
445
+ atomic scripts stay on a cache (`redis` / `memory`) namespace.
446
+
447
+ ```text
448
+ oke store: durable store.kv does not support eval
449
+ ```
450
+
451
+ Without a SQL driver configured:
452
+
453
+ ```text
454
+ oke store: durable store.kv needs a configured sql driver
455
+ ```
456
+
457
+ </Accordion>
458
+
459
+ <Accordion title="When to prefer durable vs redis">
460
+
461
+ | Need | Prefer |
462
+ | ----------------------------------------------- | ---------------------------------------------- |
463
+ | Sessions, OTP, short locks, Gate rates | `redis` / `memory` cache namespace |
464
+ | Flags / drafts that must outlive Redis recreate | `durable: true` |
465
+ | Relational queries / joins | [SQL](/docs/elements/store/sql), not KV `list` |
466
+
467
+ </Accordion>
468
+
469
+ </Accordions>
470
+
471
+ ## TTL Physics
472
+
473
+ <Callout title="Detailed section">
474
+ TTL is best-effort and driver-dependent. The same `set(key, value, "30m")` call does not mean
475
+ identical expiry semantics on every backend.
476
+ </Callout>
477
+
478
+ <StoreKvTtl />
479
+
480
+ | Driver | TTL on `set(…, "30m")` |
481
+ | ---------------------- | -------------------------------------------------------------------- |
482
+ | `redis` | Applied (`SET … EX`); key expires |
483
+ | `memory` | `ttlMs` may report remaining time; **`get` does not expire** the key |
484
+ | Durable SQL (`oke_kv`) | Applied on read (`expires_at` filter) |
485
+
486
+ Valid units match `^(\d+)(ms|s|m|h|d)$`. Invalid strings parse to `0` (no
487
+ useful expiry). Prefer explicit units: `"30s"`, `"15m"`, `"1h"`, `"1d"`.
488
+
489
+ Redis converts the duration to whole seconds (`EX`); sub-second `"ms"` values
490
+ ceil to at least `1` second when TTL is set.
491
+
492
+ ## Tenant Scoping
493
+
494
+ When [`gate.auth.tenant`](/docs/elements/gate/tenancy) is on, KV namespaces
495
+ default to tenant-prefixed keys. Application code still uses logical keys —
496
+ the runtime rewrites:
497
+
498
+ | Op | Physical key (when scoped) |
499
+ | ---------------------------------- | --------------------------------------------------------- |
500
+ | `get` / `set` / `delete` / `ttlMs` | `{tenantId}:{key}` |
501
+ | `list(prefix?)` | scans `{tenantId}:{prefix…}`; strips the prefix on return |
502
+
503
+ ```typescript
504
+ // With tenancy on and fx.tenant.id = "acme":
505
+ await fx.store(sessions).set("session:abc", "u1", "1h");
506
+ // Physical redis key: oke:kv:sessions:acme:session:abc
507
+ ```
508
+
509
+ Opt out for genuinely global namespaces:
510
+
511
+ ```typescript
512
+ export const catalog = store.kv("catalog", { tenantScoped: false });
513
+ ```
514
+
515
+ Missing tenant on a scoped op throws **OKE1810** (`TENANT_REQUIRED`):
516
+
517
+ ```text
518
+ This operation needs a tenant, but none is resolved for this request.
519
+ ```
520
+
521
+ Fix: switch tenant (`fx.auth.switchTenant`), send a signed `tid` claim, or pass
522
+ the tenant header — see [Tenancy](/docs/elements/gate/tenancy).
523
+
524
+ ## Key Prefixes & Effects
525
+
526
+ Two prefix layers sit under the logical key you pass to `fx.store`:
527
+
528
+ | Layer | Shape | Who sees it |
529
+ | -------------------- | ---------------------------------------------------------------------- | ---------------------------- |
530
+ | Tenant (when scoped) | `{tenantId}:` | Stripped from `list` results |
531
+ | Driver | Redis `oke:kv:{ns}:` · memory `{ns}:` · durable SQL `namespace` column | Console / Redis tools |
532
+
533
+ Compiler effects stamp `kv:name` on reads (`get` · `list` · `ttlMs`) and
534
+ writes (`set` · `delete`). Declare the same refs when you hand-write
535
+ `effects` on a Flow.
536
+
537
+ ## Drivers
538
+
539
+ | Driver | Runs as | Best for |
540
+ | -------------------------------------------- | -------------------------------- | ---------------------------------- |
541
+ | `redis` | Docker Redis / Valkey-compatible | Dev + prod default |
542
+ | `memory` | Process map | Test default |
543
+ | Durable (`postgres` / `pglite` via `oke_kv`) | Shared SQL URL | Namespaces that must outlive Redis |
544
+
545
+ Defaults: `redis` / `memory` / `redis` (dev / test / prod). Pin overrides in
546
+ `oke.config.ts`; image pins stay under `images.store.kv` — the driver id stays
547
+ `redis` for Valkey / Dragonfly / Upstash wire-compatible servers.
548
+
549
+ Gate rate strategies inherit `drivers.store.kv` (no separate `drivers.gate`).
550
+
551
+ ## Troubleshooting
552
+
553
+ <Accordions>
554
+
555
+ <Accordion title="No kv driver configured">
556
+ Boot needs `drivers.store.kv` (or `DRIVER_DEFAULTS`). Check `REDIS_URL` when the id is `redis`.
557
+ </Accordion>
558
+
559
+ <Accordion title="oke store: durable store.kv needs a configured sql driver">
560
+ `durable: true` opens `oke_kv` on the SQL connection. Configure `drivers.store.sql` and
561
+ `DATABASE_URL` (or the project SQL URL).
562
+ </Accordion>
563
+
564
+ <Accordion title="oke store: durable store.kv does not support eval">
565
+ Durable namespaces reject Lua/`eval`. Keep rate-limit buckets on a Redis namespace; use durable KV
566
+ for flags and similar durable maps.
567
+ </Accordion>
568
+
569
+ <Accordion title="Unknown kv ref">
570
+ Cause: `Unknown kv ref: …`. The declaration was never imported. Export `store.kv(…)` from a module
571
+ the app loads before Flows run (starters: `@/core`).
572
+ </Accordion>
573
+
574
+ <Accordion title="TTL set but key still readable (memory)">
575
+ Expected on the `memory` driver — see [TTL Physics](#ttl-physics). Use `redis` in Docker when
576
+ expiry must be enforced on read.
577
+ </Accordion>
578
+
579
+ <Accordion title='I passed { ttl: "15m" } and it did not typecheck'>
580
+ Third argument is a bare string: `set(key, value, "15m")`. There is no options bag on `set`.
581
+ </Accordion>
582
+
583
+ <Accordion title="OKE1810 — TENANT_REQUIRED on kv ops">
584
+ Cause: `This operation needs a tenant, but none is resolved for this request.` Tenancy is on and
585
+ the namespace is tenant-scoped. Resolve `fx.tenant.id`, or set `tenantScoped: false` for global
586
+ keys.
587
+ </Accordion>
588
+
589
+ <Accordion title="redis kv.list: client lacks SCAN">
590
+ Browse refused rather than `KEYS *`. Use a Redis client that supports `SCAN` (Bun.RedisClient
591
+ does). Prefer prefix filters in admin Flows.
592
+ </Accordion>
593
+
594
+ <Accordion title="I expected incr / setNx / fx.store.kv">
595
+ Those helpers are not on the public handle. Use `get` / `set` / `delete` / `list` / `ttlMs` via
596
+ `fx.store(decl)`. Gate rates use Redis Lua internally.
597
+ </Accordion>
598
+
599
+ </Accordions>
600
+
601
+ ## Learn more
602
+
603
+ - [Store](/docs/elements/store) — four facets; driver defaults
604
+ - [SQL](/docs/elements/store/sql) — durable KV shares the SQL driver
605
+ - [Gate · Tenancy](/docs/elements/gate/tenancy) — `tenantScoped` and `fx.tenant.id`
606
+ - [Gate](/docs/elements/gate) — rate buckets use Redis internally
607
+ - [fx](/docs/reference/fx) — `fx.store(decl)`
608
+ - [Configuration](/docs/reference/configuration) — `drivers.store.kv`
609
+ - [Errors](/docs/reference/errors) — OKE1810
610
+
611
+ ## Next
612
+
613
+ <Cards>
614
+ <Card
615
+ title="Files"
616
+ description="Object buckets, putImage variants, and image pipelines."
617
+ href="/docs/elements/store/files"
618
+ />
619
+ <Card
620
+ title="SQL"
621
+ description="Relational tables and store.resource."
622
+ href="/docs/elements/store/sql"
623
+ />
624
+ <Card
625
+ title="Store Overview"
626
+ description="SQL · KV · files · index — one handle."
627
+ href="/docs/elements/store"
628
+ />
629
+ </Cards>
@@ -0,0 +1,5 @@
1
+ {
2
+ "title": "Store",
3
+ "icon": "Database",
4
+ "pages": ["index", "sql", "kv", "files", "search"]
5
+ }