okengine 0.18.5 → 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 (510) 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/llms-txt.mdx +1 -1
  5. package/site/content/docs/ai/meta.json +1 -1
  6. package/site/content/docs/ai/skills.mdx +4 -1
  7. package/site/content/docs/ai/try-it.mdx +58 -0
  8. package/site/content/docs/client/auth.mdx +208 -0
  9. package/site/content/docs/client/calling.mdx +451 -0
  10. package/site/content/docs/client/index.mdx +209 -0
  11. package/site/content/docs/client/live.mdx +234 -0
  12. package/site/content/docs/client/meta.json +5 -0
  13. package/site/content/docs/client/react.mdx +249 -0
  14. package/site/content/docs/elements/ai/agents.mdx +232 -0
  15. package/site/content/docs/elements/ai/index.mdx +341 -0
  16. package/site/content/docs/elements/ai/mcp.mdx +275 -0
  17. package/site/content/docs/elements/ai/meta.json +5 -0
  18. package/site/content/docs/elements/ai/models.mdx +283 -0
  19. package/site/content/docs/elements/ai/prompts.mdx +256 -0
  20. package/site/content/docs/elements/channel/email.mdx +266 -0
  21. package/site/content/docs/elements/channel/index.mdx +344 -0
  22. package/site/content/docs/elements/channel/meta.json +5 -0
  23. package/site/content/docs/elements/channel/push.mdx +218 -0
  24. package/site/content/docs/elements/channel/receipts.mdx +206 -0
  25. package/site/content/docs/elements/channel/sms.mdx +264 -0
  26. package/site/content/docs/elements/channel/whatsapp.mdx +220 -0
  27. package/site/content/docs/elements/clock/index.mdx +300 -0
  28. package/site/content/docs/elements/clock/meta.json +5 -0
  29. package/site/content/docs/elements/clock/schedules.mdx +508 -0
  30. package/site/content/docs/elements/clock/sleep.mdx +445 -0
  31. package/site/content/docs/elements/flow/consumers.mdx +707 -0
  32. package/site/content/docs/elements/flow/http.mdx +1080 -0
  33. package/site/content/docs/elements/flow/index.mdx +618 -0
  34. package/site/content/docs/elements/flow/meta.json +5 -0
  35. package/site/content/docs/elements/flow/routing.mdx +559 -0
  36. package/site/content/docs/elements/flow/workflows.mdx +640 -0
  37. package/site/content/docs/elements/gate/auth.mdx +371 -0
  38. package/site/content/docs/elements/gate/authorization.mdx +298 -0
  39. package/site/content/docs/elements/gate/index.mdx +418 -0
  40. package/site/content/docs/elements/gate/meta.json +5 -0
  41. package/site/content/docs/elements/gate/rate-limits.mdx +365 -0
  42. package/site/content/docs/elements/gate/rls.mdx +303 -0
  43. package/site/content/docs/elements/gate/tenancy.mdx +372 -0
  44. package/site/content/docs/elements/index.mdx +20 -10
  45. package/site/content/docs/elements/signal/broadcast.mdx +517 -0
  46. package/site/content/docs/elements/signal/index.mdx +352 -0
  47. package/site/content/docs/elements/signal/live.mdx +590 -0
  48. package/site/content/docs/elements/signal/meta.json +5 -0
  49. package/site/content/docs/elements/signal/once.mdx +596 -0
  50. package/site/content/docs/elements/store/files.mdx +659 -0
  51. package/site/content/docs/elements/store/index.mdx +310 -0
  52. package/site/content/docs/elements/store/kv.mdx +629 -0
  53. package/site/content/docs/elements/store/meta.json +5 -0
  54. package/site/content/docs/elements/store/search.mdx +946 -0
  55. package/site/content/docs/elements/store/sql.mdx +937 -0
  56. package/site/content/docs/elements/vault/config.mdx +256 -0
  57. package/site/content/docs/elements/vault/index.mdx +356 -0
  58. package/site/content/docs/elements/vault/meta.json +5 -0
  59. package/site/content/docs/elements/vault/rotation.mdx +273 -0
  60. package/site/content/docs/elements/vault/secrets.mdx +285 -0
  61. package/site/content/docs/index.mdx +63 -36
  62. package/site/content/docs/meta.json +8 -7
  63. package/site/content/docs/plugins/cors.mdx +2 -1
  64. package/site/content/docs/plugins/csrf.mdx +4 -1
  65. package/site/content/docs/plugins/headers.mdx +2 -1
  66. package/site/content/docs/plugins/ip-allowlist.mdx +2 -1
  67. package/site/content/docs/plugins/magic-link.mdx +6 -0
  68. package/site/content/docs/plugins/maintenance-mode.mdx +2 -1
  69. package/site/content/docs/plugins/oauth.mdx +1 -1
  70. package/site/content/docs/plugins/otp.mdx +12 -1
  71. package/site/content/docs/plugins/passkey.mdx +39 -15
  72. package/site/content/docs/plugins/two-factor.mdx +91 -34
  73. package/site/content/docs/plugins/username.mdx +1 -1
  74. package/site/content/docs/recipes/caddy.mdx +3 -3
  75. package/site/content/docs/recipes/index.mdx +18 -28
  76. package/site/content/docs/recipes/mailpit.mdx +1 -1
  77. package/site/content/docs/recipes/meilisearch.mdx +3 -3
  78. package/site/content/docs/recipes/meta.json +1 -4
  79. package/site/content/docs/recipes/nginx.mdx +2 -2
  80. package/site/content/docs/recipes/openrouter.mdx +283 -0
  81. package/site/content/docs/recipes/pgdog.mdx +3 -3
  82. package/site/content/docs/recipes/postgres.mdx +1 -2
  83. package/site/content/docs/recipes/rustfs.mdx +2 -2
  84. package/site/content/docs/recipes/traefik.mdx +3 -3
  85. package/site/content/docs/reference/cli.mdx +231 -0
  86. package/site/content/docs/reference/configuration.mdx +30 -10
  87. package/site/content/docs/reference/environment-variables.mdx +93 -20
  88. package/site/content/docs/reference/errors.mdx +132 -29
  89. package/site/content/docs/reference/fx.mdx +137 -73
  90. package/site/content/docs/reference/index.mdx +13 -21
  91. package/site/content/docs/reference/meta.json +4 -5
  92. package/site/content/docs/reference/okid.mdx +42 -17
  93. package/site/content/docs/reference/plugins.mdx +4 -3
  94. package/site/content/docs/reference/security.mdx +197 -0
  95. package/site/content/docs/understand/meta.json +5 -0
  96. package/site/content/docs/understand/the-anatomy.mdx +132 -0
  97. package/site/content/docs/understand/the-model.mdx +32 -0
  98. package/site/content/docs/understand/the-problem.mdx +74 -0
  99. package/site/content/docs/understand/the-vocabulary.mdx +26 -0
  100. package/src/auth/api-keys.ts +2 -1
  101. package/src/auth/bindings.ts +64 -16
  102. package/src/auth/gate-auth.test.ts +6 -1
  103. package/src/auth/identity.ts +152 -5
  104. package/src/auth/index.ts +29 -0
  105. package/src/auth/invites.ts +2 -1
  106. package/src/auth/method-context.ts +41 -0
  107. package/src/auth/oauth-as/crypto.ts +2 -1
  108. package/src/auth/oauth-as/stores.ts +2 -1
  109. package/src/auth/operator.ts +2 -1
  110. package/src/auth/sessions-jwt.test.ts +77 -0
  111. package/src/auth/sessions.ts +20 -4
  112. package/src/auth/tenants.ts +3 -2
  113. package/src/auth/two-factor-challenge.test.ts +61 -0
  114. package/src/auth/two-factor-challenge.ts +284 -0
  115. package/src/auth/verification.ts +5 -0
  116. package/src/bench/REPORT.md +49 -0
  117. package/src/bench/g03-signal-once.bench.ts +1 -1
  118. package/src/bench/g10-observability-contention.bench.ts +1 -1
  119. package/src/bench/g17-hybrid-search.bench.ts +572 -0
  120. package/src/bench/load-app.ts +2 -10
  121. package/src/cli/ai-setup/ai-setup.test.ts +331 -42
  122. package/src/cli/ai-setup/apply.ts +294 -52
  123. package/src/cli/ai-setup/catalog.ts +238 -1342
  124. package/src/cli/ai-setup/index.ts +14 -99
  125. package/src/cli/ai-setup/prompts.ts +60 -443
  126. package/src/cli/ask-seed.test.ts +96 -0
  127. package/src/cli/ask-seed.ts +82 -0
  128. package/src/cli/ask-vault-gaps.test.ts +84 -0
  129. package/src/cli/ask-vault-gaps.ts +146 -0
  130. package/src/cli/build.ts +2 -2
  131. package/src/cli/client-add.test.ts +26 -1
  132. package/src/cli/client-add.ts +106 -27
  133. package/src/cli/db-seed.ts +2 -0
  134. package/src/cli/db.ts +150 -11
  135. package/src/cli/dev.ts +119 -211
  136. package/src/cli/docker-clean.ts +2 -2
  137. package/src/cli/docker-cli.test.ts +1 -1
  138. package/src/cli/doctor-diff.ts +4 -2
  139. package/src/cli/doctor-pii.test.ts +1 -1
  140. package/src/cli/doctor.ts +14 -1
  141. package/src/cli/hero-meta.test.ts +4 -2
  142. package/src/cli/load-config.images.test.ts +8 -10
  143. package/src/cli/load-config.ts +1 -1
  144. package/src/cli/registry.ts +22 -10
  145. package/src/cli/replay.ts +3 -1
  146. package/src/cli/start.ts +1 -1
  147. package/src/cli/tui/keys.ts +2 -3
  148. package/src/client/auth/cookies.ts +74 -0
  149. package/src/client/auth/create-auth-client.ts +636 -0
  150. package/src/client/auth/denials.ts +99 -0
  151. package/src/client/auth/session.ts +283 -0
  152. package/src/client/auth.test.ts +251 -0
  153. package/src/client/auth.ts +39 -114
  154. package/src/client/create-with-session.ts +245 -0
  155. package/src/client/create.ts +51 -5
  156. package/src/client/index.ts +11 -1
  157. package/src/client/live.ts +11 -79
  158. package/src/client/notes-contract.test.ts +5 -10
  159. package/src/client/sse.ts +137 -0
  160. package/src/client/stream.ts +147 -0
  161. package/src/client/transport.test.ts +32 -0
  162. package/src/client/transport.ts +123 -26
  163. package/src/client/types.ts +118 -14
  164. package/src/client-react/index.ts +185 -24
  165. package/src/client-react/use-live-query.ts +13 -3
  166. package/src/compiler/aot.test.ts +7 -4
  167. package/src/compiler/effects-embed.test.ts +56 -0
  168. package/src/compiler/effects-fetch.test.ts +43 -0
  169. package/src/compiler/effects-infer.ts +58 -2
  170. package/src/compiler/extract.test.ts +257 -43
  171. package/src/compiler/extract.ts +566 -48
  172. package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -4
  173. package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +2 -8
  174. package/src/compiler/fixtures/skyport.expected.json +4 -4
  175. package/src/compiler/fixtures/triggers/five-triggers.ts +14 -10
  176. package/src/compiler/response.ts +2 -2
  177. package/src/compiler/search-writer-isolation.test.ts +40 -0
  178. package/src/config/index.ts +11 -0
  179. package/src/console/server/app.ts +17 -23
  180. package/src/console/server/bind.ts +4 -0
  181. package/src/console/server/console.test.ts +2 -2
  182. package/src/console/server/flows-invoke.test.ts +50 -34
  183. package/src/console/server/flows.ts +436 -255
  184. package/src/console/server/runs-ingest.test.ts +6 -3
  185. package/src/console/server/serve.ts +1 -1
  186. package/src/console/server/signals.test.ts +1 -5
  187. package/src/console/server/signals.ts +8 -6
  188. package/src/console/server/state.ts +6 -1
  189. package/src/console/server/store.ts +1 -1
  190. package/src/console/ui-next/dist/assets/FileExportIcon-Ck-5od4R.js +1 -0
  191. package/src/console/ui-next/dist/assets/MoreHorizontalCircle01Icon-gMNGsE37.js +1 -0
  192. package/src/console/ui-next/dist/assets/PlusSignIcon-CwG3nxfu.js +1 -0
  193. package/src/console/ui-next/dist/assets/UnavailableIcon-D9cvHVPr.js +1 -0
  194. package/src/console/ui-next/dist/assets/UserIcon-DaE2PB5_.js +1 -0
  195. package/src/console/ui-next/dist/assets/access-page-DFeymU07.js +4 -0
  196. package/src/console/ui-next/dist/assets/agent-disclosure-U1rdfblp.js +1 -0
  197. package/src/console/ui-next/dist/assets/cache-glyph-BeFJeqBG.js +1 -0
  198. package/src/console/ui-next/dist/assets/call-pii-button-CVAONPii.js +1 -0
  199. package/src/console/ui-next/dist/assets/collapsible-D2A6NJ-3.js +1 -0
  200. package/src/console/ui-next/dist/assets/copy-inline-button-CAYD18cr.js +1 -0
  201. package/src/console/ui-next/dist/assets/dagre.esm-B1_XeuLP.js +1 -0
  202. package/src/console/ui-next/dist/assets/detail-header-DVWNjWjg.js +1 -0
  203. package/src/console/ui-next/dist/assets/dropdown-menu-4h2LOVXM.js +1 -0
  204. package/src/console/ui-next/dist/assets/duration-tone-Cgk_h5ja.js +9 -0
  205. package/src/console/ui-next/dist/assets/element-icons-BI8cJgdh.js +1 -0
  206. package/src/console/ui-next/dist/assets/explorer-empty-CJs5A-wm.js +1 -0
  207. package/src/console/ui-next/dist/assets/flows-page-Dss7941e.js +1 -0
  208. package/src/console/ui-next/dist/assets/highlighted-json-MYZQtRnw.js +154 -0
  209. package/src/console/ui-next/dist/assets/http-method-Jrh39p7A.js +1 -0
  210. package/src/console/ui-next/dist/assets/index-D0zS5rKO.css +2 -0
  211. package/src/console/ui-next/dist/assets/index-DXP2dBIF.js +63 -0
  212. package/src/console/ui-next/dist/assets/observability-page-HvolXxTI.js +4 -0
  213. package/src/console/ui-next/dist/assets/react-dom-Ddte4I-Q.js +9 -0
  214. package/src/console/ui-next/dist/assets/replica-lag-CSh2dzrb.js +18 -0
  215. package/src/console/ui-next/dist/assets/request-meta-BatF8KrK.js +1 -0
  216. package/src/console/ui-next/dist/assets/shortcut-keys-3ILd8oGn.js +1 -0
  217. package/src/console/ui-next/dist/assets/{sql-BskegiFM.js → sql-BsFa4tDR.js} +1 -1
  218. package/src/console/ui-next/dist/assets/store-page-eiKiHnNe.js +41 -0
  219. package/src/console/ui-next/dist/assets/trace-detail-sheet-CZkMeKS-.js +2 -0
  220. package/src/console/ui-next/dist/assets/tree-expand-toggle-CW8y5A2h.js +55 -0
  221. package/src/console/ui-next/dist/assets/units-page-B_RWJrEO.js +1 -0
  222. package/src/console/ui-next/dist/assets/{use-vault-list-uk4WVboC.js → use-vault-list-CT4-gajj.js} +1 -1
  223. package/src/console/ui-next/dist/assets/vault-page-CWrg-A68.js +2 -0
  224. package/src/console/ui-next/dist/assets/xyflow-CSyC6ryz.css +1 -0
  225. package/src/console/ui-next/dist/assets/xyflow-yApv7D4e.js +7 -0
  226. package/src/console/ui-next/dist/index.html +6 -13
  227. package/src/console/ui-next/seed-invoke-host.ts +21 -18
  228. package/src/console/ui-next/src/client.ts +7 -2
  229. package/src/console/ui-next/src/features/flows/graph/element-map.ts +2 -0
  230. package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +12 -2
  231. package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +4 -0
  232. package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +8 -0
  233. package/src/console/ui-next/src/features/flows/traces/waterfall-bars.ts +4 -0
  234. package/src/console/ui-next/src/features/flows/traces/waterfall-tooltip.ts +9 -3
  235. package/src/console/ui-next/src/features/store/detail/reveal-cell.tsx +7 -11
  236. package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +2 -2
  237. package/src/console/ui-next/src/features/store/lib/fields-from-table.ts +2 -0
  238. package/src/console/ui-next/src/features/store/lib/grid-model.test.ts +25 -1
  239. package/src/console/ui-next/src/features/store/lib/grid-model.ts +36 -1
  240. package/src/console/ui-next/src/features/store/query/query-results.tsx +3 -3
  241. package/src/console/ui-next/src/features/units/detail/effects-summary.tsx +2 -0
  242. package/src/console/ui-next/src/features/units/lib/call-read-safe.ts +3 -1
  243. package/src/console/ui-next/src/features/units/lib/fields-from-schema.ts +6 -0
  244. package/src/docker/ai-model-status.test.ts +1 -12
  245. package/src/docker/ai-model-status.ts +10 -38
  246. package/src/docker/compose-up.test.ts +100 -0
  247. package/src/docker/compose-up.ts +292 -0
  248. package/src/docker/compose.ts +4 -14
  249. package/src/docker/derive.ts +9 -34
  250. package/src/docker/docker.test.ts +16 -261
  251. package/src/docker/images-config.test.ts +13 -25
  252. package/src/docker/index.ts +1 -24
  253. package/src/docker/recipes/index.ts +0 -23
  254. package/src/docker/recipes/pgdog.ts +6 -4
  255. package/src/docker/stack-id.test.ts +2 -2
  256. package/src/docker/stack-id.ts +0 -1
  257. package/src/docker/types.ts +2 -2
  258. package/src/drivers/ai-anthropic.ts +10 -0
  259. package/src/drivers/ai-openai-compatible.ts +34 -7
  260. package/src/drivers/ai-providers.test.ts +0 -107
  261. package/src/drivers/ai-stream.test.ts +0 -34
  262. package/src/drivers/ai-types.ts +17 -8
  263. package/src/drivers/channel-fcm.ts +6 -1
  264. package/src/drivers/channel-msegat.ts +6 -1
  265. package/src/drivers/channel-resend.ts +5 -1
  266. package/src/drivers/channel-smtp.ts +5 -1
  267. package/src/drivers/channel-sndr.ts +10 -1
  268. package/src/drivers/channel-taqnyat-mail.ts +5 -1
  269. package/src/drivers/channel-taqnyat-whatsapp.ts +6 -1
  270. package/src/drivers/channel-taqnyat.ts +6 -1
  271. package/src/drivers/channel-types.ts +10 -0
  272. package/src/drivers/channel-unifonic.ts +6 -1
  273. package/src/drivers/channel-wa-cloud.ts +6 -1
  274. package/src/drivers/channel-webpush.ts +6 -1
  275. package/src/drivers/external.ts +33 -0
  276. package/src/drivers/index.ts +0 -10
  277. package/src/drivers/meilisearch.ts +6 -0
  278. package/src/drivers/oauth-types.ts +4 -0
  279. package/src/drivers/postgres.test.ts +74 -3
  280. package/src/drivers/postgres.ts +175 -6
  281. package/src/drivers/signal-types.ts +5 -5
  282. package/src/drivers/types.ts +7 -0
  283. package/src/drivers/vault-types.ts +5 -0
  284. package/src/elements/ai/declare.ts +17 -4
  285. package/src/elements/ai/eval.ts +1 -1
  286. package/src/elements/ai/mcp-http.ts +1 -1
  287. package/src/elements/ai/pii.ts +1 -1
  288. package/src/elements/ai/providers.test.ts +289 -0
  289. package/src/elements/ai/providers.ts +169 -0
  290. package/src/elements/ai/runtime.ts +68 -5
  291. package/src/elements/ai/schema.ts +2 -2
  292. package/src/elements/ai.test.ts +7 -7
  293. package/src/elements/ai.ts +17 -1
  294. package/src/elements/channel/runtime.ts +105 -12
  295. package/src/elements/clock/cron-fields.test.ts +144 -0
  296. package/src/elements/clock/cron-fields.ts +185 -0
  297. package/src/elements/clock/declare.ts +247 -3
  298. package/src/elements/clock.ts +21 -1
  299. package/src/elements/index.ts +15 -0
  300. package/src/elements/signal/chaos-child.ts +1 -2
  301. package/src/elements/signal/declare.ts +65 -37
  302. package/src/elements/signal/delivery-modes.test.ts +11 -31
  303. package/src/elements/signal/dry-run-replay.test.ts +1 -7
  304. package/src/elements/signal/dry-run-write-isolation.test.ts +1 -7
  305. package/src/elements/signal/key-ordering.test.ts +6 -26
  306. package/src/elements/signal/lease-reclaim.test.ts +2 -6
  307. package/src/elements/signal/optional-emit.test.ts +6 -9
  308. package/src/elements/signal/order-lifecycle.test.ts +4 -17
  309. package/src/elements/signal/orphan-messages.test.ts +4 -15
  310. package/src/elements/signal/reconcile.test.ts +2 -2
  311. package/src/elements/signal/runtime.ts +1 -1
  312. package/src/elements/signal/schema-emit.test.ts +4 -8
  313. package/src/elements/signal.test.ts +24 -49
  314. package/src/elements/signal.ts +9 -2
  315. package/src/elements/store/domain-ddl.test.ts +1 -1
  316. package/src/elements/store/live-http.test.ts +9 -4
  317. package/src/elements/store/prepare-row.test.ts +123 -0
  318. package/src/elements/store/resource.ts +43 -12
  319. package/src/elements/store/schema-decl.ts +117 -0
  320. package/src/elements/store/search-backfill.ts +205 -0
  321. package/src/elements/store/search-bind.ts +66 -0
  322. package/src/elements/store/search-bm25.ts +78 -0
  323. package/src/elements/store/search-ddl.ts +156 -0
  324. package/src/elements/store/search-embed-flow.ts +141 -0
  325. package/src/elements/store/search-errors.ts +34 -0
  326. package/src/elements/store/search-fusion.ts +112 -0
  327. package/src/elements/store/search-lsh.ts +179 -0
  328. package/src/elements/store/search-runtime.ts +300 -0
  329. package/src/elements/store/search.test.ts +144 -0
  330. package/src/elements/store/sql-session.test.ts +1 -0
  331. package/src/elements/store/sql-session.ts +83 -3
  332. package/src/elements/store/table.ts +23 -2
  333. package/src/elements/store/upsert-app.test.ts +1 -3
  334. package/src/elements/store.ts +50 -0
  335. package/src/full.ts +4 -2
  336. package/src/http.ts +4 -1
  337. package/src/i18n/catalogs/ar.ts +14 -6
  338. package/src/i18n/catalogs/en.ts +14 -6
  339. package/src/index.ts +6 -1
  340. package/src/kernel/adopt-barrel-fresh.test.ts +7 -7
  341. package/src/kernel/adopt-routes.ts +9 -1
  342. package/src/kernel/app-auth.ts +5 -0
  343. package/src/kernel/app.ts +102 -9
  344. package/src/kernel/auto-cache.test.ts +6 -12
  345. package/src/kernel/auto-registry.test.ts +3 -3
  346. package/src/kernel/boot-bind/ai.test.ts +22 -19
  347. package/src/kernel/boot-bind/ai.ts +5 -43
  348. package/src/kernel/boot-bind/clock.ts +23 -5
  349. package/src/kernel/boot-bind/honor-config.test.ts +4 -4
  350. package/src/kernel/boot.test.ts +11 -6
  351. package/src/kernel/boot.ts +66 -16
  352. package/src/kernel/boundary-contract.ts +93 -0
  353. package/src/kernel/budget.test.ts +1 -1
  354. package/src/kernel/call.ts +89 -0
  355. package/src/kernel/capability.ts +6 -0
  356. package/src/kernel/client-descriptor.ts +123 -0
  357. package/src/kernel/clock-timezone.test.ts +80 -0
  358. package/src/kernel/correlation.test.ts +1 -1
  359. package/src/kernel/dry-run.ts +11 -8
  360. package/src/kernel/effects-stamping.test.ts +42 -9
  361. package/src/kernel/effects.test.ts +4 -2
  362. package/src/kernel/effects.ts +51 -9
  363. package/src/kernel/errors-channel.ts +17 -0
  364. package/src/kernel/errors-live-resume.ts +3 -2
  365. package/src/kernel/errors-tenant.ts +7 -4
  366. package/src/kernel/errors.registry-helpers.ts +96 -0
  367. package/src/kernel/errors.registry.test.ts +139 -25
  368. package/src/kernel/errors.ts +102 -23
  369. package/src/kernel/external-effects.test.ts +196 -0
  370. package/src/kernel/flow.test.ts +13 -4
  371. package/src/kernel/flow.ts +69 -71
  372. package/src/kernel/fx-ask-telemetry.test.ts +2 -2
  373. package/src/kernel/fx-dead-letters.test.ts +3 -3
  374. package/src/kernel/fx-fetch.ts +55 -0
  375. package/src/kernel/fx-live-stream.ts +2 -2
  376. package/src/kernel/fx-live.test.ts +8 -8
  377. package/src/kernel/fx.test.ts +17 -11
  378. package/src/kernel/fx.ts +180 -57
  379. package/src/kernel/horizontal-child.ts +1 -1
  380. package/src/kernel/http-query.test.ts +3 -6
  381. package/src/kernel/index.ts +13 -3
  382. package/src/kernel/instance-id.ts +1 -1
  383. package/src/kernel/instances.test.ts +40 -0
  384. package/src/kernel/instances.ts +47 -9
  385. package/src/kernel/live-http.test.ts +6 -7
  386. package/src/kernel/live-http.ts +6 -2
  387. package/src/kernel/live-resume.test.ts +1 -1
  388. package/src/kernel/mcp-tool.test.ts +3 -3
  389. package/src/kernel/on.ts +98 -4
  390. package/src/kernel/pipeline.test.ts +12 -15
  391. package/src/kernel/plugin-elements.test.ts +1 -1
  392. package/src/kernel/stamp-http.test.ts +3 -3
  393. package/src/kernel/triggers.ts +210 -95
  394. package/src/kernel-entry.ts +0 -1
  395. package/src/manifest/diff.ts +11 -1
  396. package/src/manifest/fixtures/skyport.excerpt.json +1 -1
  397. package/src/manifest/types.ts +46 -2
  398. package/src/mcp/docs-index.ts +3 -3
  399. package/src/mcp/docs-mcp.test.ts +2 -2
  400. package/src/mcp/docs-tools.ts +1 -1
  401. package/src/okid.test.ts +56 -0
  402. package/src/okid.ts +55 -11
  403. package/src/plugins/anonymous.ts +8 -4
  404. package/src/plugins/auth/shared.ts +27 -3
  405. package/src/plugins/auth-delivery.mailpit.integration.test.ts +1 -1
  406. package/src/plugins/auth-methods.security.test.ts +561 -23
  407. package/src/plugins/config-source.ts +1 -1
  408. package/src/plugins/csrf.test.ts +65 -0
  409. package/src/plugins/index.ts +2 -0
  410. package/src/plugins/magic-link.ts +36 -16
  411. package/src/plugins/oauth/shared.ts +3 -1
  412. package/src/plugins/oauth.ts +22 -14
  413. package/src/plugins/otp.ts +37 -21
  414. package/src/plugins/passkey-webauthn.ts +9 -6
  415. package/src/plugins/passkey.ts +98 -26
  416. package/src/plugins/pre-account-hijack.test.ts +223 -0
  417. package/src/plugins/two-factor.ts +466 -37
  418. package/src/plugins/username.ts +19 -7
  419. package/src/release/build-lib.ts +1 -0
  420. package/src/release/limits.ts +2 -2
  421. package/src/release/measure.ts +1 -1
  422. package/src/runs/collect.test.ts +0 -2
  423. package/src/runtime/json-code-block.test.ts +250 -20
  424. package/src/runtime/json-code-block.ts +1261 -41
  425. package/src/term.test.ts +1 -1
  426. package/src/test/create-test-app.test.ts +12 -12
  427. package/src/test/live-signals.test.ts +6 -6
  428. package/src/test/provisions.integration.test.ts +10 -14
  429. package/src/test/reset-element-registries.ts +1 -1
  430. package/src/test/tenant-isolation.test.ts +12 -6
  431. package/site/content/docs/deployment/docker-swarm.mdx +0 -164
  432. package/site/content/docs/deployment/docker.mdx +0 -227
  433. package/site/content/docs/deployment/index.mdx +0 -83
  434. package/site/content/docs/deployment/kubernetes.mdx +0 -176
  435. package/site/content/docs/deployment/meta.json +0 -5
  436. package/site/content/docs/deployment/reverse-proxy.mdx +0 -234
  437. package/site/content/docs/elements/ai.mdx +0 -385
  438. package/site/content/docs/elements/channel.mdx +0 -346
  439. package/site/content/docs/elements/clock.mdx +0 -244
  440. package/site/content/docs/elements/flow.mdx +0 -420
  441. package/site/content/docs/elements/gate.mdx +0 -436
  442. package/site/content/docs/elements/signal.mdx +0 -380
  443. package/site/content/docs/elements/store.mdx +0 -1099
  444. package/site/content/docs/elements/vault.mdx +0 -405
  445. package/site/content/docs/get-started/basic-usage.mdx +0 -173
  446. package/site/content/docs/get-started/index.mdx +0 -43
  447. package/site/content/docs/get-started/installation.mdx +0 -220
  448. package/site/content/docs/get-started/introduction.mdx +0 -144
  449. package/site/content/docs/get-started/meta.json +0 -13
  450. package/site/content/docs/get-started/project-structure.mdx +0 -925
  451. package/site/content/docs/get-started/testing.mdx +0 -328
  452. package/site/content/docs/get-started/why.mdx +0 -114
  453. package/site/content/docs/recipes/llama-cpp.mdx +0 -151
  454. package/site/content/docs/recipes/ollama.mdx +0 -142
  455. package/site/content/docs/recipes/sglang.mdx +0 -105
  456. package/site/content/docs/recipes/vllm.mdx +0 -106
  457. package/site/content/docs/reference/cli.md +0 -232
  458. package/site/content/docs/reference/client.mdx +0 -469
  459. package/site/content/docs/reference/security.md +0 -74
  460. package/src/cli/ai-setup/detect-ollama.ts +0 -213
  461. package/src/cli/ai-setup/recommend.test.ts +0 -225
  462. package/src/cli/ai-setup/recommend.ts +0 -225
  463. package/src/cli/dev-controls.test.ts +0 -122
  464. package/src/cli/dev-controls.ts +0 -164
  465. package/src/cli/tui/DevLive.tsx +0 -126
  466. package/src/console/ui-next/dist/assets/access-page-3-EFj-2G.js +0 -4
  467. package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +0 -1
  468. package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +0 -1
  469. package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +0 -1
  470. package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +0 -1
  471. package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +0 -1
  472. package/src/console/ui-next/dist/assets/dagre.esm-ZwcdTuZZ.js +0 -1
  473. package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +0 -1
  474. package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +0 -1
  475. package/src/console/ui-next/dist/assets/duration-tone-sC3lGABz.js +0 -9
  476. package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +0 -1
  477. package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +0 -1
  478. package/src/console/ui-next/dist/assets/flows-page-cVFnA4HH.js +0 -1
  479. package/src/console/ui-next/dist/assets/highlighted-json-Awq7gYdu.js +0 -154
  480. package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +0 -1
  481. package/src/console/ui-next/dist/assets/index-CMIgUbD0.js +0 -66
  482. package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +0 -2
  483. package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +0 -1
  484. package/src/console/ui-next/dist/assets/observability-page-CQ3p34ip.js +0 -4
  485. package/src/console/ui-next/dist/assets/preload-helper-oH4irX4C.js +0 -1
  486. package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +0 -1
  487. package/src/console/ui-next/dist/assets/react-dom-Bph1y7z7.js +0 -9
  488. package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +0 -18
  489. package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +0 -1
  490. package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +0 -1
  491. package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +0 -1
  492. package/src/console/ui-next/dist/assets/skeleton-D-czQJT6.js +0 -1
  493. package/src/console/ui-next/dist/assets/store-page-02xOiqIK.js +0 -41
  494. package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +0 -2
  495. package/src/console/ui-next/dist/assets/tree-expand-toggle-BtyhmWb4.js +0 -55
  496. package/src/console/ui-next/dist/assets/units-page-l8FeKfnP.js +0 -1
  497. package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +0 -1
  498. package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +0 -1
  499. package/src/console/ui-next/dist/assets/vault-page-CL-d_mLE.js +0 -2
  500. package/src/console/ui-next/dist/assets/xyflow-D7n4g6go.js +0 -7
  501. package/src/console/ui-next/dist/assets/xyflow-DZ0Ws1xk.css +0 -1
  502. package/src/docker/ollama-pull.ts +0 -232
  503. package/src/docker/recipes/llama-cpp.ts +0 -298
  504. package/src/docker/recipes/ollama.ts +0 -44
  505. package/src/docker/recipes/sglang.ts +0 -55
  506. package/src/docker/recipes/vllm.ts +0 -44
  507. package/src/drivers/ai-ollama-tools.integration.test.ts +0 -109
  508. package/src/drivers/ai-ollama.integration.test.ts +0 -184
  509. package/src/drivers/ai-ollama.ts +0 -389
  510. 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
+ }