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.
- package/AGENTS.md +18 -10
- package/README.md +2 -2
- package/package.json +51 -50
- package/site/content/docs/ai/index.mdx +25 -6
- package/site/content/docs/ai/llms-txt.mdx +1 -1
- package/site/content/docs/ai/meta.json +1 -1
- package/site/content/docs/ai/skills.mdx +4 -1
- package/site/content/docs/ai/try-it.mdx +58 -0
- package/site/content/docs/client/auth.mdx +208 -0
- package/site/content/docs/client/calling.mdx +451 -0
- package/site/content/docs/client/index.mdx +209 -0
- package/site/content/docs/client/live.mdx +234 -0
- package/site/content/docs/client/meta.json +5 -0
- package/site/content/docs/client/react.mdx +249 -0
- package/site/content/docs/elements/ai/agents.mdx +232 -0
- package/site/content/docs/elements/ai/index.mdx +341 -0
- package/site/content/docs/elements/ai/mcp.mdx +275 -0
- package/site/content/docs/elements/ai/meta.json +5 -0
- package/site/content/docs/elements/ai/models.mdx +283 -0
- package/site/content/docs/elements/ai/prompts.mdx +256 -0
- package/site/content/docs/elements/channel/email.mdx +266 -0
- package/site/content/docs/elements/channel/index.mdx +344 -0
- package/site/content/docs/elements/channel/meta.json +5 -0
- package/site/content/docs/elements/channel/push.mdx +218 -0
- package/site/content/docs/elements/channel/receipts.mdx +206 -0
- package/site/content/docs/elements/channel/sms.mdx +264 -0
- package/site/content/docs/elements/channel/whatsapp.mdx +220 -0
- package/site/content/docs/elements/clock/index.mdx +300 -0
- package/site/content/docs/elements/clock/meta.json +5 -0
- package/site/content/docs/elements/clock/schedules.mdx +508 -0
- package/site/content/docs/elements/clock/sleep.mdx +445 -0
- package/site/content/docs/elements/flow/consumers.mdx +707 -0
- package/site/content/docs/elements/flow/http.mdx +1080 -0
- package/site/content/docs/elements/flow/index.mdx +618 -0
- package/site/content/docs/elements/flow/meta.json +5 -0
- package/site/content/docs/elements/flow/routing.mdx +559 -0
- package/site/content/docs/elements/flow/workflows.mdx +640 -0
- package/site/content/docs/elements/gate/auth.mdx +371 -0
- package/site/content/docs/elements/gate/authorization.mdx +298 -0
- package/site/content/docs/elements/gate/index.mdx +418 -0
- package/site/content/docs/elements/gate/meta.json +5 -0
- package/site/content/docs/elements/gate/rate-limits.mdx +365 -0
- package/site/content/docs/elements/gate/rls.mdx +303 -0
- package/site/content/docs/elements/gate/tenancy.mdx +372 -0
- package/site/content/docs/elements/index.mdx +52 -11
- package/site/content/docs/elements/signal/broadcast.mdx +517 -0
- package/site/content/docs/elements/signal/index.mdx +352 -0
- package/site/content/docs/elements/signal/live.mdx +590 -0
- package/site/content/docs/elements/signal/meta.json +5 -0
- package/site/content/docs/elements/signal/once.mdx +596 -0
- package/site/content/docs/elements/store/files.mdx +659 -0
- package/site/content/docs/elements/store/index.mdx +310 -0
- package/site/content/docs/elements/store/kv.mdx +629 -0
- package/site/content/docs/elements/store/meta.json +5 -0
- package/site/content/docs/elements/store/search.mdx +946 -0
- package/site/content/docs/elements/store/sql.mdx +937 -0
- package/site/content/docs/elements/vault/config.mdx +256 -0
- package/site/content/docs/elements/vault/index.mdx +356 -0
- package/site/content/docs/elements/vault/meta.json +5 -0
- package/site/content/docs/elements/vault/rotation.mdx +273 -0
- package/site/content/docs/elements/vault/secrets.mdx +285 -0
- package/site/content/docs/index.mdx +78 -30
- package/site/content/docs/meta.json +11 -8
- package/site/content/docs/plugins/cors.mdx +2 -1
- package/site/content/docs/plugins/csrf.mdx +4 -1
- package/site/content/docs/plugins/headers.mdx +2 -1
- package/site/content/docs/plugins/ip-allowlist.mdx +2 -1
- package/site/content/docs/plugins/magic-link.mdx +6 -0
- package/site/content/docs/plugins/maintenance-mode.mdx +2 -1
- package/site/content/docs/plugins/oauth.mdx +1 -1
- package/site/content/docs/plugins/otp.mdx +12 -1
- package/site/content/docs/plugins/passkey.mdx +39 -15
- package/site/content/docs/plugins/two-factor.mdx +91 -34
- package/site/content/docs/plugins/username.mdx +1 -1
- package/site/content/docs/recipes/caddy.mdx +3 -3
- package/site/content/docs/recipes/index.mdx +18 -28
- package/site/content/docs/recipes/mailpit.mdx +1 -1
- package/site/content/docs/recipes/meilisearch.mdx +3 -3
- package/site/content/docs/recipes/meta.json +1 -4
- package/site/content/docs/recipes/nginx.mdx +2 -2
- package/site/content/docs/recipes/openrouter.mdx +283 -0
- package/site/content/docs/recipes/pgdog.mdx +3 -3
- package/site/content/docs/recipes/postgres.mdx +1 -2
- package/site/content/docs/recipes/rustfs.mdx +2 -2
- package/site/content/docs/recipes/traefik.mdx +3 -3
- package/site/content/docs/reference/cli.mdx +231 -0
- package/site/content/docs/reference/configuration.mdx +30 -10
- package/site/content/docs/reference/environment-variables.mdx +93 -20
- package/site/content/docs/reference/errors.mdx +132 -29
- package/site/content/docs/reference/fx.mdx +137 -73
- package/site/content/docs/reference/index.mdx +13 -21
- package/site/content/docs/reference/meta.json +4 -5
- package/site/content/docs/reference/okid.mdx +42 -17
- package/site/content/docs/reference/plugins.mdx +4 -3
- package/site/content/docs/reference/security.mdx +197 -0
- package/site/content/docs/understand/meta.json +5 -0
- package/site/content/docs/understand/the-anatomy.mdx +132 -0
- package/site/content/docs/understand/the-model.mdx +32 -0
- package/site/content/docs/understand/the-problem.mdx +74 -0
- package/site/content/docs/understand/the-vocabulary.mdx +26 -0
- package/src/auth/api-keys.ts +2 -1
- package/src/auth/bindings.ts +64 -16
- package/src/auth/gate-auth.test.ts +6 -1
- package/src/auth/identity.ts +152 -5
- package/src/auth/index.ts +29 -0
- package/src/auth/invites.ts +2 -1
- package/src/auth/method-context.ts +41 -0
- package/src/auth/oauth-as/crypto.ts +2 -1
- package/src/auth/oauth-as/stores.ts +2 -1
- package/src/auth/operator.ts +2 -1
- package/src/auth/sessions-jwt.test.ts +77 -0
- package/src/auth/sessions.ts +20 -4
- package/src/auth/tenants.ts +3 -2
- package/src/auth/two-factor-challenge.test.ts +61 -0
- package/src/auth/two-factor-challenge.ts +284 -0
- package/src/auth/verification.ts +5 -0
- package/src/bench/REPORT.md +49 -0
- package/src/bench/g03-signal-once.bench.ts +1 -1
- package/src/bench/g10-observability-contention.bench.ts +1 -1
- package/src/bench/g17-hybrid-search.bench.ts +572 -0
- package/src/bench/load-app.ts +2 -10
- package/src/cli/ai-setup/ai-setup.test.ts +331 -42
- package/src/cli/ai-setup/apply.ts +294 -52
- package/src/cli/ai-setup/catalog.ts +238 -1342
- package/src/cli/ai-setup/index.ts +14 -99
- package/src/cli/ai-setup/prompts.ts +60 -443
- package/src/cli/ask-seed.test.ts +96 -0
- package/src/cli/ask-seed.ts +82 -0
- package/src/cli/ask-vault-gaps.test.ts +84 -0
- package/src/cli/ask-vault-gaps.ts +146 -0
- package/src/cli/build.ts +2 -2
- package/src/cli/client-add.test.ts +26 -1
- package/src/cli/client-add.ts +106 -27
- package/src/cli/competitor-mention-removal.test.ts +1 -1
- package/src/cli/db-seed.ts +2 -0
- package/src/cli/db.ts +150 -11
- package/src/cli/dev.ts +119 -211
- package/src/cli/docker-clean.ts +2 -2
- package/src/cli/docker-cli.test.ts +1 -1
- package/src/cli/doctor-diff.ts +4 -2
- package/src/cli/doctor-pii.test.ts +1 -1
- package/src/cli/doctor.ts +14 -1
- package/src/cli/hero-meta.test.ts +4 -2
- package/src/cli/load-config.images.test.ts +8 -10
- package/src/cli/load-config.ts +1 -1
- package/src/cli/registry.ts +22 -10
- package/src/cli/replay.ts +3 -1
- package/src/cli/start.ts +1 -1
- package/src/cli/tui/keys.ts +2 -3
- package/src/client/auth/cookies.ts +74 -0
- package/src/client/auth/create-auth-client.ts +636 -0
- package/src/client/auth/denials.ts +99 -0
- package/src/client/auth/session.ts +283 -0
- package/src/client/auth.test.ts +251 -0
- package/src/client/auth.ts +39 -114
- package/src/client/create-with-session.ts +245 -0
- package/src/client/create.ts +51 -5
- package/src/client/index.ts +11 -1
- package/src/client/live.ts +11 -79
- package/src/client/notes-contract.test.ts +5 -10
- package/src/client/sse.ts +137 -0
- package/src/client/stream.ts +147 -0
- package/src/client/transport.test.ts +32 -0
- package/src/client/transport.ts +123 -26
- package/src/client/types.ts +118 -14
- package/src/client-react/index.ts +185 -24
- package/src/client-react/use-live-query.ts +13 -3
- package/src/compiler/aot.test.ts +7 -4
- package/src/compiler/effects-embed.test.ts +56 -0
- package/src/compiler/effects-fetch.test.ts +43 -0
- package/src/compiler/effects-infer.ts +58 -2
- package/src/compiler/extract.test.ts +258 -44
- package/src/compiler/extract.ts +566 -48
- package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -4
- package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +2 -8
- package/src/compiler/fixtures/skyport.expected.json +4 -4
- package/src/compiler/fixtures/triggers/five-triggers.ts +14 -10
- package/src/compiler/response.ts +2 -2
- package/src/compiler/search-writer-isolation.test.ts +40 -0
- package/src/config/index.ts +11 -0
- package/src/console/server/app.ts +17 -23
- package/src/console/server/bind.ts +4 -0
- package/src/console/server/console.test.ts +2 -2
- package/src/console/server/flows-invoke.test.ts +50 -34
- package/src/console/server/flows.ts +436 -255
- package/src/console/server/runs-ingest.test.ts +6 -3
- package/src/console/server/serve.ts +1 -1
- package/src/console/server/signals.test.ts +1 -5
- package/src/console/server/signals.ts +8 -6
- package/src/console/server/state.ts +6 -1
- package/src/console/server/store.ts +1 -1
- package/src/console/ui-next/dist/assets/FileExportIcon-Ck-5od4R.js +1 -0
- package/src/console/ui-next/dist/assets/MoreHorizontalCircle01Icon-gMNGsE37.js +1 -0
- package/src/console/ui-next/dist/assets/PlusSignIcon-CwG3nxfu.js +1 -0
- package/src/console/ui-next/dist/assets/UnavailableIcon-D9cvHVPr.js +1 -0
- package/src/console/ui-next/dist/assets/UserIcon-DaE2PB5_.js +1 -0
- package/src/console/ui-next/dist/assets/access-page-DFeymU07.js +4 -0
- package/src/console/ui-next/dist/assets/agent-disclosure-U1rdfblp.js +1 -0
- package/src/console/ui-next/dist/assets/cache-glyph-BeFJeqBG.js +1 -0
- package/src/console/ui-next/dist/assets/call-pii-button-CVAONPii.js +1 -0
- package/src/console/ui-next/dist/assets/collapsible-D2A6NJ-3.js +1 -0
- package/src/console/ui-next/dist/assets/copy-inline-button-CAYD18cr.js +1 -0
- package/src/console/ui-next/dist/assets/dagre.esm-B1_XeuLP.js +1 -0
- package/src/console/ui-next/dist/assets/detail-header-DVWNjWjg.js +1 -0
- package/src/console/ui-next/dist/assets/dropdown-menu-4h2LOVXM.js +1 -0
- package/src/console/ui-next/dist/assets/duration-tone-Cgk_h5ja.js +9 -0
- package/src/console/ui-next/dist/assets/element-icons-BI8cJgdh.js +1 -0
- package/src/console/ui-next/dist/assets/explorer-empty-CJs5A-wm.js +1 -0
- package/src/console/ui-next/dist/assets/flows-page-Dss7941e.js +1 -0
- package/src/console/ui-next/dist/assets/highlighted-json-MYZQtRnw.js +154 -0
- package/src/console/ui-next/dist/assets/http-method-Jrh39p7A.js +1 -0
- package/src/console/ui-next/dist/assets/index-D0zS5rKO.css +2 -0
- package/src/console/ui-next/dist/assets/index-DXP2dBIF.js +63 -0
- package/src/console/ui-next/dist/assets/observability-page-HvolXxTI.js +4 -0
- package/src/console/ui-next/dist/assets/react-dom-Ddte4I-Q.js +9 -0
- package/src/console/ui-next/dist/assets/replica-lag-CSh2dzrb.js +18 -0
- package/src/console/ui-next/dist/assets/request-meta-BatF8KrK.js +1 -0
- package/src/console/ui-next/dist/assets/shortcut-keys-3ILd8oGn.js +1 -0
- package/src/console/ui-next/dist/assets/{sql-BskegiFM.js → sql-BsFa4tDR.js} +1 -1
- package/src/console/ui-next/dist/assets/store-page-eiKiHnNe.js +41 -0
- package/src/console/ui-next/dist/assets/trace-detail-sheet-CZkMeKS-.js +2 -0
- package/src/console/ui-next/dist/assets/tree-expand-toggle-CW8y5A2h.js +55 -0
- package/src/console/ui-next/dist/assets/units-page-B_RWJrEO.js +1 -0
- package/src/console/ui-next/dist/assets/{use-vault-list-uk4WVboC.js → use-vault-list-CT4-gajj.js} +1 -1
- package/src/console/ui-next/dist/assets/vault-page-CWrg-A68.js +2 -0
- package/src/console/ui-next/dist/assets/xyflow-CSyC6ryz.css +1 -0
- package/src/console/ui-next/dist/assets/xyflow-yApv7D4e.js +7 -0
- package/src/console/ui-next/dist/index.html +6 -13
- package/src/console/ui-next/seed-invoke-host.ts +21 -18
- package/src/console/ui-next/src/client.ts +7 -2
- package/src/console/ui-next/src/features/flows/graph/element-map.ts +2 -0
- package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +12 -2
- package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +4 -0
- package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +8 -0
- package/src/console/ui-next/src/features/flows/traces/waterfall-bars.ts +4 -0
- package/src/console/ui-next/src/features/flows/traces/waterfall-tooltip.ts +9 -3
- package/src/console/ui-next/src/features/store/detail/reveal-cell.tsx +7 -11
- package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +2 -2
- package/src/console/ui-next/src/features/store/lib/fields-from-table.ts +2 -0
- package/src/console/ui-next/src/features/store/lib/grid-model.test.ts +25 -1
- package/src/console/ui-next/src/features/store/lib/grid-model.ts +36 -1
- package/src/console/ui-next/src/features/store/query/query-results.tsx +3 -3
- package/src/console/ui-next/src/features/units/detail/effects-summary.tsx +2 -0
- package/src/console/ui-next/src/features/units/lib/call-read-safe.ts +3 -1
- package/src/console/ui-next/src/features/units/lib/fields-from-schema.ts +6 -0
- package/src/docker/ai-model-status.test.ts +1 -12
- package/src/docker/ai-model-status.ts +10 -38
- package/src/docker/compose-up.test.ts +100 -0
- package/src/docker/compose-up.ts +292 -0
- package/src/docker/compose.ts +4 -14
- package/src/docker/derive.ts +9 -34
- package/src/docker/docker.test.ts +16 -261
- package/src/docker/images-config.test.ts +13 -25
- package/src/docker/index.ts +1 -24
- package/src/docker/recipes/index.ts +0 -23
- package/src/docker/recipes/pgdog.ts +6 -4
- package/src/docker/stack-id.test.ts +2 -2
- package/src/docker/stack-id.ts +0 -1
- package/src/docker/types.ts +2 -2
- package/src/drivers/ai-anthropic.ts +10 -0
- package/src/drivers/ai-openai-compatible.ts +34 -7
- package/src/drivers/ai-providers.test.ts +0 -107
- package/src/drivers/ai-stream.test.ts +0 -34
- package/src/drivers/ai-types.ts +17 -8
- package/src/drivers/channel-fcm.ts +6 -1
- package/src/drivers/channel-msegat.ts +6 -1
- package/src/drivers/channel-resend.ts +5 -1
- package/src/drivers/channel-smtp.ts +5 -1
- package/src/drivers/channel-sndr.ts +10 -1
- package/src/drivers/channel-taqnyat-mail.ts +5 -1
- package/src/drivers/channel-taqnyat-whatsapp.ts +6 -1
- package/src/drivers/channel-taqnyat.ts +6 -1
- package/src/drivers/channel-types.ts +10 -0
- package/src/drivers/channel-unifonic.ts +6 -1
- package/src/drivers/channel-wa-cloud.ts +6 -1
- package/src/drivers/channel-webpush.ts +6 -1
- package/src/drivers/external.ts +33 -0
- package/src/drivers/index.ts +0 -10
- package/src/drivers/meilisearch.ts +6 -0
- package/src/drivers/oauth-types.ts +4 -0
- package/src/drivers/postgres.test.ts +74 -3
- package/src/drivers/postgres.ts +175 -6
- package/src/drivers/signal-types.ts +5 -5
- package/src/drivers/types.ts +7 -0
- package/src/drivers/vault-types.ts +5 -0
- package/src/elements/ai/declare.ts +17 -4
- package/src/elements/ai/eval.ts +1 -1
- package/src/elements/ai/mcp-http.ts +1 -1
- package/src/elements/ai/pii.ts +1 -1
- package/src/elements/ai/providers.test.ts +289 -0
- package/src/elements/ai/providers.ts +169 -0
- package/src/elements/ai/runtime.ts +68 -5
- package/src/elements/ai/schema.ts +2 -2
- package/src/elements/ai.test.ts +7 -7
- package/src/elements/ai.ts +17 -1
- package/src/elements/channel/runtime.ts +105 -12
- package/src/elements/clock/cron-fields.test.ts +144 -0
- package/src/elements/clock/cron-fields.ts +185 -0
- package/src/elements/clock/declare.ts +247 -3
- package/src/elements/clock.ts +21 -1
- package/src/elements/index.ts +15 -0
- package/src/elements/signal/chaos-child.ts +1 -2
- package/src/elements/signal/declare.ts +65 -37
- package/src/elements/signal/delivery-modes.test.ts +11 -31
- package/src/elements/signal/dry-run-replay.test.ts +1 -7
- package/src/elements/signal/dry-run-write-isolation.test.ts +1 -7
- package/src/elements/signal/key-ordering.test.ts +6 -26
- package/src/elements/signal/lease-reclaim.test.ts +2 -6
- package/src/elements/signal/optional-emit.test.ts +6 -9
- package/src/elements/signal/order-lifecycle.test.ts +4 -17
- package/src/elements/signal/orphan-messages.test.ts +4 -15
- package/src/elements/signal/reconcile.test.ts +2 -2
- package/src/elements/signal/runtime.ts +1 -1
- package/src/elements/signal/schema-emit.test.ts +4 -8
- package/src/elements/signal.test.ts +24 -49
- package/src/elements/signal.ts +9 -2
- package/src/elements/store/domain-ddl.test.ts +1 -1
- package/src/elements/store/live-http.test.ts +9 -4
- package/src/elements/store/prepare-row.test.ts +123 -0
- package/src/elements/store/resource.ts +43 -12
- package/src/elements/store/schema-decl.ts +117 -0
- package/src/elements/store/search-backfill.ts +205 -0
- package/src/elements/store/search-bind.ts +66 -0
- package/src/elements/store/search-bm25.ts +78 -0
- package/src/elements/store/search-ddl.ts +156 -0
- package/src/elements/store/search-embed-flow.ts +141 -0
- package/src/elements/store/search-errors.ts +34 -0
- package/src/elements/store/search-fusion.ts +112 -0
- package/src/elements/store/search-lsh.ts +179 -0
- package/src/elements/store/search-runtime.ts +300 -0
- package/src/elements/store/search.test.ts +144 -0
- package/src/elements/store/sql-session.test.ts +1 -0
- package/src/elements/store/sql-session.ts +83 -3
- package/src/elements/store/table.ts +23 -2
- package/src/elements/store/upsert-app.test.ts +1 -3
- package/src/elements/store.ts +50 -0
- package/src/full.ts +4 -2
- package/src/http.ts +4 -1
- package/src/i18n/catalogs/ar.ts +14 -6
- package/src/i18n/catalogs/en.ts +14 -6
- package/src/index.ts +6 -1
- package/src/kernel/adopt-barrel-fresh.test.ts +7 -7
- package/src/kernel/adopt-routes.ts +9 -1
- package/src/kernel/app-auth.ts +5 -0
- package/src/kernel/app.ts +102 -9
- package/src/kernel/auto-cache.test.ts +6 -12
- package/src/kernel/auto-registry.test.ts +3 -3
- package/src/kernel/boot-bind/ai.test.ts +22 -19
- package/src/kernel/boot-bind/ai.ts +5 -43
- package/src/kernel/boot-bind/clock.ts +23 -5
- package/src/kernel/boot-bind/honor-config.test.ts +4 -4
- package/src/kernel/boot.test.ts +11 -6
- package/src/kernel/boot.ts +66 -16
- package/src/kernel/boundary-contract.ts +93 -0
- package/src/kernel/budget.test.ts +1 -1
- package/src/kernel/call.ts +89 -0
- package/src/kernel/capability.ts +6 -0
- package/src/kernel/client-descriptor.ts +123 -0
- package/src/kernel/clock-timezone.test.ts +80 -0
- package/src/kernel/correlation.test.ts +1 -1
- package/src/kernel/dry-run.ts +11 -8
- package/src/kernel/effects-stamping.test.ts +42 -9
- package/src/kernel/effects.test.ts +4 -2
- package/src/kernel/effects.ts +51 -9
- package/src/kernel/errors-channel.ts +17 -0
- package/src/kernel/errors-live-resume.ts +3 -2
- package/src/kernel/errors-tenant.ts +7 -4
- package/src/kernel/errors.registry-helpers.ts +96 -0
- package/src/kernel/errors.registry.test.ts +139 -25
- package/src/kernel/errors.ts +102 -23
- package/src/kernel/external-effects.test.ts +196 -0
- package/src/kernel/flow.test.ts +13 -4
- package/src/kernel/flow.ts +69 -71
- package/src/kernel/fx-ask-telemetry.test.ts +2 -2
- package/src/kernel/fx-dead-letters.test.ts +3 -3
- package/src/kernel/fx-fetch.ts +55 -0
- package/src/kernel/fx-live-stream.ts +2 -2
- package/src/kernel/fx-live.test.ts +8 -8
- package/src/kernel/fx.test.ts +17 -11
- package/src/kernel/fx.ts +180 -57
- package/src/kernel/horizontal-child.ts +1 -1
- package/src/kernel/http-query.test.ts +3 -6
- package/src/kernel/index.ts +13 -3
- package/src/kernel/instance-id.ts +1 -1
- package/src/kernel/instances.test.ts +40 -0
- package/src/kernel/instances.ts +47 -9
- package/src/kernel/live-http.test.ts +6 -7
- package/src/kernel/live-http.ts +6 -2
- package/src/kernel/live-resume.test.ts +1 -1
- package/src/kernel/mcp-tool.test.ts +3 -3
- package/src/kernel/on.ts +98 -4
- package/src/kernel/pipeline.test.ts +12 -15
- package/src/kernel/plugin-elements.test.ts +1 -1
- package/src/kernel/stamp-http.test.ts +3 -3
- package/src/kernel/triggers.ts +210 -95
- package/src/kernel-entry.ts +0 -1
- package/src/manifest/diff.ts +11 -1
- package/src/manifest/fixtures/skyport.excerpt.json +1 -1
- package/src/manifest/types.ts +46 -2
- package/src/mcp/docs-index.ts +3 -3
- package/src/mcp/docs-mcp.test.ts +2 -2
- package/src/mcp/docs-tools.ts +1 -1
- package/src/okid.test.ts +56 -0
- package/src/okid.ts +55 -11
- package/src/plugins/anonymous.ts +8 -4
- package/src/plugins/auth/shared.ts +27 -3
- package/src/plugins/auth-delivery.mailpit.integration.test.ts +1 -1
- package/src/plugins/auth-methods.security.test.ts +561 -23
- package/src/plugins/config-source.ts +1 -1
- package/src/plugins/csrf.test.ts +65 -0
- package/src/plugins/index.ts +2 -0
- package/src/plugins/magic-link.ts +36 -16
- package/src/plugins/oauth/shared.ts +3 -1
- package/src/plugins/oauth.ts +22 -14
- package/src/plugins/otp.ts +37 -21
- package/src/plugins/passkey-webauthn.ts +9 -6
- package/src/plugins/passkey.ts +98 -26
- package/src/plugins/pre-account-hijack.test.ts +223 -0
- package/src/plugins/two-factor.ts +466 -37
- package/src/plugins/username.ts +19 -7
- package/src/release/build-lib.ts +1 -0
- package/src/release/limits.ts +2 -2
- package/src/release/measure.ts +1 -1
- package/src/runs/collect.test.ts +0 -2
- package/src/runtime/json-code-block.test.ts +250 -20
- package/src/runtime/json-code-block.ts +1261 -41
- package/src/term.test.ts +1 -1
- package/src/test/create-test-app.test.ts +12 -12
- package/src/test/live-signals.test.ts +6 -6
- package/src/test/provisions.integration.test.ts +10 -14
- package/src/test/reset-element-registries.ts +1 -1
- package/src/test/tenant-isolation.test.ts +12 -6
- package/site/content/docs/deployment/docker-swarm.mdx +0 -164
- package/site/content/docs/deployment/docker.mdx +0 -227
- package/site/content/docs/deployment/index.mdx +0 -83
- package/site/content/docs/deployment/kubernetes.mdx +0 -176
- package/site/content/docs/deployment/meta.json +0 -5
- package/site/content/docs/deployment/reverse-proxy.mdx +0 -234
- package/site/content/docs/elements/ai.mdx +0 -385
- package/site/content/docs/elements/channel.mdx +0 -346
- package/site/content/docs/elements/clock.mdx +0 -244
- package/site/content/docs/elements/flow.mdx +0 -420
- package/site/content/docs/elements/gate.mdx +0 -436
- package/site/content/docs/elements/signal.mdx +0 -380
- package/site/content/docs/elements/store.mdx +0 -1099
- package/site/content/docs/elements/vault.mdx +0 -405
- package/site/content/docs/get-started/basic-usage.mdx +0 -173
- package/site/content/docs/get-started/index.mdx +0 -43
- package/site/content/docs/get-started/installation.mdx +0 -220
- package/site/content/docs/get-started/introduction.mdx +0 -132
- package/site/content/docs/get-started/meta.json +0 -13
- package/site/content/docs/get-started/project-structure.mdx +0 -925
- package/site/content/docs/get-started/testing.mdx +0 -328
- package/site/content/docs/get-started/why.mdx +0 -162
- package/site/content/docs/recipes/llama-cpp.mdx +0 -151
- package/site/content/docs/recipes/ollama.mdx +0 -142
- package/site/content/docs/recipes/sglang.mdx +0 -105
- package/site/content/docs/recipes/vllm.mdx +0 -106
- package/site/content/docs/reference/cli.md +0 -232
- package/site/content/docs/reference/client.mdx +0 -469
- package/site/content/docs/reference/security.md +0 -74
- package/src/cli/ai-setup/detect-ollama.ts +0 -213
- package/src/cli/ai-setup/recommend.test.ts +0 -225
- package/src/cli/ai-setup/recommend.ts +0 -225
- package/src/cli/dev-controls.test.ts +0 -122
- package/src/cli/dev-controls.ts +0 -164
- package/src/cli/tui/DevLive.tsx +0 -126
- package/src/console/ui-next/dist/assets/access-page-De7Lc2JC.js +0 -4
- package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +0 -1
- package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +0 -1
- package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +0 -1
- package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +0 -1
- package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +0 -1
- package/src/console/ui-next/dist/assets/dagre.esm-ZwcdTuZZ.js +0 -1
- package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +0 -1
- package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +0 -1
- package/src/console/ui-next/dist/assets/duration-tone-sC3lGABz.js +0 -9
- package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +0 -1
- package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +0 -1
- package/src/console/ui-next/dist/assets/flows-page-RGy7VEA_.js +0 -1
- package/src/console/ui-next/dist/assets/highlighted-json-Awq7gYdu.js +0 -154
- package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +0 -1
- package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +0 -2
- package/src/console/ui-next/dist/assets/index-_rgpdVzo.js +0 -66
- package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +0 -1
- package/src/console/ui-next/dist/assets/observability-page-Ds6pcnh-.js +0 -4
- package/src/console/ui-next/dist/assets/preload-helper-oH4irX4C.js +0 -1
- package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +0 -1
- package/src/console/ui-next/dist/assets/react-dom-Bph1y7z7.js +0 -9
- package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +0 -18
- package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +0 -1
- package/src/console/ui-next/dist/assets/skeleton-D-czQJT6.js +0 -1
- package/src/console/ui-next/dist/assets/store-page-02xOiqIK.js +0 -41
- package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +0 -2
- package/src/console/ui-next/dist/assets/tree-expand-toggle-BtyhmWb4.js +0 -55
- package/src/console/ui-next/dist/assets/units-page-4rHOePuE.js +0 -1
- package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +0 -1
- package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +0 -1
- package/src/console/ui-next/dist/assets/vault-page-DISPgxLM.js +0 -2
- package/src/console/ui-next/dist/assets/xyflow-D7n4g6go.js +0 -7
- package/src/console/ui-next/dist/assets/xyflow-DZ0Ws1xk.css +0 -1
- package/src/docker/ollama-pull.ts +0 -232
- package/src/docker/recipes/llama-cpp.ts +0 -298
- package/src/docker/recipes/ollama.ts +0 -44
- package/src/docker/recipes/sglang.ts +0 -55
- package/src/docker/recipes/vllm.ts +0 -44
- package/src/drivers/ai-ollama-tools.integration.test.ts +0 -109
- package/src/drivers/ai-ollama.integration.test.ts +0 -184
- package/src/drivers/ai-ollama.ts +0 -389
- package/src/drivers/ollama.ts +0 -14
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Authentication"
|
|
3
|
+
description: "Enable gate.auth so sessions and API keys fill fx.auth — then protect Flows with policy gates."
|
|
4
|
+
icon: "Lock"
|
|
5
|
+
source: "docs/spec/unified-theory.md"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Authentication is configured as `oke({ gate: { auth } })`. That bag issues `/auth/*` Flows
|
|
9
|
+
(unless `http: false`), fills `fx.auth`, and leaves permission to policy gates — there is no
|
|
10
|
+
`gate.auth` handle for `.gate(...)`.
|
|
11
|
+
|
|
12
|
+
For developers shipping signed-in APIs on okengine — turn on auth, plug a method, attach policies.
|
|
13
|
+
|
|
14
|
+
<Callout title="The one rule">
|
|
15
|
+
Turn on `gate.auth` for identity. Attach `gate.policy` / `gate.scope` (or `.public()`) for
|
|
16
|
+
permission. Boot fails if an HTTP trigger has neither.
|
|
17
|
+
</Callout>
|
|
18
|
+
|
|
19
|
+
## Smallest Example
|
|
20
|
+
|
|
21
|
+
<Steps>
|
|
22
|
+
|
|
23
|
+
<Step>
|
|
24
|
+
### Enable auth on the app
|
|
25
|
+
|
|
26
|
+
```typescript title="src/app.ts"
|
|
27
|
+
import { oke } from "okengine";
|
|
28
|
+
import { username } from "okengine/plugins";
|
|
29
|
+
|
|
30
|
+
export const app = oke({
|
|
31
|
+
name: "notes",
|
|
32
|
+
env: "dev",
|
|
33
|
+
gate: {
|
|
34
|
+
auth: {
|
|
35
|
+
// secret required in prod; minted in dev when omitted
|
|
36
|
+
// basePath defaults to "/auth"
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
}).plug(username());
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
</Step>
|
|
43
|
+
|
|
44
|
+
<Step>
|
|
45
|
+
### Declare a signed-in policy and attach it
|
|
46
|
+
|
|
47
|
+
```typescript title="src/core/gate.ts"
|
|
48
|
+
import { gate } from "okengine";
|
|
49
|
+
|
|
50
|
+
export const member = gate.policy("member", {
|
|
51
|
+
description: "Signed-in user",
|
|
52
|
+
check: ({ auth }) => !!auth.verified,
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```typescript title="src/flows/profile/get.ts"
|
|
57
|
+
import { on, flow, http } from "okengine";
|
|
58
|
+
import { member } from "@/core/gate";
|
|
59
|
+
|
|
60
|
+
export const get = on(
|
|
61
|
+
http.get().gate(member),
|
|
62
|
+
flow({
|
|
63
|
+
do: async (_, fx) => ({ userId: fx.auth.userId }),
|
|
64
|
+
}),
|
|
65
|
+
);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
</Step>
|
|
69
|
+
|
|
70
|
+
<Step>
|
|
71
|
+
### Sign in and call
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# Method routes live under basePath (default /auth) — see your plugged method docs
|
|
75
|
+
curl -X GET http://localhost:6530/profile \
|
|
76
|
+
-H "accept: application/json" \
|
|
77
|
+
-H "authorization: Bearer …"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Authenticated callers reach `do` with `fx.auth.userId` and `fx.auth.scopes` set. Anonymous
|
|
81
|
+
callers fail the policy → typed `Unauthorized`.
|
|
82
|
+
|
|
83
|
+
</Step>
|
|
84
|
+
|
|
85
|
+
</Steps>
|
|
86
|
+
|
|
87
|
+
## Progressive Patterns
|
|
88
|
+
|
|
89
|
+
From Bearer-only identity to cookies, API keys, and method plugins:
|
|
90
|
+
|
|
91
|
+
<Tabs items={["Bearer", "Cookies", "API keys", "Plugins"]}>
|
|
92
|
+
|
|
93
|
+
<Tab value="Bearer">
|
|
94
|
+
|
|
95
|
+
Default transport is `Authorization: Bearer <access>`. The pipeline verifies the token into
|
|
96
|
+
`fx.auth` before gate evaluation:
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
export const app = oke({
|
|
100
|
+
name: "notes",
|
|
101
|
+
env: "dev",
|
|
102
|
+
gate: { auth: {} },
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
In production, set `gate.auth.secret` (or `OKE_AUTH_SECRET`). Omitting it in `prod` throws:
|
|
107
|
+
`gate.auth: secret is required in production (set gate.auth.secret or OKE_AUTH_SECRET)`.
|
|
108
|
+
|
|
109
|
+
Forged, expired, or revoked access tokens map to typed `Unauthorized` — they never become a
|
|
110
|
+
principal.
|
|
111
|
+
|
|
112
|
+
</Tab>
|
|
113
|
+
|
|
114
|
+
<Tab value="Cookies">
|
|
115
|
+
|
|
116
|
+
Opt-in HttpOnly cookie mirror (Bearer remains default). Enable under `gate.auth.cookies`:
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
gate: {
|
|
120
|
+
auth: {
|
|
121
|
+
cookies: {
|
|
122
|
+
enabled: true,
|
|
123
|
+
prefix: "oke", // default
|
|
124
|
+
sameSite: "lax", // default
|
|
125
|
+
// secure defaults true; path defaults "/"
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Consequence:** cookie sessions need the same CSRF / CORS posture as any cookie app — plug
|
|
132
|
+
[`csrf`](/docs/plugins/csrf) and [`cors`](/docs/plugins/cors) when browsers call cross-origin.
|
|
133
|
+
|
|
134
|
+
</Tab>
|
|
135
|
+
|
|
136
|
+
<Tab value="API keys">
|
|
137
|
+
|
|
138
|
+
Machine principals authenticate with a key secret. Inside `do`, `fx.auth.apiKeyId` is set;
|
|
139
|
+
session-only methods (`fx.auth.createApiKey`, tenant admin, …) refuse keys.
|
|
140
|
+
|
|
141
|
+
```typescript title="src/flows/keys/create.ts"
|
|
142
|
+
import { on, flow, http } from "okengine";
|
|
143
|
+
import { member } from "@/core/gate";
|
|
144
|
+
|
|
145
|
+
export const create = on(
|
|
146
|
+
http.post().gate(member),
|
|
147
|
+
flow({
|
|
148
|
+
do: async (_, fx) => {
|
|
149
|
+
const { key, secret } = await fx.auth.createApiKey({
|
|
150
|
+
name: "ci",
|
|
151
|
+
scopes: ["notes:read"],
|
|
152
|
+
expiresIn: "90d",
|
|
153
|
+
});
|
|
154
|
+
return { id: key.id, secret }; // secret shown once
|
|
155
|
+
},
|
|
156
|
+
}),
|
|
157
|
+
);
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Key methods: `createApiKey` · `listApiKeys` · `revokeApiKey` · `rotateApiKey` · `updateApiKey`.
|
|
161
|
+
|
|
162
|
+
</Tab>
|
|
163
|
+
|
|
164
|
+
<Tab value="Plugins">
|
|
165
|
+
|
|
166
|
+
`gate.auth` alone does not ship a login UI — plug a method from `okengine/plugins`:
|
|
167
|
+
|
|
168
|
+
| Plugin | Docs |
|
|
169
|
+
| ------------------- | -------------------------------------- |
|
|
170
|
+
| `username` | [Username](/docs/plugins/username) |
|
|
171
|
+
| `magicLink` | [Magic link](/docs/plugins/magic-link) |
|
|
172
|
+
| `passkey` | [Passkey](/docs/plugins/passkey) |
|
|
173
|
+
| `oauth` / providers | [OAuth](/docs/plugins/oauth) |
|
|
174
|
+
| `anonymous` | [Anonymous](/docs/plugins/anonymous) |
|
|
175
|
+
| `twoFactor` | [Two-factor](/docs/plugins/two-factor) |
|
|
176
|
+
| `otp` | [OTP](/docs/plugins/otp) |
|
|
177
|
+
|
|
178
|
+
</Tab>
|
|
179
|
+
|
|
180
|
+
</Tabs>
|
|
181
|
+
|
|
182
|
+
## Options
|
|
183
|
+
|
|
184
|
+
| Option | Type | Default | Meaning |
|
|
185
|
+
| ------------------------------------------- | ------------- | ------------------ | ---------------------------------------------------------- |
|
|
186
|
+
| `secret` | `string` | minted in non-prod | HMAC for access tokens; required in prod |
|
|
187
|
+
| `basePath` | `string` | `"/auth"` | HTTP prefix for auth Flows |
|
|
188
|
+
| `http` | `boolean` | `true` | `false` skips `/auth/*` bindings (secret + tables only) |
|
|
189
|
+
| `audience` | `string` | `"oke-app"` | Access-token audience claim |
|
|
190
|
+
| `emailAndPassword.enabled` | `boolean` | `false` | Credential method knobs |
|
|
191
|
+
| `emailAndPassword.requireEmailVerification` | `boolean` | `false` | Block sign-in until verified |
|
|
192
|
+
| `session.accessTtlMs` | `number` | `14m` | Access token lifetime |
|
|
193
|
+
| `session.refreshTtlMs` | `number` | `30d` | Refresh token lifetime |
|
|
194
|
+
| `session.freshAgeMs` | `number` | `24h` | Max age for "fresh" step-up policies |
|
|
195
|
+
| `session.idleTtlMs` | `number` | off | Idle timeout from last activity |
|
|
196
|
+
| `session.absoluteTtlMs` | `number` | off | Absolute lifetime from creation |
|
|
197
|
+
| `session.singleSessionPerUser` | `boolean` | `false` | One live family per user |
|
|
198
|
+
| `cookies` | bag | off | HttpOnly cookie mirror |
|
|
199
|
+
| `secondaryStorage` | bag | off | Hot-path KV cache (`prefix` default `"auth:"`) |
|
|
200
|
+
| `tenant` | `true` \| bag | off | Multi-tenancy — see [Tenancy](/docs/elements/gate/tenancy) |
|
|
201
|
+
|
|
202
|
+
## What `fx.auth` carries
|
|
203
|
+
|
|
204
|
+
| Field | Meaning |
|
|
205
|
+
| --------------- | -------------------------------------------------------------------------- |
|
|
206
|
+
| `userId` | Principal id, or `null` when anonymous |
|
|
207
|
+
| `scopes` | `ReadonlySet<string>` used by `gate.scope` (may include tenant-role union) |
|
|
208
|
+
| `sessionScopes` | Session / JWT scopes before tenant-role union |
|
|
209
|
+
| `verified` | Session / credential passed verification |
|
|
210
|
+
| `apiKeyId` | Present when the principal is an API key |
|
|
211
|
+
|
|
212
|
+
Inside `do`, read identity from `fx.auth` — not `fx.user`. World access stays on `fx`.
|
|
213
|
+
|
|
214
|
+
## Sessions & Cookies
|
|
215
|
+
|
|
216
|
+
<Callout title="Detailed section">
|
|
217
|
+
Defaults match a short-lived access token plus a long-lived refresh family. Override only what
|
|
218
|
+
your product needs.
|
|
219
|
+
</Callout>
|
|
220
|
+
|
|
221
|
+
```typescript title="src/app.ts"
|
|
222
|
+
export const app = oke({
|
|
223
|
+
name: "notes",
|
|
224
|
+
env: "prod",
|
|
225
|
+
gate: {
|
|
226
|
+
auth: {
|
|
227
|
+
secret: process.env.OKE_AUTH_SECRET!,
|
|
228
|
+
session: {
|
|
229
|
+
accessTtlMs: 14 * 60 * 1000,
|
|
230
|
+
refreshTtlMs: 30 * 24 * 60 * 60 * 1000,
|
|
231
|
+
freshAgeMs: 24 * 60 * 60 * 1000,
|
|
232
|
+
// idleTtlMs / absoluteTtlMs / singleSessionPerUser when needed
|
|
233
|
+
},
|
|
234
|
+
cookies: {
|
|
235
|
+
enabled: true,
|
|
236
|
+
prefix: "oke",
|
|
237
|
+
sameSite: "lax",
|
|
238
|
+
secure: true,
|
|
239
|
+
path: "/",
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
| Cookie option | Default | Meaning |
|
|
247
|
+
| ---------------- | ------- | --------------------------------- |
|
|
248
|
+
| `enabled` | `false` | Opt-in HttpOnly mirror |
|
|
249
|
+
| `prefix` | `"oke"` | Cookie name prefix |
|
|
250
|
+
| `secure` | `true` | HTTPS-only |
|
|
251
|
+
| `sameSite` | `"lax"` | `"strict"` \| `"lax"` \| `"none"` |
|
|
252
|
+
| `path` | `"/"` | Cookie path |
|
|
253
|
+
| `crossSubdomain` | `false` | Share across subdomains |
|
|
254
|
+
| `domain` | — | Explicit cookie domain |
|
|
255
|
+
|
|
256
|
+
**Freshness:** policies that require a recent sign-in should compare session age against
|
|
257
|
+
`session.freshAgeMs` (default 24h). Step-up plugins (e.g. [two-factor](/docs/plugins/two-factor))
|
|
258
|
+
build on the same window.
|
|
259
|
+
|
|
260
|
+
## API Keys
|
|
261
|
+
|
|
262
|
+
<Accordions>
|
|
263
|
+
|
|
264
|
+
<Accordion title="createApiKey options">
|
|
265
|
+
|
|
266
|
+
| Field | Type | Meaning |
|
|
267
|
+
| ------------- | ---------------------- | ------------------------------------------ |
|
|
268
|
+
| `name` | `string` | Label for Console / list |
|
|
269
|
+
| `scopes` | `string[]` | Cannot exceed the creator’s session scopes |
|
|
270
|
+
| `expiresIn` | duration string | Optional (`"90d"`, `"1h"`, …) |
|
|
271
|
+
| `ipAllowlist` | `string[]` | Optional source IP allowlist |
|
|
272
|
+
| `rateLimit` | `{ max, per } \| null` | Optional per-key throttle |
|
|
273
|
+
|
|
274
|
+
Return shape: `{ key, secret }` — the secret is shown once at create / rotate.
|
|
275
|
+
|
|
276
|
+
</Accordion>
|
|
277
|
+
|
|
278
|
+
<Accordion title="Session-only refusals">
|
|
279
|
+
Key management and tenant admin refuse API-key principals:
|
|
280
|
+
|
|
281
|
+
```json
|
|
282
|
+
{
|
|
283
|
+
"data": null,
|
|
284
|
+
"error": {
|
|
285
|
+
"code": "Forbidden",
|
|
286
|
+
"message": "You are not allowed to perform this action.",
|
|
287
|
+
"data": { "gate": "auth:api-keys", "reason": "session_only" }
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Call those methods from a user session. Machine keys authenticate _into_ Flows; they do not
|
|
293
|
+
mint more keys.
|
|
294
|
+
|
|
295
|
+
</Accordion>
|
|
296
|
+
|
|
297
|
+
</Accordions>
|
|
298
|
+
|
|
299
|
+
## Public routes
|
|
300
|
+
|
|
301
|
+
Health checks and login endpoints must declare open posture explicitly:
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
http.get().public();
|
|
305
|
+
// equivalent: http.get().gate(gate.public)
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Auth method Flows under `basePath` register their own posture; your app routes still need
|
|
309
|
+
`.gate(...)` or `.public()`.
|
|
310
|
+
|
|
311
|
+
Set `gate.auth.http: false` when you want tables + Bearer verify without materializing
|
|
312
|
+
`/auth/*` HTTP bindings (embedding / Console-style hosts).
|
|
313
|
+
|
|
314
|
+
## Troubleshooting
|
|
315
|
+
|
|
316
|
+
<Accordions>
|
|
317
|
+
|
|
318
|
+
<Accordion title="gate.auth: secret is required in production">
|
|
319
|
+
Cause: `gate.auth: secret is required in production (set gate.auth.secret or OKE_AUTH_SECRET)`.
|
|
320
|
+
Set an explicit secret before shipping — never rely on the minted dev secret in prod.
|
|
321
|
+
</Accordion>
|
|
322
|
+
|
|
323
|
+
<Accordion title="401 on every gated route after sign-in">
|
|
324
|
+
Token missing, expired, wrong audience, or cookies enabled without sending credentials. Check
|
|
325
|
+
`Authorization: Bearer`, `audience`, and cookie `SameSite` / CORS.
|
|
326
|
+
</Accordion>
|
|
327
|
+
|
|
328
|
+
<Accordion title="Forbidden · session_only on createApiKey / listTenants">
|
|
329
|
+
Those methods refuse API-key principals (`error.data.reason: "session_only"`). Call them from a
|
|
330
|
+
user session, not a machine key.
|
|
331
|
+
</Accordion>
|
|
332
|
+
|
|
333
|
+
<Accordion title="Forbidden · not_owner on revokeApiKey">
|
|
334
|
+
Keys are owned by the creator. A different session cannot revoke or rotate another user’s key
|
|
335
|
+
(`reason: "not_owner"`).
|
|
336
|
+
</Accordion>
|
|
337
|
+
|
|
338
|
+
<Accordion title="GateBootError after enabling auth">
|
|
339
|
+
Enabling `gate.auth` does not auto-gate your routes. Attach `member` (or `.public()`) on every
|
|
340
|
+
HTTP trigger — see [Boot Posture](/docs/elements/gate#boot-posture).
|
|
341
|
+
</Accordion>
|
|
342
|
+
|
|
343
|
+
</Accordions>
|
|
344
|
+
|
|
345
|
+
## Learn more
|
|
346
|
+
|
|
347
|
+
- [Username plugin](/docs/plugins/username) — email-free sign-up on `gate.auth`
|
|
348
|
+
- [Authorization](/docs/elements/gate/authorization) — scopes and ABAC policies
|
|
349
|
+
- [RLS](/docs/elements/gate/rls) — row policies from stamped identity
|
|
350
|
+
- [Tenancy](/docs/elements/gate/tenancy) — `fx.tenant.id`
|
|
351
|
+
- [HTTP](/docs/elements/flow/http) — `.gate` / `.public` on triggers
|
|
352
|
+
|
|
353
|
+
## Next
|
|
354
|
+
|
|
355
|
+
<Cards>
|
|
356
|
+
<Card
|
|
357
|
+
title="Authorization"
|
|
358
|
+
description="gate.policy and gate.scope for permission checks."
|
|
359
|
+
href="/docs/elements/gate/authorization"
|
|
360
|
+
/>
|
|
361
|
+
<Card
|
|
362
|
+
title="RLS"
|
|
363
|
+
description="Stamp Gate identity into SQL row policies."
|
|
364
|
+
href="/docs/elements/gate/rls"
|
|
365
|
+
/>
|
|
366
|
+
<Card
|
|
367
|
+
title="Tenancy"
|
|
368
|
+
description="Resolve fx.tenant.id from claims, headers, or subdomains."
|
|
369
|
+
href="/docs/elements/gate/tenancy"
|
|
370
|
+
/>
|
|
371
|
+
</Cards>
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Authorization"
|
|
3
|
+
description: "gate.policy and gate.scope — reusable permission checks composed with gate.all and attached via .gate."
|
|
4
|
+
icon: "ShieldAlert"
|
|
5
|
+
source: "docs/spec/unified-theory.md"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Authorization answers “may this principal do this?” after identity is known. Declare reusable
|
|
9
|
+
`gate.policy` / `gate.scope` handles, compose them with `gate.all`, and attach the chain on the
|
|
10
|
+
trigger.
|
|
11
|
+
|
|
12
|
+
For developers enforcing RBAC / ABAC on okengine — name the check once, reuse it on every route.
|
|
13
|
+
|
|
14
|
+
<Callout title="The one rule">
|
|
15
|
+
Policies receive `GatePolicyContext` (`auth`, `operator`, optional `meta`) — never invent
|
|
16
|
+
`ctx.user` or `ctx.store`. World access stays inside `do` via `fx`.
|
|
17
|
+
</Callout>
|
|
18
|
+
|
|
19
|
+
## Smallest Example
|
|
20
|
+
|
|
21
|
+
<Steps>
|
|
22
|
+
|
|
23
|
+
<Step>
|
|
24
|
+
### Declare policies and scopes
|
|
25
|
+
|
|
26
|
+
```typescript title="src/core/gate.ts"
|
|
27
|
+
import { gate } from "okengine";
|
|
28
|
+
|
|
29
|
+
/** Signed-in member. */
|
|
30
|
+
export const member = gate.policy("member", {
|
|
31
|
+
check: ({ auth }) => !!auth.verified,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
/** Holds the notes:write scope (name is the scope string). */
|
|
35
|
+
export const notesWrite = gate.scope("notes:write");
|
|
36
|
+
|
|
37
|
+
/** Admin-only ABAC example. */
|
|
38
|
+
export const adminOnly = gate.policy("adminOnly", ({ auth }) => auth.scopes.has("admin"));
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`gate.scope(name)` is shorthand for
|
|
42
|
+
`gate.policy(name, ({ auth }) => auth.scopes.has(name))` with `scopes: [name]` recorded.
|
|
43
|
+
|
|
44
|
+
</Step>
|
|
45
|
+
|
|
46
|
+
<Step>
|
|
47
|
+
### Compose and attach
|
|
48
|
+
|
|
49
|
+
```typescript title="src/flows/notes/create.ts"
|
|
50
|
+
import { on, flow, http, gate } from "okengine";
|
|
51
|
+
import { member, notesWrite } from "@/core/gate";
|
|
52
|
+
|
|
53
|
+
export const notesMutate = gate.all(member, notesWrite);
|
|
54
|
+
|
|
55
|
+
export const create = on(
|
|
56
|
+
http.post().gate(notesMutate),
|
|
57
|
+
// or: .gate(member, notesWrite)
|
|
58
|
+
flow({
|
|
59
|
+
do: async ({ title }, fx) => fx.json.create({ id: fx.id(), title }),
|
|
60
|
+
}),
|
|
61
|
+
);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
</Step>
|
|
65
|
+
|
|
66
|
+
<Step>
|
|
67
|
+
### See the denial
|
|
68
|
+
|
|
69
|
+
Missing `notes:write` on an authenticated caller:
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"data": null,
|
|
74
|
+
"error": {
|
|
75
|
+
"code": "Forbidden",
|
|
76
|
+
"message": "You are not allowed to perform this action.",
|
|
77
|
+
"data": { "gate": "notes:write", "reason": "policy denied" }
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Anonymous callers denied earlier in the chain get `Unauthorized` instead.
|
|
83
|
+
|
|
84
|
+
</Step>
|
|
85
|
+
|
|
86
|
+
</Steps>
|
|
87
|
+
|
|
88
|
+
## Progressive Patterns
|
|
89
|
+
|
|
90
|
+
From a single scope to Module:Action names, composition, and operator plane:
|
|
91
|
+
|
|
92
|
+
<Tabs items={["Scope", "ABAC", "Compose", "Operator"]}>
|
|
93
|
+
|
|
94
|
+
<Tab value="Scope">
|
|
95
|
+
|
|
96
|
+
Prefer `gate.scope` when the check is exactly “has this scope string”:
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
export const bookingCreate = gate.scope("booking:create");
|
|
100
|
+
export const notesRead = gate.scope("notes:read");
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Names containing `:` are Module:Action pairs — extracted into Manifest permissions for Console.
|
|
104
|
+
|
|
105
|
+
</Tab>
|
|
106
|
+
|
|
107
|
+
<Tab value="ABAC">
|
|
108
|
+
|
|
109
|
+
Use `gate.policy` when the predicate needs more than a single scope membership:
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
export const freshAdmin = gate.policy("freshAdmin", ({ auth, meta }) => {
|
|
113
|
+
if (!auth.scopes.has("admin")) return false;
|
|
114
|
+
// Example: combine scopes with request meta (ip allowlists live in plugins)
|
|
115
|
+
return !!auth.verified && meta?.ip !== undefined;
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Async predicates are allowed (`Promise<boolean>`).
|
|
120
|
+
|
|
121
|
+
</Tab>
|
|
122
|
+
|
|
123
|
+
<Tab value="Compose">
|
|
124
|
+
|
|
125
|
+
`gate.all` is every-member-must-pass, left to right. Nesting flattens:
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
const write = gate.all(member, notesWrite, gate.rate({ max: 60, per: "1m", keyBy: "user" }));
|
|
129
|
+
const strictWrite = gate.all(write, gate.scope("notes:admin"));
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Attach either the composed handle or list members on `.gate(...)` — order is declaration order;
|
|
133
|
+
first denial wins.
|
|
134
|
+
|
|
135
|
+
</Tab>
|
|
136
|
+
|
|
137
|
+
<Tab value="Operator">
|
|
138
|
+
|
|
139
|
+
Console / operator-plane Flows read `operator`, not `auth.userId`:
|
|
140
|
+
|
|
141
|
+
```typescript title="src/core/gate.ts"
|
|
142
|
+
import { gate } from "okengine";
|
|
143
|
+
|
|
144
|
+
export const consoleOp = gate.policy("consoleOp", ({ operator }) => operator.id !== null);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
```typescript title="src/flows/ops/cleanup.ts"
|
|
148
|
+
import { on, flow, http } from "okengine";
|
|
149
|
+
import { consoleOp } from "@/core/gate";
|
|
150
|
+
|
|
151
|
+
export const cleanup = on(
|
|
152
|
+
http.post().gate(consoleOp),
|
|
153
|
+
flow({
|
|
154
|
+
plane: "operator",
|
|
155
|
+
do: async (_, fx) => ({ operatorId: fx.operator.id }),
|
|
156
|
+
}),
|
|
157
|
+
);
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Rate `keyBy: "operator"` keys on `operator.id`.
|
|
161
|
+
|
|
162
|
+
</Tab>
|
|
163
|
+
|
|
164
|
+
</Tabs>
|
|
165
|
+
|
|
166
|
+
## Policy Context
|
|
167
|
+
|
|
168
|
+
| Field | Type | Meaning |
|
|
169
|
+
| -------------------- | ----------------------------- | ---------------------------------------------- |
|
|
170
|
+
| `auth.userId` | `string \| null` | User-plane principal |
|
|
171
|
+
| `auth.scopes` | `ReadonlySet<string>` | Granted scopes (may include tenant-role union) |
|
|
172
|
+
| `auth.verified` | `boolean \| undefined` | Credential verified |
|
|
173
|
+
| `auth.apiKeyId` | `string \| null \| undefined` | API-key principal when present |
|
|
174
|
+
| `operator.id` | `string \| null` | Operator-plane principal |
|
|
175
|
+
| `meta.ip` / `userId` | optional | Subject dims for rate `keyBy` |
|
|
176
|
+
|
|
177
|
+
Policies must not touch the store, vault, or network — those belong in `do` via `fx`.
|
|
178
|
+
|
|
179
|
+
## Declaration Forms
|
|
180
|
+
|
|
181
|
+
| Form | Example | Notes |
|
|
182
|
+
| --------------- | ------------------------------------------------------ | --------------------------------- |
|
|
183
|
+
| Predicate | `gate.policy("member", ({ auth }) => !!auth.verified)` | Shortest |
|
|
184
|
+
| Options | `gate.policy("member", { check, description? })` | Console / docs label |
|
|
185
|
+
| Scope shorthand | `gate.scope("notes:write")` | Records `scopes: ["notes:write"]` |
|
|
186
|
+
| Public sentinel | `gate.public` | Always allows; reserved name |
|
|
187
|
+
| Chain | `gate.all(a, b, c)` | Flattened at attach time |
|
|
188
|
+
|
|
189
|
+
Reserved: `gate.policy("public", …)` and `gate.scope("public")` throw — use `gate.public`.
|
|
190
|
+
|
|
191
|
+
## Module:Action Permissions
|
|
192
|
+
|
|
193
|
+
<Callout title="Detailed section">
|
|
194
|
+
Scopes with a colon (`notes:write`) are Module:Action pairs. Manifest + Console derive the
|
|
195
|
+
permission catalog from Flows, effects, and gate scopes — you do not hand-maintain a second list.
|
|
196
|
+
</Callout>
|
|
197
|
+
|
|
198
|
+
| Source | Example pair |
|
|
199
|
+
| ------------------------------ | -------------------------------- |
|
|
200
|
+
| Flow id `notes.create` | `notes:create` |
|
|
201
|
+
| `gate.scope("booking:create")` | `booking:create` |
|
|
202
|
+
| Effect `reads: ["sql:notes"]` | `store.sql:read` |
|
|
203
|
+
| Operator-plane Flow | also `console:…` when applicable |
|
|
204
|
+
|
|
205
|
+
**Consequence:** prefer `gate.scope("notes:write")` over a one-off policy with the same string —
|
|
206
|
+
the scope is recorded on the declaration for Manifest / Console.
|
|
207
|
+
|
|
208
|
+
Tenant roles may grant **application** scopes only (`notes:write`), not `console:*`. See
|
|
209
|
+
[Tenancy](/docs/elements/gate/tenancy).
|
|
210
|
+
|
|
211
|
+
## Attaching on Resources & Live
|
|
212
|
+
|
|
213
|
+
Chain once on the mount — every verb (and live, when present) inherits the same gates:
|
|
214
|
+
|
|
215
|
+
```typescript title="src/flows/notes/index.ts"
|
|
216
|
+
import { on, http } from "okengine";
|
|
217
|
+
import { member, notesWrite } from "@/core/gate";
|
|
218
|
+
import { notesResource } from "./resource";
|
|
219
|
+
|
|
220
|
+
export const notes = on(http.resource("/notes", notesResource.all()).gate(member, notesWrite));
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Live firehoses use the same `.gate(...)` fluent:
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
on(http.live(orderStatus).gate(member));
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
See [HTTP · Resources](/docs/elements/flow/http#resources) and
|
|
230
|
+
[HTTP · Live Streams](/docs/elements/flow/http#live-streams).
|
|
231
|
+
|
|
232
|
+
## Denial Mapping
|
|
233
|
+
|
|
234
|
+
| Situation | Code | Status | `error.data` |
|
|
235
|
+
| -------------------------------- | -------------- | ------ | --------------------------- |
|
|
236
|
+
| Policy denied, no `auth.userId` | `Unauthorized` | 401 | `{}` |
|
|
237
|
+
| Policy denied, principal present | `Forbidden` | 403 | `{ gate, reason }` |
|
|
238
|
+
| Unknown gate name at runtime | deny | — | `reason: "unknown gate: …"` |
|
|
239
|
+
|
|
240
|
+
`reason` is `"policy denied"` for failed predicates (unless a rate gate burned earlier).
|
|
241
|
+
|
|
242
|
+
## Troubleshooting
|
|
243
|
+
|
|
244
|
+
<Accordions>
|
|
245
|
+
|
|
246
|
+
<Accordion title="403 Forbidden but the user looks signed in">
|
|
247
|
+
A later gate in the chain failed — read `error.data.gate`. Confirm the session / key actually
|
|
248
|
+
carries that scope (`fx.auth.scopes`), including tenant-role unions when tenancy is on.
|
|
249
|
+
</Accordion>
|
|
250
|
+
|
|
251
|
+
<Accordion title="Policy always fails with verified users">
|
|
252
|
+
Check for `!!auth.verified` vs `auth.userId !== null`. Some principals have a `userId` before
|
|
253
|
+
verification completes — pick the predicate that matches your product rule.
|
|
254
|
+
</Accordion>
|
|
255
|
+
|
|
256
|
+
<Accordion title='TypeError: name "public" is reserved'>
|
|
257
|
+
Cause: `gate.policy: name "public" is reserved — use gate.public for intentionally unauthenticated
|
|
258
|
+
surfaces` (or the `gate.scope` variant). Rename the policy or use `.public()`.
|
|
259
|
+
</Accordion>
|
|
260
|
+
|
|
261
|
+
<Accordion title="TypeError: gate.all: at least one member is required">
|
|
262
|
+
Pass one or more policy / rate / nested `all` handles — empty `gate.all()` is invalid.
|
|
263
|
+
</Accordion>
|
|
264
|
+
|
|
265
|
+
<Accordion title="Scope present in JWT but route still Forbidden">
|
|
266
|
+
Tenant-role scopes union into `fx.auth.scopes` only when the Flow is tenant-scoped (default when
|
|
267
|
+
tenancy is on). A Flow with `tenantScoped: false` keeps session scopes only.
|
|
268
|
+
</Accordion>
|
|
269
|
+
|
|
270
|
+
</Accordions>
|
|
271
|
+
|
|
272
|
+
## Learn more
|
|
273
|
+
|
|
274
|
+
- [Authentication](/docs/elements/gate/auth) — how `fx.auth` is filled
|
|
275
|
+
- [RLS](/docs/elements/gate/rls) — row policies from the same Gate identity
|
|
276
|
+
- [Rate Limits](/docs/elements/gate/rate-limits) — throttle on the same chain
|
|
277
|
+
- [Tenancy](/docs/elements/gate/tenancy) — tenant-role scope union
|
|
278
|
+
- [HTTP](/docs/elements/flow/http) — `.gate(...)` on triggers
|
|
279
|
+
|
|
280
|
+
## Next
|
|
281
|
+
|
|
282
|
+
<Cards>
|
|
283
|
+
<Card
|
|
284
|
+
title="RLS"
|
|
285
|
+
description="Stamp Gate identity into SQL row policies."
|
|
286
|
+
href="/docs/elements/gate/rls"
|
|
287
|
+
/>
|
|
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="fx.tenant.id and membership-scoped data."
|
|
296
|
+
href="/docs/elements/gate/tenancy"
|
|
297
|
+
/>
|
|
298
|
+
</Cards>
|