okengine 0.18.5 → 0.19.1

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/llms-txt.mdx +1 -1
  5. package/site/content/docs/ai/skills.mdx +4 -1
  6. package/site/content/docs/client/auth.mdx +208 -0
  7. package/site/content/docs/client/calling.mdx +451 -0
  8. package/site/content/docs/client/index.mdx +208 -0
  9. package/site/content/docs/client/live.mdx +234 -0
  10. package/site/content/docs/client/meta.json +5 -0
  11. package/site/content/docs/client/react.mdx +249 -0
  12. package/site/content/docs/elements/ai/agents.mdx +235 -0
  13. package/site/content/docs/elements/ai/index.mdx +342 -0
  14. package/site/content/docs/elements/ai/mcp.mdx +275 -0
  15. package/site/content/docs/elements/ai/meta.json +5 -0
  16. package/site/content/docs/elements/ai/models.mdx +283 -0
  17. package/site/content/docs/elements/ai/prompts.mdx +257 -0
  18. package/site/content/docs/elements/channel/email.mdx +269 -0
  19. package/site/content/docs/elements/channel/index.mdx +344 -0
  20. package/site/content/docs/elements/channel/meta.json +5 -0
  21. package/site/content/docs/elements/channel/push.mdx +218 -0
  22. package/site/content/docs/elements/channel/receipts.mdx +206 -0
  23. package/site/content/docs/elements/channel/sms.mdx +264 -0
  24. package/site/content/docs/elements/channel/whatsapp.mdx +220 -0
  25. package/site/content/docs/elements/clock/index.mdx +301 -0
  26. package/site/content/docs/elements/clock/meta.json +5 -0
  27. package/site/content/docs/elements/clock/schedules.mdx +508 -0
  28. package/site/content/docs/elements/clock/sleep.mdx +448 -0
  29. package/site/content/docs/elements/flow/consumers.mdx +707 -0
  30. package/site/content/docs/elements/flow/http.mdx +1080 -0
  31. package/site/content/docs/elements/flow/index.mdx +618 -0
  32. package/site/content/docs/elements/flow/meta.json +5 -0
  33. package/site/content/docs/elements/flow/routing.mdx +559 -0
  34. package/site/content/docs/elements/flow/workflows.mdx +640 -0
  35. package/site/content/docs/elements/gate/auth.mdx +371 -0
  36. package/site/content/docs/elements/gate/authorization.mdx +298 -0
  37. package/site/content/docs/elements/gate/index.mdx +418 -0
  38. package/site/content/docs/elements/gate/meta.json +5 -0
  39. package/site/content/docs/elements/gate/rate-limits.mdx +365 -0
  40. package/site/content/docs/elements/gate/rls.mdx +303 -0
  41. package/site/content/docs/elements/gate/tenancy.mdx +375 -0
  42. package/site/content/docs/elements/index.mdx +20 -10
  43. package/site/content/docs/elements/signal/broadcast.mdx +519 -0
  44. package/site/content/docs/elements/signal/index.mdx +352 -0
  45. package/site/content/docs/elements/signal/live.mdx +591 -0
  46. package/site/content/docs/elements/signal/meta.json +5 -0
  47. package/site/content/docs/elements/signal/once.mdx +597 -0
  48. package/site/content/docs/elements/store/files.mdx +667 -0
  49. package/site/content/docs/elements/store/index.mdx +313 -0
  50. package/site/content/docs/elements/store/kv.mdx +637 -0
  51. package/site/content/docs/elements/store/meta.json +5 -0
  52. package/site/content/docs/elements/store/search.mdx +950 -0
  53. package/site/content/docs/elements/store/sql.mdx +941 -0
  54. package/site/content/docs/elements/vault/config.mdx +259 -0
  55. package/site/content/docs/elements/vault/index.mdx +356 -0
  56. package/site/content/docs/elements/vault/meta.json +5 -0
  57. package/site/content/docs/elements/vault/rotation.mdx +276 -0
  58. package/site/content/docs/elements/vault/secrets.mdx +288 -0
  59. package/site/content/docs/index.mdx +52 -40
  60. package/site/content/docs/meta.json +8 -7
  61. package/site/content/docs/plugins/cors.mdx +2 -1
  62. package/site/content/docs/plugins/csrf.mdx +4 -1
  63. package/site/content/docs/plugins/headers.mdx +2 -1
  64. package/site/content/docs/plugins/ip-allowlist.mdx +2 -1
  65. package/site/content/docs/plugins/magic-link.mdx +6 -0
  66. package/site/content/docs/plugins/maintenance-mode.mdx +2 -1
  67. package/site/content/docs/plugins/oauth.mdx +1 -1
  68. package/site/content/docs/plugins/otp.mdx +12 -1
  69. package/site/content/docs/plugins/passkey.mdx +39 -15
  70. package/site/content/docs/plugins/two-factor.mdx +91 -34
  71. package/site/content/docs/plugins/username.mdx +1 -1
  72. package/site/content/docs/recipes/caddy.mdx +3 -3
  73. package/site/content/docs/recipes/index.mdx +18 -28
  74. package/site/content/docs/recipes/mailpit.mdx +1 -1
  75. package/site/content/docs/recipes/meilisearch.mdx +3 -3
  76. package/site/content/docs/recipes/meta.json +1 -4
  77. package/site/content/docs/recipes/nginx.mdx +2 -2
  78. package/site/content/docs/recipes/openrouter.mdx +283 -0
  79. package/site/content/docs/recipes/pgdog.mdx +3 -3
  80. package/site/content/docs/recipes/postgres.mdx +1 -2
  81. package/site/content/docs/recipes/rustfs.mdx +2 -2
  82. package/site/content/docs/recipes/traefik.mdx +3 -3
  83. package/site/content/docs/reference/cli.mdx +231 -0
  84. package/site/content/docs/reference/configuration.mdx +30 -10
  85. package/site/content/docs/reference/environment-variables.mdx +93 -20
  86. package/site/content/docs/reference/errors.mdx +132 -29
  87. package/site/content/docs/reference/fx.mdx +138 -73
  88. package/site/content/docs/reference/index.mdx +13 -21
  89. package/site/content/docs/reference/meta.json +4 -5
  90. package/site/content/docs/reference/okid.mdx +42 -17
  91. package/site/content/docs/reference/plugins.mdx +4 -3
  92. package/site/content/docs/reference/security.mdx +197 -0
  93. package/site/content/docs/understand/meta.json +5 -0
  94. package/site/content/docs/understand/the-architecture.mdx +264 -0
  95. package/site/content/docs/understand/try-it.mdx +58 -0
  96. package/src/auth/api-keys.ts +2 -1
  97. package/src/auth/bindings.ts +64 -16
  98. package/src/auth/gate-auth.test.ts +6 -1
  99. package/src/auth/identity.ts +152 -5
  100. package/src/auth/index.ts +29 -0
  101. package/src/auth/invites.ts +2 -1
  102. package/src/auth/method-context.ts +41 -0
  103. package/src/auth/oauth-as/crypto.ts +2 -1
  104. package/src/auth/oauth-as/stores.ts +2 -1
  105. package/src/auth/operator.ts +2 -1
  106. package/src/auth/sessions-jwt.test.ts +77 -0
  107. package/src/auth/sessions.ts +20 -4
  108. package/src/auth/tenants.ts +3 -2
  109. package/src/auth/two-factor-challenge.test.ts +61 -0
  110. package/src/auth/two-factor-challenge.ts +284 -0
  111. package/src/auth/verification.ts +5 -0
  112. package/src/bench/REPORT.md +80 -0
  113. package/src/bench/g03-signal-once.bench.ts +1 -1
  114. package/src/bench/g10-observability-contention.bench.ts +1 -1
  115. package/src/bench/g17-hybrid-search.bench.ts +572 -0
  116. package/src/bench/load-app.ts +2 -10
  117. package/src/cli/ai-setup/ai-setup.test.ts +331 -42
  118. package/src/cli/ai-setup/apply.ts +294 -52
  119. package/src/cli/ai-setup/catalog.ts +238 -1342
  120. package/src/cli/ai-setup/index.ts +14 -99
  121. package/src/cli/ai-setup/prompts.ts +60 -443
  122. package/src/cli/ask-seed.test.ts +96 -0
  123. package/src/cli/ask-seed.ts +82 -0
  124. package/src/cli/ask-vault-gaps.test.ts +84 -0
  125. package/src/cli/ask-vault-gaps.ts +146 -0
  126. package/src/cli/build.ts +2 -2
  127. package/src/cli/client-add.test.ts +26 -1
  128. package/src/cli/client-add.ts +106 -27
  129. package/src/cli/db-seed.ts +2 -0
  130. package/src/cli/db.ts +150 -11
  131. package/src/cli/dev.ts +119 -211
  132. package/src/cli/docker-clean.ts +2 -2
  133. package/src/cli/docker-cli.test.ts +1 -1
  134. package/src/cli/doctor-diff.ts +4 -2
  135. package/src/cli/doctor-pii.test.ts +1 -1
  136. package/src/cli/doctor.ts +14 -1
  137. package/src/cli/hero-meta.test.ts +4 -2
  138. package/src/cli/load-config.images.test.ts +8 -10
  139. package/src/cli/load-config.ts +1 -1
  140. package/src/cli/registry.ts +22 -10
  141. package/src/cli/replay.ts +3 -1
  142. package/src/cli/start.ts +1 -1
  143. package/src/cli/tui/keys.ts +2 -3
  144. package/src/client/auth/cookies.ts +74 -0
  145. package/src/client/auth/create-auth-client.ts +636 -0
  146. package/src/client/auth/denials.ts +99 -0
  147. package/src/client/auth/session.ts +283 -0
  148. package/src/client/auth.test.ts +251 -0
  149. package/src/client/auth.ts +39 -114
  150. package/src/client/create-with-session.ts +245 -0
  151. package/src/client/create.ts +51 -5
  152. package/src/client/index.ts +11 -1
  153. package/src/client/live.ts +11 -79
  154. package/src/client/notes-contract.test.ts +5 -10
  155. package/src/client/sse.ts +137 -0
  156. package/src/client/stream.ts +147 -0
  157. package/src/client/transport.test.ts +32 -0
  158. package/src/client/transport.ts +123 -26
  159. package/src/client/types.ts +118 -14
  160. package/src/client-react/index.ts +185 -24
  161. package/src/client-react/use-live-query.ts +13 -3
  162. package/src/compiler/aot.test.ts +7 -4
  163. package/src/compiler/effects-embed.test.ts +56 -0
  164. package/src/compiler/effects-fetch.test.ts +43 -0
  165. package/src/compiler/effects-infer.ts +58 -2
  166. package/src/compiler/extract.test.ts +257 -43
  167. package/src/compiler/extract.ts +566 -48
  168. package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -4
  169. package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +2 -8
  170. package/src/compiler/fixtures/skyport.expected.json +4 -4
  171. package/src/compiler/fixtures/triggers/five-triggers.ts +14 -10
  172. package/src/compiler/response.ts +2 -2
  173. package/src/compiler/search-writer-isolation.test.ts +40 -0
  174. package/src/config/index.ts +11 -0
  175. package/src/console/server/app.ts +17 -23
  176. package/src/console/server/bind.ts +4 -0
  177. package/src/console/server/console.test.ts +2 -2
  178. package/src/console/server/flows-invoke.test.ts +50 -34
  179. package/src/console/server/flows.ts +436 -255
  180. package/src/console/server/runs-ingest.test.ts +6 -3
  181. package/src/console/server/serve.ts +1 -1
  182. package/src/console/server/signals.test.ts +1 -5
  183. package/src/console/server/signals.ts +8 -6
  184. package/src/console/server/state.ts +6 -1
  185. package/src/console/server/store.ts +1 -1
  186. package/src/console/ui-next/dist/assets/FileExportIcon-Ck-5od4R.js +1 -0
  187. package/src/console/ui-next/dist/assets/MoreHorizontalCircle01Icon-gMNGsE37.js +1 -0
  188. package/src/console/ui-next/dist/assets/PlusSignIcon-CwG3nxfu.js +1 -0
  189. package/src/console/ui-next/dist/assets/UnavailableIcon-D9cvHVPr.js +1 -0
  190. package/src/console/ui-next/dist/assets/UserIcon-DaE2PB5_.js +1 -0
  191. package/src/console/ui-next/dist/assets/access-page-C_qLDhTq.js +4 -0
  192. package/src/console/ui-next/dist/assets/agent-disclosure-BHVqr3TN.js +1 -0
  193. package/src/console/ui-next/dist/assets/cache-glyph-CKe92lRQ.js +1 -0
  194. package/src/console/ui-next/dist/assets/call-pii-button-DEwTl8ZX.js +1 -0
  195. package/src/console/ui-next/dist/assets/collapsible-BCBtDrCt.js +1 -0
  196. package/src/console/ui-next/dist/assets/copy-inline-button-CAYD18cr.js +1 -0
  197. package/src/console/ui-next/dist/assets/dagre.esm-B1_XeuLP.js +1 -0
  198. package/src/console/ui-next/dist/assets/detail-header-DVWNjWjg.js +1 -0
  199. package/src/console/ui-next/dist/assets/dropdown-menu-4h2LOVXM.js +1 -0
  200. package/src/console/ui-next/dist/assets/duration-tone-JroqeuCp.js +9 -0
  201. package/src/console/ui-next/dist/assets/element-icons-BI8cJgdh.js +1 -0
  202. package/src/console/ui-next/dist/assets/explorer-empty-CJs5A-wm.js +1 -0
  203. package/src/console/ui-next/dist/assets/flows-page-CVHa0RTt.js +1 -0
  204. package/src/console/ui-next/dist/assets/highlighted-json-DjJW6hqe.js +154 -0
  205. package/src/console/ui-next/dist/assets/http-method-DC5HBdLU.js +1 -0
  206. package/src/console/ui-next/dist/assets/index-CYjiZ3WO.js +63 -0
  207. package/src/console/ui-next/dist/assets/index-D0zS5rKO.css +2 -0
  208. package/src/console/ui-next/dist/assets/observability-page-CAYMyKb3.js +4 -0
  209. package/src/console/ui-next/dist/assets/react-dom-Ddte4I-Q.js +9 -0
  210. package/src/console/ui-next/dist/assets/replica-lag-yAQYLv75.js +18 -0
  211. package/src/console/ui-next/dist/assets/request-meta-D0yusGxJ.js +1 -0
  212. package/src/console/ui-next/dist/assets/shortcut-keys-3ILd8oGn.js +1 -0
  213. package/src/console/ui-next/dist/assets/{sql-BskegiFM.js → sql-BsFa4tDR.js} +1 -1
  214. package/src/console/ui-next/dist/assets/store-page-BTKJeJ02.js +41 -0
  215. package/src/console/ui-next/dist/assets/trace-detail-sheet-Bp-Yygs5.js +2 -0
  216. package/src/console/ui-next/dist/assets/tree-expand-toggle-DoaVDfAM.js +55 -0
  217. package/src/console/ui-next/dist/assets/units-page-BRz7xyYL.js +1 -0
  218. package/src/console/ui-next/dist/assets/{use-vault-list-uk4WVboC.js → use-vault-list-CT4-gajj.js} +1 -1
  219. package/src/console/ui-next/dist/assets/vault-page-3jQt-bOJ.js +2 -0
  220. package/src/console/ui-next/dist/assets/xyflow-CSyC6ryz.css +1 -0
  221. package/src/console/ui-next/dist/assets/xyflow-yApv7D4e.js +7 -0
  222. package/src/console/ui-next/dist/index.html +6 -13
  223. package/src/console/ui-next/seed-invoke-host.ts +21 -18
  224. package/src/console/ui-next/src/client.ts +7 -2
  225. package/src/console/ui-next/src/features/flows/graph/element-map.test.ts +1 -1
  226. package/src/console/ui-next/src/features/flows/graph/element-map.ts +5 -3
  227. package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +12 -2
  228. package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +4 -0
  229. package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +8 -0
  230. package/src/console/ui-next/src/features/flows/traces/trace-detail.test.ts +4 -5
  231. package/src/console/ui-next/src/features/flows/traces/trace-gates.ts +3 -6
  232. package/src/console/ui-next/src/features/flows/traces/waterfall-bars.ts +4 -0
  233. package/src/console/ui-next/src/features/flows/traces/waterfall-tooltip.ts +9 -3
  234. package/src/console/ui-next/src/features/store/detail/reveal-cell.tsx +7 -11
  235. package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +2 -2
  236. package/src/console/ui-next/src/features/store/lib/fields-from-table.ts +2 -0
  237. package/src/console/ui-next/src/features/store/lib/grid-model.test.ts +25 -1
  238. package/src/console/ui-next/src/features/store/lib/grid-model.ts +36 -1
  239. package/src/console/ui-next/src/features/store/query/query-results.tsx +3 -3
  240. package/src/console/ui-next/src/features/units/detail/effects-summary.tsx +2 -0
  241. package/src/console/ui-next/src/features/units/lib/call-read-safe.ts +3 -1
  242. package/src/console/ui-next/src/features/units/lib/fields-from-schema.ts +6 -0
  243. package/src/docker/ai-model-status.test.ts +1 -12
  244. package/src/docker/ai-model-status.ts +10 -38
  245. package/src/docker/compose-up.test.ts +100 -0
  246. package/src/docker/compose-up.ts +292 -0
  247. package/src/docker/compose.ts +4 -14
  248. package/src/docker/derive.ts +9 -34
  249. package/src/docker/docker.test.ts +16 -261
  250. package/src/docker/images-config.test.ts +13 -25
  251. package/src/docker/index.ts +1 -24
  252. package/src/docker/recipes/index.ts +0 -23
  253. package/src/docker/recipes/pgdog.ts +6 -4
  254. package/src/docker/stack-id.test.ts +2 -2
  255. package/src/docker/stack-id.ts +0 -1
  256. package/src/docker/types.ts +2 -2
  257. package/src/drivers/ai-anthropic.ts +10 -0
  258. package/src/drivers/ai-openai-compatible.ts +34 -7
  259. package/src/drivers/ai-providers.test.ts +0 -107
  260. package/src/drivers/ai-stream.test.ts +0 -34
  261. package/src/drivers/ai-types.ts +17 -8
  262. package/src/drivers/channel-fcm.ts +6 -1
  263. package/src/drivers/channel-msegat.ts +6 -1
  264. package/src/drivers/channel-resend.ts +5 -1
  265. package/src/drivers/channel-smtp.ts +5 -1
  266. package/src/drivers/channel-sndr.ts +10 -1
  267. package/src/drivers/channel-taqnyat-mail.ts +5 -1
  268. package/src/drivers/channel-taqnyat-whatsapp.ts +6 -1
  269. package/src/drivers/channel-taqnyat.ts +6 -1
  270. package/src/drivers/channel-types.ts +10 -0
  271. package/src/drivers/channel-unifonic.ts +6 -1
  272. package/src/drivers/channel-wa-cloud.ts +6 -1
  273. package/src/drivers/channel-webpush.ts +6 -1
  274. package/src/drivers/external.ts +33 -0
  275. package/src/drivers/index.ts +0 -10
  276. package/src/drivers/meilisearch.ts +6 -0
  277. package/src/drivers/oauth-types.ts +4 -0
  278. package/src/drivers/postgres.test.ts +74 -3
  279. package/src/drivers/postgres.ts +175 -6
  280. package/src/drivers/signal-types.ts +5 -5
  281. package/src/drivers/types.ts +7 -0
  282. package/src/drivers/vault-types.ts +5 -0
  283. package/src/elements/ai/declare.ts +17 -4
  284. package/src/elements/ai/eval.ts +1 -1
  285. package/src/elements/ai/mcp-http.ts +1 -1
  286. package/src/elements/ai/pii.ts +1 -1
  287. package/src/elements/ai/providers.test.ts +289 -0
  288. package/src/elements/ai/providers.ts +169 -0
  289. package/src/elements/ai/runtime.ts +68 -5
  290. package/src/elements/ai/schema.ts +2 -2
  291. package/src/elements/ai.test.ts +7 -7
  292. package/src/elements/ai.ts +17 -1
  293. package/src/elements/channel/runtime.ts +105 -12
  294. package/src/elements/clock/cron-fields.test.ts +144 -0
  295. package/src/elements/clock/cron-fields.ts +185 -0
  296. package/src/elements/clock/declare.ts +247 -3
  297. package/src/elements/clock.ts +21 -1
  298. package/src/elements/gate/declare.ts +1 -1
  299. package/src/elements/gate.ts +1 -1
  300. package/src/elements/index.ts +15 -0
  301. package/src/elements/signal/chaos-child.ts +1 -2
  302. package/src/elements/signal/declare.ts +65 -37
  303. package/src/elements/signal/delivery-modes.test.ts +11 -31
  304. package/src/elements/signal/dry-run-replay.test.ts +1 -7
  305. package/src/elements/signal/dry-run-write-isolation.test.ts +1 -7
  306. package/src/elements/signal/key-ordering.test.ts +6 -26
  307. package/src/elements/signal/lease-reclaim.test.ts +2 -6
  308. package/src/elements/signal/optional-emit.test.ts +6 -9
  309. package/src/elements/signal/order-lifecycle.test.ts +4 -17
  310. package/src/elements/signal/orphan-messages.test.ts +4 -15
  311. package/src/elements/signal/reconcile.test.ts +2 -2
  312. package/src/elements/signal/runtime.ts +1 -1
  313. package/src/elements/signal/schema-emit.test.ts +4 -8
  314. package/src/elements/signal.test.ts +24 -49
  315. package/src/elements/signal.ts +9 -2
  316. package/src/elements/store/domain-ddl.test.ts +1 -1
  317. package/src/elements/store/live-http.test.ts +9 -4
  318. package/src/elements/store/prepare-row.test.ts +123 -0
  319. package/src/elements/store/resource.ts +43 -12
  320. package/src/elements/store/schema-decl.ts +117 -0
  321. package/src/elements/store/search-backfill.ts +205 -0
  322. package/src/elements/store/search-bind.ts +66 -0
  323. package/src/elements/store/search-bm25.ts +78 -0
  324. package/src/elements/store/search-ddl.ts +156 -0
  325. package/src/elements/store/search-embed-flow.ts +141 -0
  326. package/src/elements/store/search-errors.ts +34 -0
  327. package/src/elements/store/search-fusion.ts +112 -0
  328. package/src/elements/store/search-lsh.ts +214 -0
  329. package/src/elements/store/search-runtime.pglite.test.ts +186 -0
  330. package/src/elements/store/search-runtime.ts +314 -0
  331. package/src/elements/store/search.test.ts +226 -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 +52 -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 +2 -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 -144
  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 -114
  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-3-EFj-2G.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-cVFnA4HH.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-CMIgUbD0.js +0 -66
  484. package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +0 -2
  485. package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +0 -1
  486. package/src/console/ui-next/dist/assets/observability-page-CQ3p34ip.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-l8FeKfnP.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-CL-d_mLE.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,197 @@
1
+ ---
2
+ title: "Security"
3
+ description: "Host and Origin allow-lists, user vs operator planes, Console and MCP posture."
4
+ icon: "Shield"
5
+ source: "docs/spec/unified-theory.md"
6
+ ---
7
+
8
+ Every served request on the backend, Console, and app MCP passes Host / Origin checks
9
+ before your Flow runs. Private bind addresses are not a substitute for that check.
10
+
11
+ Reach for this page when you put a public hostname in front of the app, open Console
12
+ in production, or wire MCP tokens.
13
+
14
+ <Callout title="The one rule">
15
+ Pass every public hostname in `allowedHosts` on `createBunRuntime().serve` (and the Console / MCP
16
+ serve options). Loopback is always allowed; `Origin: null` is always rejected. Failures return
17
+ **403**.
18
+ </Callout>
19
+
20
+ ## Smallest Example
21
+
22
+ <Steps>
23
+
24
+ <Step>
25
+ ### Serve with an explicit host allow-list
26
+
27
+ ```typescript title="src/app.ts"
28
+ import { createBunRuntime } from "okengine/http";
29
+ import { app } from "./app";
30
+
31
+ createBunRuntime().serve(app, {
32
+ port: Number(process.env.PORT ?? 6530),
33
+ hostname: "0.0.0.0",
34
+ allowedHosts: ["app.example.com", ".example.com"],
35
+ });
36
+ ```
37
+
38
+ `.example.com` allows `a.example.com` (Vite-style suffix). Always merged with
39
+ `localhost` · `127.0.0.1` · `::1` and the listen hostname when it is not a
40
+ wildcard bind.
41
+
42
+ </Step>
43
+
44
+ <Step>
45
+ ### Confirm a bad Host is refused
46
+
47
+ ```bash
48
+ curl -i http://127.0.0.1:6530/health -H "Host: evil.example"
49
+ ```
50
+
51
+ Expect `403` with body `Forbidden: unexpected Host header`.
52
+
53
+ </Step>
54
+
55
+ </Steps>
56
+
57
+ ## Progressive Patterns
58
+
59
+ <Tabs items={["Reverse proxy", "Planes", "MCP"]}>
60
+
61
+ <Tab value="Reverse proxy">
62
+
63
+ Edge terminates TLS and forwards to the app. The `Host` the app sees must be on
64
+ the allow-list — usually the public name, not the container hostname.
65
+
66
+ ```typescript
67
+ createBunRuntime().serve(app, {
68
+ hostname: "0.0.0.0",
69
+ allowedHosts: ["app.example.com"],
70
+ });
71
+ ```
72
+
73
+ **Consequence:** omitting the public name behind Caddy / nginx / Traefik looks
74
+ like a random 403 to browsers that send the site's `Host`.
75
+
76
+ </Tab>
77
+
78
+ <Tab value="Planes">
79
+
80
+ Only two planes exist: **user** (application Flows, `fx.auth`) and **operator**
81
+ (Console / MCP capability, `fx.operator`). Declare `plane: "operator"` on Flows
82
+ that must stay off the public API.
83
+
84
+ A user-plane token on an operator Flow (or the reverse) throws `CrossPlaneError`.
85
+ API keys and Console `invoke-as` are attenuated — a derived principal never
86
+ exceeds the creator's scopes.
87
+
88
+ </Tab>
89
+
90
+ <Tab value="MCP">
91
+
92
+ | Surface | Port | Auth |
93
+ | -------- | -------- | --------------------------------------------------------- |
94
+ | App MCP | **6535** | Bearer required **even on localhost**; audience `oke-mcp` |
95
+ | Docs MCP | **6536** | Host / Origin only — public docs, not a live Manifest |
96
+
97
+ App MCP inherits **operator-plane** capability and never exceeds it. Tokens are
98
+ never forwarded upstream. Tools declare scopes; writes need confirmation.
99
+
100
+ </Tab>
101
+
102
+ </Tabs>
103
+
104
+ ## Host / Origin Rules
105
+
106
+ | Check | Behavior |
107
+ | -------- | -------------------------------------------------------------------- |
108
+ | `Host` | Required; must match the effective allow-list |
109
+ | `Origin` | When present, host must match; `Origin: null` → 403 |
110
+ | Defaults | Always includes loopback + listen hostname (unless `0.0.0.0` / `::`) |
111
+ | Extras | `ServeOptions.allowedHosts` — never replaces the mandatory check |
112
+
113
+ `allowedHosts` lives on **serve options**, not in `oke.config.ts`. Pass it wherever
114
+ you call `serve` for **6530**, Console **6533**, and app MCP **6535**.
115
+
116
+ ## Console Posture
117
+
118
+ | Control | Meaning |
119
+ | ------------- | -------------------------------------------------------------------------- |
120
+ | Host / Origin | Same allow-list logic as the backend |
121
+ | CSP | `default-src 'self'`; `frame-ancestors 'none'` |
122
+ | Cookies | `SameSite=Strict` |
123
+ | Claim code | Printed on `oke dev` TTY; `oke console claim-code` while setup is open |
124
+ | PII | Store / Call API mask classified fields unless `revealPii: true` (audited) |
125
+
126
+ Production Console: set `OKE_CONSOLE_SECRET` and configure
127
+ `console.prod` in [Configuration](/docs/reference/configuration#console).
128
+
129
+ ## Sessions and Audiences
130
+
131
+ | Audience | Surface |
132
+ | ------------- | -------------------- |
133
+ | `oke-app` | Application sessions |
134
+ | `oke-console` | Console operators |
135
+ | `oke-mcp` | App MCP |
136
+
137
+ Short access JWT (default ~14m) + rotating refresh. Refresh-token reuse revokes
138
+ the family.
139
+
140
+ ## Troubleshooting
141
+
142
+ <Accordions>
143
+
144
+ <Accordion title="403 Forbidden: unexpected Host header">
145
+ The request `Host` is not on the allow-list. Add the public hostname to `allowedHosts`, or hit
146
+ loopback (`localhost` / `127.0.0.1`) during local `oke dev`.
147
+ </Accordion>
148
+
149
+ <Accordion title="403 Forbidden: unexpected Origin header">
150
+ Browser sent an `Origin` whose host is not allowed, or `Origin: null`. Align the page origin with
151
+ `allowedHosts`, or call same-origin / non-browser clients without a spoofed Origin.
152
+ </Accordion>
153
+
154
+ <Accordion title="MCP tools refuse without Bearer on localhost">
155
+ App MCP on **6535** requires a Bearer with audience `oke-mcp` even on loopback. Docs MCP on
156
+ **6536** does not — it serves documentation only.
157
+ </Accordion>
158
+
159
+ <Accordion title="CrossPlaneError">
160
+ A user-plane token reached an operator Flow (or the reverse). Use the principal that matches the
161
+ Flow's `plane`.
162
+ </Accordion>
163
+
164
+ <Accordion title="AttenuationError">
165
+ A derived key or invoke-as identity requested a scope the creator cannot grant. Re-issue with a
166
+ subset of the creator's scopes.
167
+ </Accordion>
168
+
169
+ </Accordions>
170
+
171
+ ## Learn more
172
+
173
+ - [CLI](/docs/reference/cli) — `oke dev`, `oke console claim-code`
174
+ - [Gate](/docs/elements/gate) — policies, API keys, tenancy
175
+ - [The Architecture](/docs/understand/the-architecture) — one shape, one door, fixed vocabulary
176
+ - [Errors](/docs/reference/errors) — `CrossPlaneError`, `SessionError`, `AttenuationError`
177
+ - [Environment Variables](/docs/reference/environment-variables) — `OKE_CONSOLE_SECRET`
178
+
179
+ ## Next
180
+
181
+ <Cards>
182
+ <Card
183
+ title="Gate"
184
+ description="Auth, authorization, and rate limits."
185
+ href="/docs/elements/gate"
186
+ />
187
+ <Card
188
+ title="CLI"
189
+ description="oke and create-oke command catalogue."
190
+ href="/docs/reference/cli"
191
+ />
192
+ <Card
193
+ title="The Model"
194
+ description="One shape for every trigger, one door for every effect."
195
+ href="/docs/understand/the-architecture"
196
+ />
197
+ </Cards>
@@ -0,0 +1,5 @@
1
+ {
2
+ "title": "Understand",
3
+ "icon": "Compass",
4
+ "pages": ["the-architecture", "try-it"]
5
+ }
@@ -0,0 +1,264 @@
1
+ ---
2
+ title: "The Architecture"
3
+ description: "Why backends drift apart, the one rule that stops it, and the five pieces behind every Flow."
4
+ icon: "Compass"
5
+ ---
6
+
7
+ Four lines ship the signup flow. Eight months later it is six files that disagree about retries, audits, and who is allowed to do what.
8
+
9
+ This page shows that drift, the one rule that stops it, and the exact anatomy behind every Flow — in one sitting.
10
+
11
+ <Callout title="The Law">
12
+ Every backend behavior is a Flow: `on(Trigger) → Effects`. One species; triggers are typed values.
13
+ All world access goes through `fx`.
14
+ </Callout>
15
+
16
+ ## The problem: three features, same wall
17
+
18
+ **A signup flow.** A user registers. Send them a welcome email:
19
+
20
+ ```typescript
21
+ app.post("/signup", async (req, res) => {
22
+ const user = await db.users.create(req.body);
23
+ await sendMail(user.email, "Welcome!", welcomeTemplate(user));
24
+ res.json(user);
25
+ });
26
+ ```
27
+
28
+ It works. It ships. Then a traffic spike, a silent failure, a compliance question, and a double-submit turn those four lines into six files — endpoint, queue, worker, Redis connection, mail client, audit table — that never agreed with each other about anything.
29
+
30
+ **A payment webhook.** A provider confirms a charge. Mark the order paid. Simple — until the provider retries the same webhook twice during a network hiccup, and "mark the order paid" needs to somehow know it already ran.
31
+
32
+ **A nightly report.** Summarize yesterday's activity and email it to managers. Trivial — until a manager's access gets revoked at 11:58pm and the report that runs at midnight has no idea the permission it checked when the feature was built isn't the permission that holds right now.
33
+
34
+ Three teams. Three domains. Nobody on any of them talked to the other two. And all three land on the identical fork: **something has to happen later, exactly once, provably — and nothing in the original four lines said what "provably" would end up costing.**
35
+
36
+ ### Follow one all the way through
37
+
38
+ Two weeks later, a launch drives a traffic spike, the mail provider starts returning `429`, and signups start failing because an unrelated email is slow. You move the send off the request path:
39
+
40
+ ```typescript
41
+ app.post("/signup", async (req, res) => {
42
+ const user = await db.users.create(req.body);
43
+ emailQueue.add("welcome", { userId: user.id });
44
+ res.json(user);
45
+ });
46
+ ```
47
+
48
+ The endpoint is fast again. It's also no longer one system — it's an endpoint, a queue, a worker, and a Redis connection nobody else on the team knew existed until a missing `REDIS_URL` broke staging.
49
+
50
+ <SixSystemsDrift />
51
+
52
+ From here the same pattern repeats on a longer clock. A support ticket reveals a job failed silently — nobody had configured retries, so you add them, and now a specific number (3? 5? with what backoff?) lives in a file that nobody will remember the reasoning for in six weeks. Compliance asks for proof of every email sent — you add a table written to from inside the worker, and now that worker has two jobs instead of one, quietly capable of disagreeing with itself if the second write fails. Someone double-clicks submit — two jobs enqueue, two emails send, and idempotency becomes a fact that has to live in two systems that were never introduced to each other.
53
+
54
+ None of these were mistakes. Each one was the correct call, made by a competent engineer, in direct response to something that actually happened.
55
+
56
+ ### What's actually going on
57
+
58
+ Look at what the six resulting files have in common: none of them agree with each other about the same three things. What counts as "done." What happens on failure. Who's allowed to do this at all.
59
+
60
+ That's the real cost — not the number of tools, but what sits between them:
61
+
62
+ - **Failure means something different in each one.** A queue retry, an HTTP 500, and a rejected promise from a mail SDK are three unrelated shapes that all happen to mean "this didn't work."
63
+ - **Permission has no fixed address.** It lives wherever whoever wrote that file remembered to put it — which means a reviewer can't point at one place and ask "is this checked?"
64
+ - **Two systems both think they own the same fact.** The database says an order is paid. The already-running webhook handler doesn't know that yet. Nothing keeps them honest with each other in the gap.
65
+ - **Nobody can see the whole thing at once.** There is no file, diagram, or dashboard where "the signup flow" exists as one object — only as the sum of files that happen to call each other.
66
+
67
+ What would have to be true on day one for month eight to never happen?
68
+
69
+ ## The model: one rule, in two parts
70
+
71
+ Every seam above came from the same root cause: each system involved had its own idea of when it should run and what it was allowed to touch, and nothing forced those ideas to agree with each other.
72
+
73
+ OKE removes the disagreement by removing the choice. It's one rule, in two parts.
74
+
75
+ **First: every trigger reduces to the same shape.** An HTTP request, a scheduled tick, a queue message, a database change — whatever wakes the code up, what follows has one identical anatomy: `on(Trigger) → Effects`. Not four systems that happen to look similar. One system, with four ways to wake it up.
76
+
77
+ **Second: every effect passes through one door.** Nothing is allowed to touch a database, send an email, check a permission, or read the clock on its own — all of it goes through a single surface. Not because that's tidier. Because it's the only way retries, auditing, idempotency, and permission checks stop being infrastructure every team reinvents at the exact moment they get burned by not having it.
78
+
79
+ <FlowShape />
80
+
81
+ That's the whole model. Not a bigger toolbox — a smaller number of things that are allowed to happen at all.
82
+
83
+ ### What OKE is — and isn't
84
+
85
+ OKE is a backend programming model: behavior is expressed as Flows, effects are captured through `fx`, and the compiler turns that model into a versioned Manifest that powers the rest of the backend.
86
+
87
+ | OKE is not … | OKE is … |
88
+ | --------------------------------------------- | ----------------------------------------------------------------------- |
89
+ | Another queue, ORM, or mailer to wire up | One Flow species every trigger wakes up |
90
+ | A toolbox of forty clients with forty configs | Eight elements with irreducible physics — nothing else gets added |
91
+ | A platform you deploy into | TypeScript you host; Client, Console, and MCP derive from your Manifest |
92
+
93
+ One shape for every trigger, one door for every effect, a fixed vocabulary for what the door allows — that's what this project built. It's called **OKE**.
94
+
95
+ <sub>
96
+ *OKE isn't an acronym for anything in the code. It comes from Omq Khafi — the organization this
97
+ engine grew out of — with "Engine" appended: **O**mq **K**hafi **E**ngine.*
98
+ </sub>
99
+
100
+ ## The vocabulary: eight elements, closed set
101
+
102
+ The door from the last section isn't open-ended — it recognizes a fixed set of things it's willing to do. Each one made the cut because it has _irreducible physics_: behavior that breaks if you tried to fake it using one of the others.
103
+
104
+ <Features />
105
+
106
+ | Element | What it is | What it replaces |
107
+ | ----------- | -------------------- | ----------------------------------------------------------------------- |
108
+ | **Flow** | Execution & behavior | endpoint, handler, consumer, job, workflow, webhook |
109
+ | **Signal** | Data in motion | queue, pub/sub, stream, websocket, SSE, event bus |
110
+ | **Store** | Data at rest | relational database, cache, key-value store, file storage, search index |
111
+ | **Clock** | Time & schedules | cron, every, delay, durable sleep, timeout |
112
+ | **Gate** | Permission to act | auth, session, tenancy, RBAC, scope, rate limit, public |
113
+ | **Vault** | Protected knowledge | secrets, encryption keys, rotation, environment variables |
114
+ | **Channel** | Reaching a human | transactional email, SMS, push notifications, receipts |
115
+ | **AI** | Machine intelligence | model calls, structured prompts, embeddings, agents, RAG |
116
+
117
+ Read the right column as the honest answer to "what would I have reached for before this?" Every item in it is a separate tool with its own configuration, its own failure modes, and its own place to go wrong.
118
+
119
+ The left column is the same ground, covered by something with one shared door and one shared set of guarantees. Where that distinction is real, it gets a name. Where it isn't, it doesn't — which is why the list stops at eight instead of growing indefinitely.
120
+
121
+ ## The anatomy: five pieces behind every Flow
122
+
123
+ Everything a Flow does reduces to one line:
124
+
125
+ ```typescript
126
+ on(
127
+ trigger,
128
+ flow({
129
+ do: (input, fx) => {
130
+ /* ... */
131
+ },
132
+ }),
133
+ );
134
+ ```
135
+
136
+ If that line doesn't mean much yet, that's what this section is for. Five pieces make it up. We'll take them one at a time, then put them together using a complete signup example.
137
+
138
+ ### `on(...)` — wires a trigger to a flow
139
+
140
+ `on` does exactly one thing: it connects "something that can happen" to "code that should run when it does." Nothing executes until this connection exists.
141
+
142
+ ```typescript
143
+ on(someTrigger, someFlow);
144
+ ```
145
+
146
+ That's the whole job. The interesting parts are what goes in each slot.
147
+
148
+ ### A trigger — the answer to "when"
149
+
150
+ The first argument to `on` is the trigger: whatever wakes the code up. A trigger doesn't run any of your logic — it only answers one question: _when should this happen?_
151
+
152
+ ```typescript
153
+ http.post(); // path from the file tree — e.g. src/flows/users/signup.ts → POST /users/signup
154
+ ```
155
+
156
+ There are five kinds of trigger in total — the table at the end of this page lists them. For now: the trigger is the _when_, and it's the only thing that changes between an endpoint, a scheduled job, and everything else.
157
+
158
+ <FlowTriggers />
159
+
160
+ ### `flow(...)` — the actual unit of work
161
+
162
+ The second argument to `on` is a Flow — declared with the `flow()` function. It answers _what_: what work is this, and what does it promise about its inputs and outputs?
163
+
164
+ ```typescript
165
+ flow({
166
+ do: /* the actual code — next */,
167
+ });
168
+ ```
169
+
170
+ Omit the name on tree files — the compiler stamps `unit.export` (e.g. `users.signup`).
171
+ Pass `flow("users.signup", { … })` only for control: barrels, stable names across
172
+ moves, or call-only Flows you `fx.call` by name.
173
+
174
+ ### `do` — the code that actually runs
175
+
176
+ `do` is a function you write. It answers _how_. It receives two things: `input` (your data) and `fx` (next). Everything your Flow actually does lives here.
177
+
178
+ ```typescript
179
+ do: async (input, fx) => {
180
+ return { ok: true };
181
+ };
182
+ ```
183
+
184
+ ### `fx` — the only door to the outside world
185
+
186
+ `fx` is the second argument to `do`, and it's the piece the other four exist to protect. The rule is simple and absolute: **your Flow is not allowed to read a database, send an email, check a clock, or touch anything outside itself except through `fx`.**
187
+
188
+ ```typescript
189
+ do: async (input, fx) => {
190
+ const user = await fx.store(db).insert(users).values(input); // the database, through fx
191
+ await fx.send(welcomeEmail, { to: user.email }); // another system, through fx
192
+ return user;
193
+ };
194
+ ```
195
+
196
+ This one rule is what made the month-8 drift above avoidable: if `fx` is the only door, retries, auditing, and idempotency stop being separate systems teams build by hand, and become properties of the one boundary everything already passes through.
197
+
198
+ ### Putting the five pieces together
199
+
200
+ Here is the complete signup flow, with every piece labeled where it sits:
201
+
202
+ ```typescript title="src/flows/users/signup.ts"
203
+ export const signup = on(
204
+ http.post(), // ← trigger: when (stamped POST /users/signup)
205
+ flow({
206
+ // ↑ flow: what (stamped users.signup)
207
+ do: async (input, fx) => {
208
+ // ← do: how
209
+ const user = await fx.store(db).insert(users).values(input); // ← fx: the only way out
210
+ await fx.send(welcomeEmail, { to: user.email, data: { name: user.name } });
211
+ return user;
212
+ },
213
+ }),
214
+ );
215
+ ```
216
+
217
+ <Callout title="Call-only flows">
218
+ A `flow(...)` declared without `on(...)` around it is internal — nothing outside your code can
219
+ start it. Other flows invoke it directly with `fx.call(flowRef, input)`.
220
+ </Callout>
221
+
222
+ ### Checking it against the timeline
223
+
224
+ Nothing about the code above looks more complicated than the four lines that started the drift — because it isn't. The difference only shows up when the same pressure from that timeline hits it. Every fork was really the same question — _is this safe to retry, safe to audit, safe to run twice?_ — and now it has one home:
225
+
226
+ | Then: a new system per incident | Now: one door, fixed answer |
227
+ | -------------------------------------------------------------------- | ----------------------------------------------------------------------- |
228
+ | **Week 2 spike** — hand-build a queue + worker to run the send later | Running later, safely, is asked of the trigger or the effect itself |
229
+ | **Month 2 silence** — a retry count in a worker nobody remembers | Retries are a property of the `fx` boundary every effect passes through |
230
+ | **Month 4 audit** — a `sent_emails` table written by hand from a job | What was sent is already known — nothing sends outside `fx` |
231
+ | **Month 6 double-submit** — dedup split across a queue and a DB | One call through one door — no second path for a duplicate |
232
+
233
+ None of that required new code beyond what's above. It required the four lines to already be the kind of thing where those questions have a fixed answer, instead of a new one invented per team, per incident.
234
+
235
+ ### Five kinds of trigger
236
+
237
+ `flow`, `do`, and `fx` never change shape. Only the trigger does — and there are exactly five kinds, one per element that can independently wake a Flow up:
238
+
239
+ | Trigger | Element | Starts When |
240
+ | --------------------------------------------------- | ------- | -------------------------------- |
241
+ | `http.post()` (path from file tree) | Flow | A request arrives |
242
+ | `clock("name", { every: "10m" })` | Clock | A time interval elapses |
243
+ | `signal.once("name", {…})` / `.broadcast` / `.live` | Signal | Another flow announces something |
244
+ | `db.table(users).changed("email")` | Store | A database row changes |
245
+ | `mcp.tool("name")` | AI | An AI agent calls it |
246
+
247
+ The Elements section walks through each element in depth — this is just enough to recognize them when you see them.
248
+
249
+ ## Where this goes next
250
+
251
+ You now hold the whole model: the drift, the rule, the eight elements, the five-piece anatomy. Don't read more — run it. From an empty folder to this exact signup Flow answering a real request, in one sitting:
252
+
253
+ <Cards>
254
+ <Card
255
+ title="Try It"
256
+ description="From an empty folder to a Flow running in the Console — one sitting, minimal detour."
257
+ href="/docs/understand/try-it"
258
+ />
259
+ <Card
260
+ title="Elements"
261
+ description="When you're back: Flow, Signal, Store, Clock, Gate, Vault, Channel, AI — each in depth."
262
+ href="/docs/elements"
263
+ />
264
+ </Cards>
@@ -0,0 +1,58 @@
1
+ ---
2
+ title: "Try It"
3
+ description: "From an empty folder to a Flow running in the Console — one sitting, minimal detour."
4
+ icon: "Terminal"
5
+ ---
6
+
7
+ ## What you need
8
+
9
+ Bun ≥ 1.4.2, and Docker running. `oke dev` starts everything else — Postgres, Redis, mail — for you.
10
+
11
+ ```bash
12
+ bun --version
13
+ docker info
14
+ ```
15
+
16
+ ## Scaffold and run
17
+
18
+ ```bash
19
+ bunx create-oke@latest my-app
20
+ cd my-app
21
+ oke dev
22
+ ```
23
+
24
+ Open the Console at the address printed in the terminal and claim it with the code shown there. The Flows listed weren't configured anywhere — they were derived from the code you just scaffolded.
25
+
26
+ ## Call it
27
+
28
+ The starter ships a health Flow. Call it from a typed client, the same way your frontend would:
29
+
30
+ ```typescript
31
+ import { createClient } from "okengine/client";
32
+ import type { App } from "./app";
33
+
34
+ const api = createClient<App>("http://localhost:6530");
35
+ const { data, error } = await api.main.health({});
36
+ ```
37
+
38
+ `data` and `error` are inferred straight from the Flow you just ran — not from a separate schema you maintain by hand.
39
+
40
+ ## Prove it, don't just believe it
41
+
42
+ ```typescript
43
+ test("boots — health flow", async () => {
44
+ const t = await createTestApp(app);
45
+ const { data } = await t.api.main.health({});
46
+ expect(data).toEqual({ ok: true });
47
+ });
48
+ ```
49
+
50
+ ```bash
51
+ bun test
52
+ ```
53
+
54
+ No mocked HTTP server, no real clock, no live mail provider. `createTestApp` swaps every driver behind `fx` for a deterministic one — because effects were never allowed to happen any other way.
55
+
56
+ ## Where this goes next
57
+
58
+ If this went smoothly, the next page is the honest one: what's solid today, what's still moving, and who this is actually ready for right now.
@@ -13,6 +13,7 @@ import { parseDurationMs } from "../elements/clock/duration.ts";
13
13
  import { assertAttenuated } from "./attenuation.ts";
14
14
  import type { AuthPlane } from "./planes.ts";
15
15
  import type { ApiKeyRow } from "./tables.ts";
16
+ import { okid } from "../okid.ts";
16
17
 
17
18
  /** Capability / Manifest resource for `fx.auth` key methods. */
18
19
  export const AUTH_API_KEYS_RESOURCE = "auth:api-keys";
@@ -145,7 +146,7 @@ export async function createApiKey(
145
146
  assertAttenuated(options.creatorScopes, options.scopes, "api key");
146
147
 
147
148
  const now = options.now ?? (() => Date.now());
148
- const id = options.id ?? crypto.randomUUID();
149
+ const id = options.id ?? okid();
149
150
  const secret = options.secret ?? `oke_${id.replace(/-/g, "")}_${randomSecret()}`;
150
151
  const pepper = options.pepper ?? store.pepper ?? DEFAULT_PEPPER;
151
152
  const hash = options.hash ?? (await hashApiKeySecret(secret, pepper));