okengine 0.18.4 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (512) hide show
  1. package/AGENTS.md +18 -10
  2. package/README.md +2 -2
  3. package/package.json +51 -50
  4. package/site/content/docs/ai/index.mdx +25 -6
  5. package/site/content/docs/ai/llms-txt.mdx +1 -1
  6. package/site/content/docs/ai/meta.json +1 -1
  7. package/site/content/docs/ai/skills.mdx +4 -1
  8. package/site/content/docs/ai/try-it.mdx +58 -0
  9. package/site/content/docs/client/auth.mdx +208 -0
  10. package/site/content/docs/client/calling.mdx +451 -0
  11. package/site/content/docs/client/index.mdx +209 -0
  12. package/site/content/docs/client/live.mdx +234 -0
  13. package/site/content/docs/client/meta.json +5 -0
  14. package/site/content/docs/client/react.mdx +249 -0
  15. package/site/content/docs/elements/ai/agents.mdx +232 -0
  16. package/site/content/docs/elements/ai/index.mdx +341 -0
  17. package/site/content/docs/elements/ai/mcp.mdx +275 -0
  18. package/site/content/docs/elements/ai/meta.json +5 -0
  19. package/site/content/docs/elements/ai/models.mdx +283 -0
  20. package/site/content/docs/elements/ai/prompts.mdx +256 -0
  21. package/site/content/docs/elements/channel/email.mdx +266 -0
  22. package/site/content/docs/elements/channel/index.mdx +344 -0
  23. package/site/content/docs/elements/channel/meta.json +5 -0
  24. package/site/content/docs/elements/channel/push.mdx +218 -0
  25. package/site/content/docs/elements/channel/receipts.mdx +206 -0
  26. package/site/content/docs/elements/channel/sms.mdx +264 -0
  27. package/site/content/docs/elements/channel/whatsapp.mdx +220 -0
  28. package/site/content/docs/elements/clock/index.mdx +300 -0
  29. package/site/content/docs/elements/clock/meta.json +5 -0
  30. package/site/content/docs/elements/clock/schedules.mdx +508 -0
  31. package/site/content/docs/elements/clock/sleep.mdx +445 -0
  32. package/site/content/docs/elements/flow/consumers.mdx +707 -0
  33. package/site/content/docs/elements/flow/http.mdx +1080 -0
  34. package/site/content/docs/elements/flow/index.mdx +618 -0
  35. package/site/content/docs/elements/flow/meta.json +5 -0
  36. package/site/content/docs/elements/flow/routing.mdx +559 -0
  37. package/site/content/docs/elements/flow/workflows.mdx +640 -0
  38. package/site/content/docs/elements/gate/auth.mdx +371 -0
  39. package/site/content/docs/elements/gate/authorization.mdx +298 -0
  40. package/site/content/docs/elements/gate/index.mdx +418 -0
  41. package/site/content/docs/elements/gate/meta.json +5 -0
  42. package/site/content/docs/elements/gate/rate-limits.mdx +365 -0
  43. package/site/content/docs/elements/gate/rls.mdx +303 -0
  44. package/site/content/docs/elements/gate/tenancy.mdx +372 -0
  45. package/site/content/docs/elements/index.mdx +52 -11
  46. package/site/content/docs/elements/signal/broadcast.mdx +517 -0
  47. package/site/content/docs/elements/signal/index.mdx +352 -0
  48. package/site/content/docs/elements/signal/live.mdx +590 -0
  49. package/site/content/docs/elements/signal/meta.json +5 -0
  50. package/site/content/docs/elements/signal/once.mdx +596 -0
  51. package/site/content/docs/elements/store/files.mdx +659 -0
  52. package/site/content/docs/elements/store/index.mdx +310 -0
  53. package/site/content/docs/elements/store/kv.mdx +629 -0
  54. package/site/content/docs/elements/store/meta.json +5 -0
  55. package/site/content/docs/elements/store/search.mdx +946 -0
  56. package/site/content/docs/elements/store/sql.mdx +937 -0
  57. package/site/content/docs/elements/vault/config.mdx +256 -0
  58. package/site/content/docs/elements/vault/index.mdx +356 -0
  59. package/site/content/docs/elements/vault/meta.json +5 -0
  60. package/site/content/docs/elements/vault/rotation.mdx +273 -0
  61. package/site/content/docs/elements/vault/secrets.mdx +285 -0
  62. package/site/content/docs/index.mdx +78 -30
  63. package/site/content/docs/meta.json +11 -8
  64. package/site/content/docs/plugins/cors.mdx +2 -1
  65. package/site/content/docs/plugins/csrf.mdx +4 -1
  66. package/site/content/docs/plugins/headers.mdx +2 -1
  67. package/site/content/docs/plugins/ip-allowlist.mdx +2 -1
  68. package/site/content/docs/plugins/magic-link.mdx +6 -0
  69. package/site/content/docs/plugins/maintenance-mode.mdx +2 -1
  70. package/site/content/docs/plugins/oauth.mdx +1 -1
  71. package/site/content/docs/plugins/otp.mdx +12 -1
  72. package/site/content/docs/plugins/passkey.mdx +39 -15
  73. package/site/content/docs/plugins/two-factor.mdx +91 -34
  74. package/site/content/docs/plugins/username.mdx +1 -1
  75. package/site/content/docs/recipes/caddy.mdx +3 -3
  76. package/site/content/docs/recipes/index.mdx +18 -28
  77. package/site/content/docs/recipes/mailpit.mdx +1 -1
  78. package/site/content/docs/recipes/meilisearch.mdx +3 -3
  79. package/site/content/docs/recipes/meta.json +1 -4
  80. package/site/content/docs/recipes/nginx.mdx +2 -2
  81. package/site/content/docs/recipes/openrouter.mdx +283 -0
  82. package/site/content/docs/recipes/pgdog.mdx +3 -3
  83. package/site/content/docs/recipes/postgres.mdx +1 -2
  84. package/site/content/docs/recipes/rustfs.mdx +2 -2
  85. package/site/content/docs/recipes/traefik.mdx +3 -3
  86. package/site/content/docs/reference/cli.mdx +231 -0
  87. package/site/content/docs/reference/configuration.mdx +30 -10
  88. package/site/content/docs/reference/environment-variables.mdx +93 -20
  89. package/site/content/docs/reference/errors.mdx +132 -29
  90. package/site/content/docs/reference/fx.mdx +137 -73
  91. package/site/content/docs/reference/index.mdx +13 -21
  92. package/site/content/docs/reference/meta.json +4 -5
  93. package/site/content/docs/reference/okid.mdx +42 -17
  94. package/site/content/docs/reference/plugins.mdx +4 -3
  95. package/site/content/docs/reference/security.mdx +197 -0
  96. package/site/content/docs/understand/meta.json +5 -0
  97. package/site/content/docs/understand/the-anatomy.mdx +132 -0
  98. package/site/content/docs/understand/the-model.mdx +32 -0
  99. package/site/content/docs/understand/the-problem.mdx +74 -0
  100. package/site/content/docs/understand/the-vocabulary.mdx +26 -0
  101. package/src/auth/api-keys.ts +2 -1
  102. package/src/auth/bindings.ts +64 -16
  103. package/src/auth/gate-auth.test.ts +6 -1
  104. package/src/auth/identity.ts +152 -5
  105. package/src/auth/index.ts +29 -0
  106. package/src/auth/invites.ts +2 -1
  107. package/src/auth/method-context.ts +41 -0
  108. package/src/auth/oauth-as/crypto.ts +2 -1
  109. package/src/auth/oauth-as/stores.ts +2 -1
  110. package/src/auth/operator.ts +2 -1
  111. package/src/auth/sessions-jwt.test.ts +77 -0
  112. package/src/auth/sessions.ts +20 -4
  113. package/src/auth/tenants.ts +3 -2
  114. package/src/auth/two-factor-challenge.test.ts +61 -0
  115. package/src/auth/two-factor-challenge.ts +284 -0
  116. package/src/auth/verification.ts +5 -0
  117. package/src/bench/REPORT.md +49 -0
  118. package/src/bench/g03-signal-once.bench.ts +1 -1
  119. package/src/bench/g10-observability-contention.bench.ts +1 -1
  120. package/src/bench/g17-hybrid-search.bench.ts +572 -0
  121. package/src/bench/load-app.ts +2 -10
  122. package/src/cli/ai-setup/ai-setup.test.ts +331 -42
  123. package/src/cli/ai-setup/apply.ts +294 -52
  124. package/src/cli/ai-setup/catalog.ts +238 -1342
  125. package/src/cli/ai-setup/index.ts +14 -99
  126. package/src/cli/ai-setup/prompts.ts +60 -443
  127. package/src/cli/ask-seed.test.ts +96 -0
  128. package/src/cli/ask-seed.ts +82 -0
  129. package/src/cli/ask-vault-gaps.test.ts +84 -0
  130. package/src/cli/ask-vault-gaps.ts +146 -0
  131. package/src/cli/build.ts +2 -2
  132. package/src/cli/client-add.test.ts +26 -1
  133. package/src/cli/client-add.ts +106 -27
  134. package/src/cli/competitor-mention-removal.test.ts +1 -1
  135. package/src/cli/db-seed.ts +2 -0
  136. package/src/cli/db.ts +150 -11
  137. package/src/cli/dev.ts +119 -211
  138. package/src/cli/docker-clean.ts +2 -2
  139. package/src/cli/docker-cli.test.ts +1 -1
  140. package/src/cli/doctor-diff.ts +4 -2
  141. package/src/cli/doctor-pii.test.ts +1 -1
  142. package/src/cli/doctor.ts +14 -1
  143. package/src/cli/hero-meta.test.ts +4 -2
  144. package/src/cli/load-config.images.test.ts +8 -10
  145. package/src/cli/load-config.ts +1 -1
  146. package/src/cli/registry.ts +22 -10
  147. package/src/cli/replay.ts +3 -1
  148. package/src/cli/start.ts +1 -1
  149. package/src/cli/tui/keys.ts +2 -3
  150. package/src/client/auth/cookies.ts +74 -0
  151. package/src/client/auth/create-auth-client.ts +636 -0
  152. package/src/client/auth/denials.ts +99 -0
  153. package/src/client/auth/session.ts +283 -0
  154. package/src/client/auth.test.ts +251 -0
  155. package/src/client/auth.ts +39 -114
  156. package/src/client/create-with-session.ts +245 -0
  157. package/src/client/create.ts +51 -5
  158. package/src/client/index.ts +11 -1
  159. package/src/client/live.ts +11 -79
  160. package/src/client/notes-contract.test.ts +5 -10
  161. package/src/client/sse.ts +137 -0
  162. package/src/client/stream.ts +147 -0
  163. package/src/client/transport.test.ts +32 -0
  164. package/src/client/transport.ts +123 -26
  165. package/src/client/types.ts +118 -14
  166. package/src/client-react/index.ts +185 -24
  167. package/src/client-react/use-live-query.ts +13 -3
  168. package/src/compiler/aot.test.ts +7 -4
  169. package/src/compiler/effects-embed.test.ts +56 -0
  170. package/src/compiler/effects-fetch.test.ts +43 -0
  171. package/src/compiler/effects-infer.ts +58 -2
  172. package/src/compiler/extract.test.ts +258 -44
  173. package/src/compiler/extract.ts +566 -48
  174. package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -4
  175. package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +2 -8
  176. package/src/compiler/fixtures/skyport.expected.json +4 -4
  177. package/src/compiler/fixtures/triggers/five-triggers.ts +14 -10
  178. package/src/compiler/response.ts +2 -2
  179. package/src/compiler/search-writer-isolation.test.ts +40 -0
  180. package/src/config/index.ts +11 -0
  181. package/src/console/server/app.ts +17 -23
  182. package/src/console/server/bind.ts +4 -0
  183. package/src/console/server/console.test.ts +2 -2
  184. package/src/console/server/flows-invoke.test.ts +50 -34
  185. package/src/console/server/flows.ts +436 -255
  186. package/src/console/server/runs-ingest.test.ts +6 -3
  187. package/src/console/server/serve.ts +1 -1
  188. package/src/console/server/signals.test.ts +1 -5
  189. package/src/console/server/signals.ts +8 -6
  190. package/src/console/server/state.ts +6 -1
  191. package/src/console/server/store.ts +1 -1
  192. package/src/console/ui-next/dist/assets/FileExportIcon-Ck-5od4R.js +1 -0
  193. package/src/console/ui-next/dist/assets/MoreHorizontalCircle01Icon-gMNGsE37.js +1 -0
  194. package/src/console/ui-next/dist/assets/PlusSignIcon-CwG3nxfu.js +1 -0
  195. package/src/console/ui-next/dist/assets/UnavailableIcon-D9cvHVPr.js +1 -0
  196. package/src/console/ui-next/dist/assets/UserIcon-DaE2PB5_.js +1 -0
  197. package/src/console/ui-next/dist/assets/access-page-DFeymU07.js +4 -0
  198. package/src/console/ui-next/dist/assets/agent-disclosure-U1rdfblp.js +1 -0
  199. package/src/console/ui-next/dist/assets/cache-glyph-BeFJeqBG.js +1 -0
  200. package/src/console/ui-next/dist/assets/call-pii-button-CVAONPii.js +1 -0
  201. package/src/console/ui-next/dist/assets/collapsible-D2A6NJ-3.js +1 -0
  202. package/src/console/ui-next/dist/assets/copy-inline-button-CAYD18cr.js +1 -0
  203. package/src/console/ui-next/dist/assets/dagre.esm-B1_XeuLP.js +1 -0
  204. package/src/console/ui-next/dist/assets/detail-header-DVWNjWjg.js +1 -0
  205. package/src/console/ui-next/dist/assets/dropdown-menu-4h2LOVXM.js +1 -0
  206. package/src/console/ui-next/dist/assets/duration-tone-Cgk_h5ja.js +9 -0
  207. package/src/console/ui-next/dist/assets/element-icons-BI8cJgdh.js +1 -0
  208. package/src/console/ui-next/dist/assets/explorer-empty-CJs5A-wm.js +1 -0
  209. package/src/console/ui-next/dist/assets/flows-page-Dss7941e.js +1 -0
  210. package/src/console/ui-next/dist/assets/highlighted-json-MYZQtRnw.js +154 -0
  211. package/src/console/ui-next/dist/assets/http-method-Jrh39p7A.js +1 -0
  212. package/src/console/ui-next/dist/assets/index-D0zS5rKO.css +2 -0
  213. package/src/console/ui-next/dist/assets/index-DXP2dBIF.js +63 -0
  214. package/src/console/ui-next/dist/assets/observability-page-HvolXxTI.js +4 -0
  215. package/src/console/ui-next/dist/assets/react-dom-Ddte4I-Q.js +9 -0
  216. package/src/console/ui-next/dist/assets/replica-lag-CSh2dzrb.js +18 -0
  217. package/src/console/ui-next/dist/assets/request-meta-BatF8KrK.js +1 -0
  218. package/src/console/ui-next/dist/assets/shortcut-keys-3ILd8oGn.js +1 -0
  219. package/src/console/ui-next/dist/assets/{sql-BskegiFM.js → sql-BsFa4tDR.js} +1 -1
  220. package/src/console/ui-next/dist/assets/store-page-eiKiHnNe.js +41 -0
  221. package/src/console/ui-next/dist/assets/trace-detail-sheet-CZkMeKS-.js +2 -0
  222. package/src/console/ui-next/dist/assets/tree-expand-toggle-CW8y5A2h.js +55 -0
  223. package/src/console/ui-next/dist/assets/units-page-B_RWJrEO.js +1 -0
  224. package/src/console/ui-next/dist/assets/{use-vault-list-uk4WVboC.js → use-vault-list-CT4-gajj.js} +1 -1
  225. package/src/console/ui-next/dist/assets/vault-page-CWrg-A68.js +2 -0
  226. package/src/console/ui-next/dist/assets/xyflow-CSyC6ryz.css +1 -0
  227. package/src/console/ui-next/dist/assets/xyflow-yApv7D4e.js +7 -0
  228. package/src/console/ui-next/dist/index.html +6 -13
  229. package/src/console/ui-next/seed-invoke-host.ts +21 -18
  230. package/src/console/ui-next/src/client.ts +7 -2
  231. package/src/console/ui-next/src/features/flows/graph/element-map.ts +2 -0
  232. package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +12 -2
  233. package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +4 -0
  234. package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +8 -0
  235. package/src/console/ui-next/src/features/flows/traces/waterfall-bars.ts +4 -0
  236. package/src/console/ui-next/src/features/flows/traces/waterfall-tooltip.ts +9 -3
  237. package/src/console/ui-next/src/features/store/detail/reveal-cell.tsx +7 -11
  238. package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +2 -2
  239. package/src/console/ui-next/src/features/store/lib/fields-from-table.ts +2 -0
  240. package/src/console/ui-next/src/features/store/lib/grid-model.test.ts +25 -1
  241. package/src/console/ui-next/src/features/store/lib/grid-model.ts +36 -1
  242. package/src/console/ui-next/src/features/store/query/query-results.tsx +3 -3
  243. package/src/console/ui-next/src/features/units/detail/effects-summary.tsx +2 -0
  244. package/src/console/ui-next/src/features/units/lib/call-read-safe.ts +3 -1
  245. package/src/console/ui-next/src/features/units/lib/fields-from-schema.ts +6 -0
  246. package/src/docker/ai-model-status.test.ts +1 -12
  247. package/src/docker/ai-model-status.ts +10 -38
  248. package/src/docker/compose-up.test.ts +100 -0
  249. package/src/docker/compose-up.ts +292 -0
  250. package/src/docker/compose.ts +4 -14
  251. package/src/docker/derive.ts +9 -34
  252. package/src/docker/docker.test.ts +16 -261
  253. package/src/docker/images-config.test.ts +13 -25
  254. package/src/docker/index.ts +1 -24
  255. package/src/docker/recipes/index.ts +0 -23
  256. package/src/docker/recipes/pgdog.ts +6 -4
  257. package/src/docker/stack-id.test.ts +2 -2
  258. package/src/docker/stack-id.ts +0 -1
  259. package/src/docker/types.ts +2 -2
  260. package/src/drivers/ai-anthropic.ts +10 -0
  261. package/src/drivers/ai-openai-compatible.ts +34 -7
  262. package/src/drivers/ai-providers.test.ts +0 -107
  263. package/src/drivers/ai-stream.test.ts +0 -34
  264. package/src/drivers/ai-types.ts +17 -8
  265. package/src/drivers/channel-fcm.ts +6 -1
  266. package/src/drivers/channel-msegat.ts +6 -1
  267. package/src/drivers/channel-resend.ts +5 -1
  268. package/src/drivers/channel-smtp.ts +5 -1
  269. package/src/drivers/channel-sndr.ts +10 -1
  270. package/src/drivers/channel-taqnyat-mail.ts +5 -1
  271. package/src/drivers/channel-taqnyat-whatsapp.ts +6 -1
  272. package/src/drivers/channel-taqnyat.ts +6 -1
  273. package/src/drivers/channel-types.ts +10 -0
  274. package/src/drivers/channel-unifonic.ts +6 -1
  275. package/src/drivers/channel-wa-cloud.ts +6 -1
  276. package/src/drivers/channel-webpush.ts +6 -1
  277. package/src/drivers/external.ts +33 -0
  278. package/src/drivers/index.ts +0 -10
  279. package/src/drivers/meilisearch.ts +6 -0
  280. package/src/drivers/oauth-types.ts +4 -0
  281. package/src/drivers/postgres.test.ts +74 -3
  282. package/src/drivers/postgres.ts +175 -6
  283. package/src/drivers/signal-types.ts +5 -5
  284. package/src/drivers/types.ts +7 -0
  285. package/src/drivers/vault-types.ts +5 -0
  286. package/src/elements/ai/declare.ts +17 -4
  287. package/src/elements/ai/eval.ts +1 -1
  288. package/src/elements/ai/mcp-http.ts +1 -1
  289. package/src/elements/ai/pii.ts +1 -1
  290. package/src/elements/ai/providers.test.ts +289 -0
  291. package/src/elements/ai/providers.ts +169 -0
  292. package/src/elements/ai/runtime.ts +68 -5
  293. package/src/elements/ai/schema.ts +2 -2
  294. package/src/elements/ai.test.ts +7 -7
  295. package/src/elements/ai.ts +17 -1
  296. package/src/elements/channel/runtime.ts +105 -12
  297. package/src/elements/clock/cron-fields.test.ts +144 -0
  298. package/src/elements/clock/cron-fields.ts +185 -0
  299. package/src/elements/clock/declare.ts +247 -3
  300. package/src/elements/clock.ts +21 -1
  301. package/src/elements/index.ts +15 -0
  302. package/src/elements/signal/chaos-child.ts +1 -2
  303. package/src/elements/signal/declare.ts +65 -37
  304. package/src/elements/signal/delivery-modes.test.ts +11 -31
  305. package/src/elements/signal/dry-run-replay.test.ts +1 -7
  306. package/src/elements/signal/dry-run-write-isolation.test.ts +1 -7
  307. package/src/elements/signal/key-ordering.test.ts +6 -26
  308. package/src/elements/signal/lease-reclaim.test.ts +2 -6
  309. package/src/elements/signal/optional-emit.test.ts +6 -9
  310. package/src/elements/signal/order-lifecycle.test.ts +4 -17
  311. package/src/elements/signal/orphan-messages.test.ts +4 -15
  312. package/src/elements/signal/reconcile.test.ts +2 -2
  313. package/src/elements/signal/runtime.ts +1 -1
  314. package/src/elements/signal/schema-emit.test.ts +4 -8
  315. package/src/elements/signal.test.ts +24 -49
  316. package/src/elements/signal.ts +9 -2
  317. package/src/elements/store/domain-ddl.test.ts +1 -1
  318. package/src/elements/store/live-http.test.ts +9 -4
  319. package/src/elements/store/prepare-row.test.ts +123 -0
  320. package/src/elements/store/resource.ts +43 -12
  321. package/src/elements/store/schema-decl.ts +117 -0
  322. package/src/elements/store/search-backfill.ts +205 -0
  323. package/src/elements/store/search-bind.ts +66 -0
  324. package/src/elements/store/search-bm25.ts +78 -0
  325. package/src/elements/store/search-ddl.ts +156 -0
  326. package/src/elements/store/search-embed-flow.ts +141 -0
  327. package/src/elements/store/search-errors.ts +34 -0
  328. package/src/elements/store/search-fusion.ts +112 -0
  329. package/src/elements/store/search-lsh.ts +179 -0
  330. package/src/elements/store/search-runtime.ts +300 -0
  331. package/src/elements/store/search.test.ts +144 -0
  332. package/src/elements/store/sql-session.test.ts +1 -0
  333. package/src/elements/store/sql-session.ts +83 -3
  334. package/src/elements/store/table.ts +23 -2
  335. package/src/elements/store/upsert-app.test.ts +1 -3
  336. package/src/elements/store.ts +50 -0
  337. package/src/full.ts +4 -2
  338. package/src/http.ts +4 -1
  339. package/src/i18n/catalogs/ar.ts +14 -6
  340. package/src/i18n/catalogs/en.ts +14 -6
  341. package/src/index.ts +6 -1
  342. package/src/kernel/adopt-barrel-fresh.test.ts +7 -7
  343. package/src/kernel/adopt-routes.ts +9 -1
  344. package/src/kernel/app-auth.ts +5 -0
  345. package/src/kernel/app.ts +102 -9
  346. package/src/kernel/auto-cache.test.ts +6 -12
  347. package/src/kernel/auto-registry.test.ts +3 -3
  348. package/src/kernel/boot-bind/ai.test.ts +22 -19
  349. package/src/kernel/boot-bind/ai.ts +5 -43
  350. package/src/kernel/boot-bind/clock.ts +23 -5
  351. package/src/kernel/boot-bind/honor-config.test.ts +4 -4
  352. package/src/kernel/boot.test.ts +11 -6
  353. package/src/kernel/boot.ts +66 -16
  354. package/src/kernel/boundary-contract.ts +93 -0
  355. package/src/kernel/budget.test.ts +1 -1
  356. package/src/kernel/call.ts +89 -0
  357. package/src/kernel/capability.ts +6 -0
  358. package/src/kernel/client-descriptor.ts +123 -0
  359. package/src/kernel/clock-timezone.test.ts +80 -0
  360. package/src/kernel/correlation.test.ts +1 -1
  361. package/src/kernel/dry-run.ts +11 -8
  362. package/src/kernel/effects-stamping.test.ts +42 -9
  363. package/src/kernel/effects.test.ts +4 -2
  364. package/src/kernel/effects.ts +51 -9
  365. package/src/kernel/errors-channel.ts +17 -0
  366. package/src/kernel/errors-live-resume.ts +3 -2
  367. package/src/kernel/errors-tenant.ts +7 -4
  368. package/src/kernel/errors.registry-helpers.ts +96 -0
  369. package/src/kernel/errors.registry.test.ts +139 -25
  370. package/src/kernel/errors.ts +102 -23
  371. package/src/kernel/external-effects.test.ts +196 -0
  372. package/src/kernel/flow.test.ts +13 -4
  373. package/src/kernel/flow.ts +69 -71
  374. package/src/kernel/fx-ask-telemetry.test.ts +2 -2
  375. package/src/kernel/fx-dead-letters.test.ts +3 -3
  376. package/src/kernel/fx-fetch.ts +55 -0
  377. package/src/kernel/fx-live-stream.ts +2 -2
  378. package/src/kernel/fx-live.test.ts +8 -8
  379. package/src/kernel/fx.test.ts +17 -11
  380. package/src/kernel/fx.ts +180 -57
  381. package/src/kernel/horizontal-child.ts +1 -1
  382. package/src/kernel/http-query.test.ts +3 -6
  383. package/src/kernel/index.ts +13 -3
  384. package/src/kernel/instance-id.ts +1 -1
  385. package/src/kernel/instances.test.ts +40 -0
  386. package/src/kernel/instances.ts +47 -9
  387. package/src/kernel/live-http.test.ts +6 -7
  388. package/src/kernel/live-http.ts +6 -2
  389. package/src/kernel/live-resume.test.ts +1 -1
  390. package/src/kernel/mcp-tool.test.ts +3 -3
  391. package/src/kernel/on.ts +98 -4
  392. package/src/kernel/pipeline.test.ts +12 -15
  393. package/src/kernel/plugin-elements.test.ts +1 -1
  394. package/src/kernel/stamp-http.test.ts +3 -3
  395. package/src/kernel/triggers.ts +210 -95
  396. package/src/kernel-entry.ts +0 -1
  397. package/src/manifest/diff.ts +11 -1
  398. package/src/manifest/fixtures/skyport.excerpt.json +1 -1
  399. package/src/manifest/types.ts +46 -2
  400. package/src/mcp/docs-index.ts +3 -3
  401. package/src/mcp/docs-mcp.test.ts +2 -2
  402. package/src/mcp/docs-tools.ts +1 -1
  403. package/src/okid.test.ts +56 -0
  404. package/src/okid.ts +55 -11
  405. package/src/plugins/anonymous.ts +8 -4
  406. package/src/plugins/auth/shared.ts +27 -3
  407. package/src/plugins/auth-delivery.mailpit.integration.test.ts +1 -1
  408. package/src/plugins/auth-methods.security.test.ts +561 -23
  409. package/src/plugins/config-source.ts +1 -1
  410. package/src/plugins/csrf.test.ts +65 -0
  411. package/src/plugins/index.ts +2 -0
  412. package/src/plugins/magic-link.ts +36 -16
  413. package/src/plugins/oauth/shared.ts +3 -1
  414. package/src/plugins/oauth.ts +22 -14
  415. package/src/plugins/otp.ts +37 -21
  416. package/src/plugins/passkey-webauthn.ts +9 -6
  417. package/src/plugins/passkey.ts +98 -26
  418. package/src/plugins/pre-account-hijack.test.ts +223 -0
  419. package/src/plugins/two-factor.ts +466 -37
  420. package/src/plugins/username.ts +19 -7
  421. package/src/release/build-lib.ts +1 -0
  422. package/src/release/limits.ts +2 -2
  423. package/src/release/measure.ts +1 -1
  424. package/src/runs/collect.test.ts +0 -2
  425. package/src/runtime/json-code-block.test.ts +250 -20
  426. package/src/runtime/json-code-block.ts +1261 -41
  427. package/src/term.test.ts +1 -1
  428. package/src/test/create-test-app.test.ts +12 -12
  429. package/src/test/live-signals.test.ts +6 -6
  430. package/src/test/provisions.integration.test.ts +10 -14
  431. package/src/test/reset-element-registries.ts +1 -1
  432. package/src/test/tenant-isolation.test.ts +12 -6
  433. package/site/content/docs/deployment/docker-swarm.mdx +0 -164
  434. package/site/content/docs/deployment/docker.mdx +0 -227
  435. package/site/content/docs/deployment/index.mdx +0 -83
  436. package/site/content/docs/deployment/kubernetes.mdx +0 -176
  437. package/site/content/docs/deployment/meta.json +0 -5
  438. package/site/content/docs/deployment/reverse-proxy.mdx +0 -234
  439. package/site/content/docs/elements/ai.mdx +0 -385
  440. package/site/content/docs/elements/channel.mdx +0 -346
  441. package/site/content/docs/elements/clock.mdx +0 -244
  442. package/site/content/docs/elements/flow.mdx +0 -420
  443. package/site/content/docs/elements/gate.mdx +0 -436
  444. package/site/content/docs/elements/signal.mdx +0 -380
  445. package/site/content/docs/elements/store.mdx +0 -1099
  446. package/site/content/docs/elements/vault.mdx +0 -405
  447. package/site/content/docs/get-started/basic-usage.mdx +0 -173
  448. package/site/content/docs/get-started/index.mdx +0 -43
  449. package/site/content/docs/get-started/installation.mdx +0 -220
  450. package/site/content/docs/get-started/introduction.mdx +0 -132
  451. package/site/content/docs/get-started/meta.json +0 -13
  452. package/site/content/docs/get-started/project-structure.mdx +0 -925
  453. package/site/content/docs/get-started/testing.mdx +0 -328
  454. package/site/content/docs/get-started/why.mdx +0 -162
  455. package/site/content/docs/recipes/llama-cpp.mdx +0 -151
  456. package/site/content/docs/recipes/ollama.mdx +0 -142
  457. package/site/content/docs/recipes/sglang.mdx +0 -105
  458. package/site/content/docs/recipes/vllm.mdx +0 -106
  459. package/site/content/docs/reference/cli.md +0 -232
  460. package/site/content/docs/reference/client.mdx +0 -469
  461. package/site/content/docs/reference/security.md +0 -74
  462. package/src/cli/ai-setup/detect-ollama.ts +0 -213
  463. package/src/cli/ai-setup/recommend.test.ts +0 -225
  464. package/src/cli/ai-setup/recommend.ts +0 -225
  465. package/src/cli/dev-controls.test.ts +0 -122
  466. package/src/cli/dev-controls.ts +0 -164
  467. package/src/cli/tui/DevLive.tsx +0 -126
  468. package/src/console/ui-next/dist/assets/access-page-De7Lc2JC.js +0 -4
  469. package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +0 -1
  470. package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +0 -1
  471. package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +0 -1
  472. package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +0 -1
  473. package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +0 -1
  474. package/src/console/ui-next/dist/assets/dagre.esm-ZwcdTuZZ.js +0 -1
  475. package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +0 -1
  476. package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +0 -1
  477. package/src/console/ui-next/dist/assets/duration-tone-sC3lGABz.js +0 -9
  478. package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +0 -1
  479. package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +0 -1
  480. package/src/console/ui-next/dist/assets/flows-page-RGy7VEA_.js +0 -1
  481. package/src/console/ui-next/dist/assets/highlighted-json-Awq7gYdu.js +0 -154
  482. package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +0 -1
  483. package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +0 -2
  484. package/src/console/ui-next/dist/assets/index-_rgpdVzo.js +0 -66
  485. package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +0 -1
  486. package/src/console/ui-next/dist/assets/observability-page-Ds6pcnh-.js +0 -4
  487. package/src/console/ui-next/dist/assets/preload-helper-oH4irX4C.js +0 -1
  488. package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +0 -1
  489. package/src/console/ui-next/dist/assets/react-dom-Bph1y7z7.js +0 -9
  490. package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +0 -18
  491. package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +0 -1
  492. package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +0 -1
  493. package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +0 -1
  494. package/src/console/ui-next/dist/assets/skeleton-D-czQJT6.js +0 -1
  495. package/src/console/ui-next/dist/assets/store-page-02xOiqIK.js +0 -41
  496. package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +0 -2
  497. package/src/console/ui-next/dist/assets/tree-expand-toggle-BtyhmWb4.js +0 -55
  498. package/src/console/ui-next/dist/assets/units-page-4rHOePuE.js +0 -1
  499. package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +0 -1
  500. package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +0 -1
  501. package/src/console/ui-next/dist/assets/vault-page-DISPgxLM.js +0 -2
  502. package/src/console/ui-next/dist/assets/xyflow-D7n4g6go.js +0 -7
  503. package/src/console/ui-next/dist/assets/xyflow-DZ0Ws1xk.css +0 -1
  504. package/src/docker/ollama-pull.ts +0 -232
  505. package/src/docker/recipes/llama-cpp.ts +0 -298
  506. package/src/docker/recipes/ollama.ts +0 -44
  507. package/src/docker/recipes/sglang.ts +0 -55
  508. package/src/docker/recipes/vllm.ts +0 -44
  509. package/src/drivers/ai-ollama-tools.integration.test.ts +0 -109
  510. package/src/drivers/ai-ollama.integration.test.ts +0 -184
  511. package/src/drivers/ai-ollama.ts +0 -389
  512. package/src/drivers/ollama.ts +0 -14
@@ -0,0 +1,365 @@
1
+ ---
2
+ title: "Rate Limits"
3
+ description: "gate.rate — KV-backed throttles with max, per, and keyBy; five strategies, sliding-window-counter by default."
4
+ icon: "Gauge"
5
+ source: "docs/spec/unified-theory.md"
6
+ ---
7
+
8
+ `gate.rate` protects Flows from abuse and runaway clients. Limits run as atomic takes on the KV
9
+ driver before `do`. Compose rates with policies via `gate.all` or list them on `.gate(...)`.
10
+
11
+ For developers throttling login, writes, and expensive reads on okengine — declare the window,
12
+ key the subject, attach on the trigger.
13
+
14
+ <Callout title="The one rule">
15
+ Pass options as one object: `{ max, per, keyBy? }`. There is no `gate.rate(name, { limit, window })`
16
+ form — the runtime names the gate from strategy and window.
17
+ </Callout>
18
+
19
+ ## Smallest Example
20
+
21
+ <Steps>
22
+
23
+ <Step>
24
+ ### Declare a rate gate
25
+
26
+ ```typescript title="src/core/gate.ts"
27
+ import { gate } from "okengine";
28
+
29
+ export const loginThrottle = gate.rate({
30
+ strategy: "sliding-window-counter", // default when omitted
31
+ max: 5,
32
+ per: "1m",
33
+ keyBy: "ip",
34
+ description: "Login attempts per IP",
35
+ });
36
+ ```
37
+
38
+ The runtime name is `rate:sliding-window-counter:5/1m` (strategy · max · per).
39
+
40
+ </Step>
41
+
42
+ <Step>
43
+ ### Attach on the trigger
44
+
45
+ ```typescript title="src/flows/auth/login.ts"
46
+ import { on, flow, http, gate } from "okengine";
47
+ import { loginThrottle } from "@/core/gate";
48
+
49
+ export const login = on(
50
+ http.post("/auth/sign-in/email").gate(gate.public, loginThrottle),
51
+ flow({
52
+ do: async (input, fx) => {
53
+ // At most 5 takes per IP per minute
54
+ return input;
55
+ },
56
+ }),
57
+ );
58
+ ```
59
+
60
+ </Step>
61
+
62
+ <Step>
63
+ ### See a burned quota
64
+
65
+ ```json
66
+ {
67
+ "data": null,
68
+ "error": {
69
+ "code": "RateLimited",
70
+ "message": "Too many requests. Try again later.",
71
+ "data": { "retryAfterMs": 42000 }
72
+ }
73
+ }
74
+ ```
75
+
76
+ Status is `429 Too Many Requests`. Clients should wait `retryAfterMs` before retrying.
77
+
78
+ </Step>
79
+
80
+ </Steps>
81
+
82
+ ## Progressive Patterns
83
+
84
+ From the default counter to bursty buckets and subject keys:
85
+
86
+ <Tabs items={["Default", "Token bucket", "keyBy", "Compose"]}>
87
+
88
+ <Tab value="Default">
89
+
90
+ Omit `strategy` for `sliding-window-counter` — best accuracy-to-cost ratio (two KV keys, no
91
+ boundary bursts):
92
+
93
+ ```typescript
94
+ export const fair = gate.rate({ max: 60, per: "1m", keyBy: "user" });
95
+ ```
96
+
97
+ </Tab>
98
+
99
+ <Tab value="Token bucket">
100
+
101
+ Allow short bursts that refill over `per`:
102
+
103
+ ```typescript
104
+ export const bursty = gate.rate({
105
+ strategy: "token-bucket",
106
+ max: 20,
107
+ per: "1m",
108
+ keyBy: "user",
109
+ });
110
+ ```
111
+
112
+ Use `leaky-bucket` when you need a smooth outbound rate instead of burst capacity.
113
+
114
+ </Tab>
115
+
116
+ <Tab value="keyBy">
117
+
118
+ Subject dimension for the take key:
119
+
120
+ | `keyBy` | Subject |
121
+ | -------------------- | ------------------------------------------------- |
122
+ | `"user"` | `auth.userId` (else `meta.userId`, else `"anon"`) |
123
+ | `"ip"` | `meta.ip` (else `"0.0.0.0"`) |
124
+ | `"operator"` | `operator.id` (else `"anon"`) |
125
+ | `"global"` / omitted | Shared `"global"` bucket |
126
+ | other string | `meta[keyBy]` when present, else the literal |
127
+
128
+ ```typescript
129
+ gate.rate({ max: 100, per: "1h", keyBy: "ip" });
130
+ gate.rate({ max: 10, per: "1s" }); // global
131
+ ```
132
+
133
+ </Tab>
134
+
135
+ <Tab value="Compose">
136
+
137
+ Rates sit on the same chain as policies — first denial wins:
138
+
139
+ ```typescript title="src/flows/notes/create.ts"
140
+ import { on, flow, http, gate } from "okengine";
141
+
142
+ export const notesMutate = gate.all(
143
+ gate.policy("member", ({ auth }) => !!auth.verified),
144
+ gate.scope("notes:write"),
145
+ gate.rate({ max: 60, per: "1m", keyBy: "user" }),
146
+ );
147
+
148
+ export const create = on(
149
+ http.post().gate(notesMutate),
150
+ flow({
151
+ do: async (input, fx) => fx.json.create({ id: fx.id(), ...input }),
152
+ }),
153
+ );
154
+ ```
155
+
156
+ Put identity policies **before** rates when anonymous traffic should 401 instead of burning
157
+ quota under `"anon"`.
158
+
159
+ </Tab>
160
+
161
+ </Tabs>
162
+
163
+ ## Strategies
164
+
165
+ | Strategy | Best for | Cost shape |
166
+ | ------------------------ | -------------------------- | --------------------------- |
167
+ | `sliding-window-counter` | Default — accuracy vs cost | Two keys, weighted estimate |
168
+ | `token-bucket` | Bursty traffic with refill | One hash (`tokens`, `ts`) |
169
+ | `leaky-bucket` | Smooth outbound rate | One hash (`level`, `ts`) |
170
+ | `fixed-window` | Simple reset intervals | One counter per bucket |
171
+ | `sliding-log` | Strict precision | ZSET of event timestamps |
172
+
173
+ All five run as atomic Lua on the KV driver (memory / redis EVAL).
174
+
175
+ <Callout title="Detailed section">
176
+ Pick the strategy from product physics, not fashion. The default covers most HTTP write paths;
177
+ switch only when you need bursts, smooth leak, or exact event logs.
178
+ </Callout>
179
+
180
+ <Tabs items={["sliding-window-counter", "token-bucket", "leaky-bucket", "fixed-window", "sliding-log"]}>
181
+
182
+ <Tab value="sliding-window-counter">
183
+
184
+ Near-exact rolling window without boundary spikes of a naive fixed window:
185
+
186
+ ```typescript
187
+ gate.rate({
188
+ strategy: "sliding-window-counter", // or omit — this is the default
189
+ max: 100,
190
+ per: "1m",
191
+ keyBy: "user",
192
+ });
193
+ ```
194
+
195
+ Uses current + previous window counters with a time-weighted estimate.
196
+
197
+ </Tab>
198
+
199
+ <Tab value="token-bucket">
200
+
201
+ Burst up to `max`, then refill smoothly across `per`:
202
+
203
+ ```typescript
204
+ gate.rate({
205
+ strategy: "token-bucket",
206
+ max: 20,
207
+ per: "1m",
208
+ keyBy: "ip",
209
+ });
210
+ ```
211
+
212
+ **Consequence:** a quiet client can spend its full burst immediately after idle time.
213
+
214
+ </Tab>
215
+
216
+ <Tab value="leaky-bucket">
217
+
218
+ Smooth outbound rate — rejects when the “bucket level” would exceed `max`:
219
+
220
+ ```typescript
221
+ gate.rate({
222
+ strategy: "leaky-bucket",
223
+ max: 50,
224
+ per: "1m",
225
+ keyBy: "user",
226
+ });
227
+ ```
228
+
229
+ Prefer this for upstream APIs that punish spikes more than averages.
230
+
231
+ </Tab>
232
+
233
+ <Tab value="fixed-window">
234
+
235
+ Simple counter that resets each `per` bucket:
236
+
237
+ ```typescript
238
+ gate.rate({
239
+ strategy: "fixed-window",
240
+ max: 1000,
241
+ per: "1h",
242
+ keyBy: "global",
243
+ });
244
+ ```
245
+
246
+ Cheapest layout; allows a double-burst at the bucket boundary.
247
+
248
+ </Tab>
249
+
250
+ <Tab value="sliding-log">
251
+
252
+ Exact event log (ZSET of timestamps) — strict precision at higher cost:
253
+
254
+ ```typescript
255
+ gate.rate({
256
+ strategy: "sliding-log",
257
+ max: 10,
258
+ per: "1m",
259
+ keyBy: "ip",
260
+ });
261
+ ```
262
+
263
+ Use for sensitive endpoints where approximate counters are not enough.
264
+
265
+ </Tab>
266
+
267
+ </Tabs>
268
+
269
+ ## Options
270
+
271
+ | Option | Type | Default | Meaning |
272
+ | ------------- | -------------- | ------------------------ | -------------------------------------------- |
273
+ | `max` | `number` | required | Takes allowed within `per` (`> 0`) |
274
+ | `per` | `string` | required | Window / refill (`"1m"`, `"60s"`, `"1h"`, …) |
275
+ | `strategy` | `RateStrategy` | `sliding-window-counter` | Algorithm id |
276
+ | `keyBy` | `string` | global | Subject dim (`"ip"`, `"user"`, …) |
277
+ | `overridable` | `boolean` | `false` | Console may override `max` / `per` in Store |
278
+ | `description` | `string` | — | Human label for Console / docs |
279
+
280
+ **Consequence:** rate gates need a KV driver in the app. Without KV, evaluation denies with
281
+ telemetry reason `"rate gate requires kv"` and the typed code is still `RateLimited`.
282
+
283
+ Declare-time guards:
284
+
285
+ - `gate.rate: max must be a positive number`
286
+ - `gate.rate: per is required`
287
+
288
+ Invalid `per` at evaluation → deny with `reason: "invalid per: …"`.
289
+
290
+ ## Naming & Overrides
291
+
292
+ The runtime name is always `rate:{strategy}:{max}/{per}`:
293
+
294
+ ```text
295
+ rate:sliding-window-counter:60/1m
296
+ rate:token-bucket:20/1m
297
+ ```
298
+
299
+ When `overridable: true`, Console may tune `max` / `per` in Store without a code deploy. Leave
300
+ it `false` (default) for hard product limits.
301
+
302
+ `oke({ gate: { rateLimit: { enabled } } })` controls whether **auth path** Flows attach
303
+ stricter presets. Default is `true` when `gate.auth` is on, `false` otherwise — it does not
304
+ replace your own `gate.rate` declarations.
305
+
306
+ ## Troubleshooting
307
+
308
+ <Accordions>
309
+
310
+ <Accordion title="429 RateLimited immediately">
311
+ Quota burned for that subject. Inspect `retryAfterMs`, widen `per`, raise `max`, or change `keyBy`
312
+ so clients do not share one global bucket unintentionally.
313
+ </Accordion>
314
+
315
+ <Accordion title='Denial reason "rate gate requires kv"'>
316
+ Configure a KV driver (`memory` for local, `redis` for multi-instance). Policy-only apps can omit
317
+ KV; any `gate.rate` on a hot path cannot. The HTTP envelope is still `RateLimited`.
318
+ </Accordion>
319
+
320
+ <Accordion title="Anonymous traffic burns user quotas">
321
+ With `keyBy: "user"`, missing `userId` maps to `"anon"` — shared. Put a member policy before the
322
+ rate, or key by `"ip"` for public endpoints.
323
+ </Accordion>
324
+
325
+ <Accordion title="TypeError: gate.rate: max must be a positive number">
326
+ `max` must be `> 0`. Zero and negatives fail at declare.
327
+ </Accordion>
328
+
329
+ <Accordion title="TypeError: gate.rate: per is required">
330
+ Pass a duration string (`"1m"`, `"30s"`, …). Empty / missing `per` fails at declare.
331
+ </Accordion>
332
+
333
+ <Accordion title='Denial reason "invalid per: …"'>
334
+ The duration string did not parse to a positive window. Use `ms|s|m|h|d` style values the Clock
335
+ duration parser accepts (`"1m"`, `"60s"`, `"1h"`).
336
+ </Accordion>
337
+
338
+ </Accordions>
339
+
340
+ ## Learn more
341
+
342
+ - [Gate Overview](/docs/elements/gate) — chain order and denial mapping
343
+ - [Authorization](/docs/elements/gate/authorization) — policies on the same `.gate(...)`
344
+ - [Store · KV](/docs/elements/store/kv) — KV facet drivers
345
+ - [HTTP](/docs/elements/flow/http) — attach rates on REST routes
346
+
347
+ ## Next
348
+
349
+ <Cards>
350
+ <Card
351
+ title="Tenancy"
352
+ description="Resolve fx.tenant.id for B2B isolation."
353
+ href="/docs/elements/gate/tenancy"
354
+ />
355
+ <Card
356
+ title="RLS"
357
+ description="Stamp Gate identity into SQL row policies."
358
+ href="/docs/elements/gate/rls"
359
+ />
360
+ <Card
361
+ title="Vault Element"
362
+ description="Secrets and protected configuration."
363
+ href="/docs/elements/vault"
364
+ />
365
+ </Cards>
@@ -0,0 +1,303 @@
1
+ ---
2
+ title: "RLS"
3
+ description: "Row-level security — Gate identity stamped into SQL so store.schema.policy helpers filter every fx.store query."
4
+ icon: "Rows3"
5
+ source: "docs/spec/unified-theory.md"
6
+ ---
7
+
8
+ Gate decides whether a caller may run a Flow. **RLS** decides which SQL rows that
9
+ caller may see or change.
10
+
11
+ After `.gate(...)` passes, `fx.store` stamps the principal into Postgres session GUCs
12
+ (`oke.gate` · `oke.user` · `oke.scopes` · `oke.tenant`) so table policies enforce the
13
+ same identity.
14
+
15
+ For developers shipping multi-user SQL on okengine — declare Gate policies, attach them
16
+ on the trigger, and mirror them on the table with `store.schema.policy.*`.
17
+
18
+ <Callout title="The one rule">
19
+ Put row predicates on the **table** (`store.schema.policy.gate` / `owner` / `scope` / `tenant`).
20
+ Put permission to **act** on the **trigger** (`.gate(...)`). Never invent a separate middleware
21
+ layer — the stamp rides every `fx.store` call.
22
+ </Callout>
23
+
24
+ ## Smallest Example
25
+
26
+ <Steps>
27
+
28
+ <Step>
29
+ ### Declare a Gate policy and an RLS table
30
+
31
+ ```typescript title="src/core/gate.ts"
32
+ import { gate } from "okengine";
33
+
34
+ export const member = gate.policy("member", ({ auth }) => !!auth.verified);
35
+ ```
36
+
37
+ ```typescript title="src/db/schema.decl.ts"
38
+ import { store, field } from "okengine";
39
+
40
+ export const tasks = store.schema.table(
41
+ "tasks",
42
+ {
43
+ id: field.id().primaryKey(),
44
+ owner: field.text().notNull(),
45
+ title: field.text().notNull(),
46
+ },
47
+ [
48
+ store.schema.policy.gate("member", { for: "select" }),
49
+ store.schema.policy.owner("owner", { for: "all" }),
50
+ ],
51
+ );
52
+ ```
53
+
54
+ </Step>
55
+
56
+ <Step>
57
+ ### Gate the route and query through `fx.store`
58
+
59
+ ```typescript title="src/flows/tasks/list.ts"
60
+ import { on, flow, http } from "okengine";
61
+ import { member } from "@/core/gate";
62
+ import { db, tasks } from "@/schema";
63
+
64
+ export const list = on(
65
+ http.get().gate(member),
66
+ flow({
67
+ do: async (_, fx) => fx.store(db).select().from(tasks),
68
+ }),
69
+ );
70
+ ```
71
+
72
+ </Step>
73
+
74
+ <Step>
75
+ ### What the caller sees
76
+
77
+ Alice’s session stamps `oke.gate = 'member'` and `oke.user = '<alice>'`. SELECT
78
+ policies that require `oke.gate() = 'member'` and `owner = oke.user()` return only
79
+ Alice’s rows — even if the Flow’s `where` clause is empty.
80
+
81
+ Use an RLS-capable SQL driver (`postgres` / `pglite`). The `memory` SQL driver does
82
+ not enforce row policies.
83
+
84
+ </Step>
85
+
86
+ </Steps>
87
+
88
+ ## Progressive Patterns
89
+
90
+ From a gate-name check to owner columns, scopes, and tenants:
91
+
92
+ <Tabs items={["Gate", "Owner", "Scope", "Tenant"]}>
93
+
94
+ <Tab value="Gate">
95
+
96
+ `store.schema.policy.gate(name)` stamps `oke.gate() = '…'` (default `for: "select"`).
97
+ The name must match a Gate policy on the trigger (rate gates are skipped when picking
98
+ the stamp):
99
+
100
+ ```typescript
101
+ store.schema.policy.gate("member", { for: "select" });
102
+ ```
103
+
104
+ **Consequence:** the first non-rate gate on `.gate(...)` becomes `oke.gate` for that
105
+ request. Put the policy you want stamped **first** when the chain mixes policies and
106
+ rates.
107
+
108
+ </Tab>
109
+
110
+ <Tab value="Owner">
111
+
112
+ `store.schema.policy.owner(column)` stamps `column = oke.user()` (default `for: "all"`):
113
+
114
+ ```typescript
115
+ store.schema.policy.owner("owner", { for: "all" });
116
+ ```
117
+
118
+ `oke.user()` is the stamped `fx.auth.userId` (empty string when anonymous / public).
119
+
120
+ </Tab>
121
+
122
+ <Tab value="Scope">
123
+
124
+ `store.schema.policy.scope(scope)` stamps `oke.has_scope('…')` (default `for: "insert"`).
125
+ Pass a string or a `gate.scope(...)` handle so the scope stays single-sourced:
126
+
127
+ ```typescript
128
+ import { gate } from "okengine";
129
+
130
+ export const bookingCreate = gate.scope("booking:create");
131
+
132
+ // On the table:
133
+ store.schema.policy.scope(bookingCreate, { for: "insert" });
134
+ // or: store.schema.policy.scope("booking:create", { for: "insert" });
135
+ ```
136
+
137
+ </Tab>
138
+
139
+ <Tab value="Tenant">
140
+
141
+ When `gate.auth.tenant` is on, every table needs a tenant policy or an explicit opt-out:
142
+
143
+ ```typescript
144
+ store.schema.policy.tenant("tenantId"); // default for: "all" → tenantId = oke.tenant()
145
+ // or shared catalog tables:
146
+ store.schema.unscoped();
147
+ ```
148
+
149
+ See [Tenancy](/docs/elements/gate/tenancy). Extract fails without one:
150
+
151
+ ```text
152
+ extract: table "{store}.{table}" needs store.schema.policy.tenant(...) or store.schema.unscoped() when gate.auth.tenant is on
153
+ ```
154
+
155
+ </Tab>
156
+
157
+ </Tabs>
158
+
159
+ ## Identity Stamp
160
+
161
+ After Gate allows, the SQL session prelude sets:
162
+
163
+ | GUC / helper | Source | Used by |
164
+ | ------------------ | ---------------------------------- | --------------- |
165
+ | `oke.gate()` | First non-rate gate on the trigger | `policy.gate` |
166
+ | `oke.user()` | `fx.auth.userId` | `policy.owner` |
167
+ | `oke.has_scope(…)` | `fx.auth.scopes` (JSON) | `policy.scope` |
168
+ | `oke.tenant()` | `fx.tenant.id` when tenancy is on | `policy.tenant` |
169
+
170
+ The stamp `SET LOCAL ROLE oke_app` and turns `row_security` on so table owners cannot
171
+ bypass policies on the hot path.
172
+
173
+ | Plane / mode | Stamp? |
174
+ | --------------------------- | -------------------------- |
175
+ | User-plane Flow, gated | Yes |
176
+ | `plane: "operator"` | No — operator bypasses RLS |
177
+ | Console / Call API `bypass` | No |
178
+
179
+ ## Policy Helpers
180
+
181
+ Extras are the **third argument** of `store.schema.table(name, cols, extras)`:
182
+
183
+ | Helper | Default `for` | Predicate |
184
+ | ---------------------------------- | ------------- | --------------------------------------- |
185
+ | `store.schema.policy.gate(name)` | `select` | `oke.gate() = '…'` |
186
+ | `store.schema.policy.owner(col)` | `all` | `col = oke.user()` |
187
+ | `store.schema.policy.scope(scope)` | `insert` | `oke.has_scope('…')` |
188
+ | `store.schema.policy.tenant(col)` | `all` | `col = oke.tenant()` |
189
+ | `store.schema.rls()` | — | Enable RLS with no policies yet |
190
+ | `store.schema.unscoped()` | — | Opt out of tenant requirement |
191
+ | `store.schema.policy(name, opts)` | — | Raw `using` / `withCheck` / `as` / `to` |
192
+
193
+ `for` accepts `select` · `insert` · `update` · `delete` · `all`. Optional `as`:
194
+ `"permissive"` (default) or `"restrictive"`. Optional `to` limits Postgres roles.
195
+
196
+ ```typescript title="src/db/schema.decl.ts"
197
+ import { store, field } from "okengine";
198
+ import { member, bookingCreate } from "@/core/gate";
199
+
200
+ export const bookings = store.schema.table(
201
+ "bookings",
202
+ {
203
+ id: field.id().primaryKey(),
204
+ owner: field.text().notNull(),
205
+ tenantId: field.text().notNull(),
206
+ },
207
+ [
208
+ store.schema.policy.gate("member", { for: "select" }),
209
+ store.schema.policy.owner("owner", { for: "all" }),
210
+ store.schema.policy.scope(bookingCreate, { for: "insert" }),
211
+ store.schema.policy.tenant("tenantId"),
212
+ ],
213
+ );
214
+ ```
215
+
216
+ <Callout title="Detailed section">
217
+ Schema extras, relations, and emit live under [Store · SQL · Schema Extras &
218
+ RLS](/docs/elements/store/sql#schema-extras--rls). This page is the Gate ↔ row identity contract.
219
+ </Callout>
220
+
221
+ ## Composition
222
+
223
+ Multiple **PERMISSIVE** policies for the same command OR together — a row is visible if
224
+ any permissive policy passes. **RESTRICTIVE** policies AND with the rest (`as: "restrictive"`).
225
+
226
+ ```typescript
227
+ [
228
+ store.schema.policy.gate("member", { for: "select" }),
229
+ store.schema.policy.owner("owner", { for: "select" }),
230
+ ];
231
+ ```
232
+
233
+ A `member` principal sees rows that pass the gate predicate **or** the owner predicate
234
+ (for SELECT). Tighten with restrictive policies or a single compound raw `using`.
235
+
236
+ ## Live Queries & Resources
237
+
238
+ Resource live (`store.resource({ live: true })`) and handwritten `liveQuery` classify
239
+ CDC events through the same RLS stamp. Needs `postgres` / `pglite` and a gated identity.
240
+
241
+ See [HTTP · Resource Live](/docs/elements/flow/http#resources) and
242
+ [Store · SQL](/docs/elements/store/sql).
243
+
244
+ ## Troubleshooting
245
+
246
+ <Accordions>
247
+
248
+ <Accordion title="Every query returns zero rows">
249
+ Stamp or policies are wrong. Confirm `.gate(member)` (or matching name), that `owner` / `tenantId`
250
+ columns match `oke.user()` / `oke.tenant()`, and that the driver is `postgres` or `pglite` — not
251
+ `memory`.
252
+ </Accordion>
253
+
254
+ <Accordion title="Extract: needs store.schema.policy.tenant or unscoped">
255
+ Cause: `extract: table "{store}.{table}" needs store.schema.policy.tenant(...) or
256
+ store.schema.unscoped() when gate.auth.tenant is on`. Add a tenant column policy or mark the table
257
+ shared.
258
+ </Accordion>
259
+
260
+ <Accordion title="live query requires an RLS-capable SQL driver">
261
+ Cause: `live query for "…" requires an RLS-capable SQL driver (postgres / pglite)`. Switch the SQL
262
+ driver and attach a Gate chain so the stamp has a principal.
263
+ </Accordion>
264
+
265
+ <Accordion title="Operator / Console sees all rows">
266
+ `plane: "operator"` and Call API `bypass` skip the RLS stamp by design. User-plane Flows always
267
+ stamp when gated.
268
+ </Accordion>
269
+
270
+ <Accordion title="Wrong gate stamped on a long chain">
271
+ The first non-rate gate name wins for `oke.gate()`. Reorder `.gate(member, write, rate)` so the
272
+ policy you want in SQL predicates comes before rates and secondary scopes when those scopes use
273
+ `oke.has_scope` instead of `oke.gate`.
274
+ </Accordion>
275
+
276
+ </Accordions>
277
+
278
+ ## Learn more
279
+
280
+ - [Authorization](/docs/elements/gate/authorization) — `gate.policy` / `gate.scope` on triggers
281
+ - [Tenancy](/docs/elements/gate/tenancy) — `fx.tenant.id` and `policy.tenant`
282
+ - [Store · SQL](/docs/elements/store/sql) — schema extras, session handle, drivers
283
+ - [HTTP](/docs/elements/flow/http) — resource live + RLS
284
+
285
+ ## Next
286
+
287
+ <Cards>
288
+ <Card
289
+ title="Rate Limits"
290
+ description="Throttle with gate.rate on the same chain."
291
+ href="/docs/elements/gate/rate-limits"
292
+ />
293
+ <Card
294
+ title="Tenancy"
295
+ description="Resolve fx.tenant.id and policy.tenant."
296
+ href="/docs/elements/gate/tenancy"
297
+ />
298
+ <Card
299
+ title="Store · SQL"
300
+ description="Schema extras, fx.store, and RLS-capable drivers."
301
+ href="/docs/elements/store/sql"
302
+ />
303
+ </Cards>