okengine 0.18.5 → 0.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +18 -10
- package/README.md +2 -2
- package/package.json +51 -50
- package/site/content/docs/ai/llms-txt.mdx +1 -1
- package/site/content/docs/ai/skills.mdx +4 -1
- 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 +208 -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 +235 -0
- package/site/content/docs/elements/ai/index.mdx +342 -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 +257 -0
- package/site/content/docs/elements/channel/email.mdx +269 -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 +301 -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 +448 -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 +375 -0
- package/site/content/docs/elements/index.mdx +20 -10
- package/site/content/docs/elements/signal/broadcast.mdx +519 -0
- package/site/content/docs/elements/signal/index.mdx +352 -0
- package/site/content/docs/elements/signal/live.mdx +591 -0
- package/site/content/docs/elements/signal/meta.json +5 -0
- package/site/content/docs/elements/signal/once.mdx +597 -0
- package/site/content/docs/elements/store/files.mdx +667 -0
- package/site/content/docs/elements/store/index.mdx +313 -0
- package/site/content/docs/elements/store/kv.mdx +637 -0
- package/site/content/docs/elements/store/meta.json +5 -0
- package/site/content/docs/elements/store/search.mdx +950 -0
- package/site/content/docs/elements/store/sql.mdx +941 -0
- package/site/content/docs/elements/vault/config.mdx +259 -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 +276 -0
- package/site/content/docs/elements/vault/secrets.mdx +288 -0
- package/site/content/docs/index.mdx +52 -40
- package/site/content/docs/meta.json +8 -7
- 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 +138 -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-architecture.mdx +264 -0
- package/site/content/docs/understand/try-it.mdx +58 -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 +80 -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/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 +257 -43
- 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-C_qLDhTq.js +4 -0
- package/src/console/ui-next/dist/assets/agent-disclosure-BHVqr3TN.js +1 -0
- package/src/console/ui-next/dist/assets/cache-glyph-CKe92lRQ.js +1 -0
- package/src/console/ui-next/dist/assets/call-pii-button-DEwTl8ZX.js +1 -0
- package/src/console/ui-next/dist/assets/collapsible-BCBtDrCt.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-JroqeuCp.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-CVHa0RTt.js +1 -0
- package/src/console/ui-next/dist/assets/highlighted-json-DjJW6hqe.js +154 -0
- package/src/console/ui-next/dist/assets/http-method-DC5HBdLU.js +1 -0
- package/src/console/ui-next/dist/assets/index-CYjiZ3WO.js +63 -0
- package/src/console/ui-next/dist/assets/index-D0zS5rKO.css +2 -0
- package/src/console/ui-next/dist/assets/observability-page-CAYMyKb3.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-yAQYLv75.js +18 -0
- package/src/console/ui-next/dist/assets/request-meta-D0yusGxJ.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-BTKJeJ02.js +41 -0
- package/src/console/ui-next/dist/assets/trace-detail-sheet-Bp-Yygs5.js +2 -0
- package/src/console/ui-next/dist/assets/tree-expand-toggle-DoaVDfAM.js +55 -0
- package/src/console/ui-next/dist/assets/units-page-BRz7xyYL.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-3jQt-bOJ.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.test.ts +1 -1
- package/src/console/ui-next/src/features/flows/graph/element-map.ts +5 -3
- 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/trace-detail.test.ts +4 -5
- package/src/console/ui-next/src/features/flows/traces/trace-gates.ts +3 -6
- 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/gate/declare.ts +1 -1
- package/src/elements/gate.ts +1 -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 +214 -0
- package/src/elements/store/search-runtime.pglite.test.ts +186 -0
- package/src/elements/store/search-runtime.ts +314 -0
- package/src/elements/store/search.test.ts +226 -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 +52 -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 +2 -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 -144
- 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 -114
- 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-3-EFj-2G.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-cVFnA4HH.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-CMIgUbD0.js +0 -66
- package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +0 -2
- package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +0 -1
- package/src/console/ui-next/dist/assets/observability-page-CQ3p34ip.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-l8FeKfnP.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-CL-d_mLE.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
|
@@ -4,17 +4,18 @@
|
|
|
4
4
|
"pages": [
|
|
5
5
|
"index",
|
|
6
6
|
"---01 Understand---",
|
|
7
|
+
"understand",
|
|
8
|
+
"---02 Elements---",
|
|
7
9
|
"elements",
|
|
8
|
-
"---
|
|
9
|
-
"get-started",
|
|
10
|
-
"ai",
|
|
11
|
-
"---03 Operate---",
|
|
12
|
-
"deployment",
|
|
13
|
-
"---04 Extend---",
|
|
10
|
+
"---03 Extend---",
|
|
14
11
|
"plugins",
|
|
15
12
|
"providers",
|
|
16
13
|
"recipes",
|
|
14
|
+
"---04 Client---",
|
|
15
|
+
"client",
|
|
17
16
|
"---05 Reference---",
|
|
18
|
-
"reference"
|
|
17
|
+
"reference",
|
|
18
|
+
"---06 AI Resources---",
|
|
19
|
+
"ai"
|
|
19
20
|
]
|
|
20
21
|
}
|
|
@@ -71,7 +71,8 @@ const origins = configSource({
|
|
|
71
71
|
db: { store: db },
|
|
72
72
|
kv: cache,
|
|
73
73
|
});
|
|
74
|
-
|
|
74
|
+
const corsSyncClock = clock("cors.sync", { every: "30s" });
|
|
75
|
+
on(corsSyncClock, origins.sync());
|
|
75
76
|
export const app = oke({ name: "shop", env: "dev" }).plug(cors(origins));
|
|
76
77
|
```
|
|
77
78
|
|
|
@@ -18,6 +18,8 @@ export const app = oke({ name: "shop", env: "dev" }).plug(csrf());
|
|
|
18
18
|
|
|
19
19
|
`GET`/`HEAD`/`OPTIONS`/`QUERY` always pass. A `POST` with `Sec-Fetch-Site: cross-site` now gets the gate element's typed `Forbidden` — unless its `Origin` is yours or allow-listed.
|
|
20
20
|
|
|
21
|
+
When `gate.auth.cookies.enabled`, the server **soft-requires** this plugin: prod boots refuse without it; dev/test warn. Use `allowNoHeader: false` for cookie-only SPAs.
|
|
22
|
+
|
|
21
23
|
## How it decides
|
|
22
24
|
|
|
23
25
|
<Steps>
|
|
@@ -73,7 +75,8 @@ const rules = configSource({
|
|
|
73
75
|
db: { store: db },
|
|
74
76
|
kv: cache,
|
|
75
77
|
});
|
|
76
|
-
|
|
78
|
+
const csrfSyncClock = clock("csrf.sync", { every: "30s" });
|
|
79
|
+
on(csrfSyncClock, rules.sync());
|
|
77
80
|
export const app = oke({ name: "shop", env: "dev" }).plug(csrf(rules));
|
|
78
81
|
```
|
|
79
82
|
|
|
@@ -103,7 +103,8 @@ const headerConfig = configSource({
|
|
|
103
103
|
db: { store: db },
|
|
104
104
|
kv: cache,
|
|
105
105
|
});
|
|
106
|
-
|
|
106
|
+
const headerSyncClock = clock("headers.sync", { every: "30s" });
|
|
107
|
+
on(headerSyncClock, headerConfig.sync());
|
|
107
108
|
export const app = oke({ name: "shop", env: "dev" }).plug(headers(headerConfig));
|
|
108
109
|
```
|
|
109
110
|
|
|
@@ -69,7 +69,8 @@ const rules = configSource({
|
|
|
69
69
|
db: { store: db },
|
|
70
70
|
kv: cache,
|
|
71
71
|
});
|
|
72
|
-
|
|
72
|
+
const ipRulesSyncClock = clock("ip-allowlist.sync", { every: "30s" });
|
|
73
|
+
on(ipRulesSyncClock, rules.sync());
|
|
73
74
|
export const app = oke({ name: "shop", env: "dev" }).plug(ipAllowlist(rules));
|
|
74
75
|
```
|
|
75
76
|
|
|
@@ -63,6 +63,12 @@ const { data } = await api.auth.verifyMagicLink({ token });
|
|
|
63
63
|
|
|
64
64
|
</Steps>
|
|
65
65
|
|
|
66
|
+
<Callout type="info" title="Pre-account hijack defense">
|
|
67
|
+
Completing verify proves inbox ownership. An **unverified** email+password account parked on that
|
|
68
|
+
address is reclaimed (sessions revoked, hashes cleared, `emailVerified` set). Verified owners
|
|
69
|
+
re-auth only — no credential purge.
|
|
70
|
+
</Callout>
|
|
71
|
+
|
|
66
72
|
## Options
|
|
67
73
|
|
|
68
74
|
| Option | Type | Default | Meaning |
|
|
@@ -78,7 +78,8 @@ const maintenance = configSource({
|
|
|
78
78
|
db: { store: db },
|
|
79
79
|
kv: cache,
|
|
80
80
|
});
|
|
81
|
-
|
|
81
|
+
const maintenanceSyncClock = clock("maintenance.sync", { every: "30s" });
|
|
82
|
+
on(maintenanceSyncClock, maintenance.sync());
|
|
82
83
|
export const app = oke({ name: "shop", env: "dev" }).plug(maintenanceMode(maintenance));
|
|
83
84
|
```
|
|
84
85
|
|
|
@@ -173,7 +173,7 @@ via `POST /auth/oauth/{provider}/link`.
|
|
|
173
173
|
|
|
174
174
|
- [Gate](/docs/elements/gate) — `gate.auth`
|
|
175
175
|
- [Passkey](/docs/plugins/passkey) — phishing-resistant alternative
|
|
176
|
-
- [Client](/docs/
|
|
176
|
+
- [Client · Auth](/docs/client/auth) — calling `/auth` from the browser
|
|
177
177
|
|
|
178
178
|
## Next
|
|
179
179
|
|
|
@@ -100,6 +100,12 @@ const { data } = await api.auth.verifyOtp({
|
|
|
100
100
|
|
|
101
101
|
</Steps>
|
|
102
102
|
|
|
103
|
+
<Callout type="info" title="Pre-account hijack defense (email)">
|
|
104
|
+
App-mode email verify proves inbox ownership. An **unverified** parked email+password account is
|
|
105
|
+
reclaimed (sessions revoked, hashes cleared, `emailVerified` set) before the OTP session. Verified
|
|
106
|
+
owners re-auth only.
|
|
107
|
+
</Callout>
|
|
108
|
+
|
|
103
109
|
## Modes
|
|
104
110
|
|
|
105
111
|
| | Provider mode | App mode |
|
|
@@ -202,6 +208,7 @@ In `local` / `test` the `console` driver captures messages. Use
|
|
|
202
208
|
## Learn more
|
|
203
209
|
|
|
204
210
|
- [Magic link](/docs/plugins/magic-link) — link instead of a code
|
|
211
|
+
- [Two-factor](/docs/plugins/two-factor) — email OTP / TOTP as a **second** factor after password (distinct from this primary `/auth/otp` sign-in)
|
|
205
212
|
- [Channel](/docs/elements/channel) — `fx.send`, `fx.sendOtp`, drivers, Mailpit
|
|
206
213
|
- [Gate](/docs/elements/gate) — `gate.auth`
|
|
207
214
|
|
|
@@ -209,6 +216,10 @@ In `local` / `test` the `console` driver captures messages. Use
|
|
|
209
216
|
|
|
210
217
|
<Cards>
|
|
211
218
|
<Card title="Magic link" description="Email link sign-in." href="/docs/plugins/magic-link" />
|
|
212
|
-
<Card
|
|
219
|
+
<Card
|
|
220
|
+
title="Two-factor"
|
|
221
|
+
description="TOTP / email OTP second factor."
|
|
222
|
+
href="/docs/plugins/two-factor"
|
|
223
|
+
/>
|
|
213
224
|
<Card title="Channel" description="Delivery drivers and Mailpit." href="/docs/elements/channel" />
|
|
214
225
|
</Cards>
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Passkey"
|
|
3
|
-
description: "Official plugin — WebAuthn register and authenticate under /auth with
|
|
3
|
+
description: "Official plugin — WebAuthn register and authenticate under /auth with UV, signCount, and ceremony session binding."
|
|
4
4
|
icon: "FingerprintPattern"
|
|
5
5
|
source: "docs/spec/unified-theory.md"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
`passkey()` adds register and authenticate Flows for WebAuthn credentials
|
|
9
|
-
Options return a challenge
|
|
10
|
-
|
|
8
|
+
`passkey()` adds register and authenticate Flows for WebAuthn credentials
|
|
9
|
+
(`oke_passkeys`). Options return a challenge plus a ceremony `sessionId`;
|
|
10
|
+
register/authenticate verify client data, authenticator data, and ECDSA P-256.
|
|
11
11
|
|
|
12
12
|
<Callout title="The one rule">
|
|
13
13
|
Enable `gate.auth`, then `.plug(passkey())`. Registration needs a Bearer session; authenticate is
|
|
14
|
-
public.
|
|
14
|
+
public. Echo the options `sessionId` with every ceremony — UV=false assertions never mint a
|
|
15
15
|
session.
|
|
16
16
|
</Callout>
|
|
17
17
|
|
|
@@ -42,15 +42,16 @@ Wire Bearer on `createClient` (`auth.getToken` / `memorySession`) — calls take
|
|
|
42
42
|
|
|
43
43
|
```typescript
|
|
44
44
|
const opts = await api.auth.passkeyRegisterOptions({});
|
|
45
|
-
// opts.data: { challenge, rpId, userId }
|
|
45
|
+
// opts.data: { challenge, sessionId, rpId, userId }
|
|
46
46
|
|
|
47
47
|
await api.auth.passkeyRegister({
|
|
48
48
|
credentialId: "...", // base64url
|
|
49
49
|
publicKey: "...", // base64url SPKI (ECDSA P-256)
|
|
50
50
|
userId: opts.data!.userId,
|
|
51
51
|
challenge: opts.data!.challenge,
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
sessionId: opts.data!.sessionId,
|
|
53
|
+
clientDataJSON: "...", // base64url JSON { type: "webauthn.create", challenge, origin }
|
|
54
|
+
authenticatorData: "...", // base64url (UP|UV required)
|
|
54
55
|
signature: "...", // base64url ECDSA over authData || SHA-256(clientDataJSON)
|
|
55
56
|
});
|
|
56
57
|
```
|
|
@@ -67,19 +68,30 @@ const opts = await api.auth.passkeyAuthenticateOptions({});
|
|
|
67
68
|
const { data } = await api.auth.passkeyAuthenticate({
|
|
68
69
|
credentialId: "...",
|
|
69
70
|
challenge: opts.data!.challenge,
|
|
70
|
-
|
|
71
|
+
sessionId: opts.data!.sessionId,
|
|
72
|
+
clientDataJSON: "...", // type must be "webauthn.get"
|
|
71
73
|
authenticatorData: "...",
|
|
72
74
|
signature: "...",
|
|
73
75
|
});
|
|
74
76
|
```
|
|
75
77
|
|
|
76
78
|
Paths: `POST /auth/passkey/authenticate/options`, `POST /auth/passkey/authenticate`.
|
|
77
|
-
Challenges are single-use
|
|
79
|
+
Challenges are single-use and bound to `sessionId`; wrong origin → `invalid_origin`; UV
|
|
80
|
+
missing → `user_not_verified`; cloned counter → `reregister_required`.
|
|
78
81
|
|
|
79
82
|
</Step>
|
|
80
83
|
|
|
81
84
|
</Steps>
|
|
82
85
|
|
|
86
|
+
## Server checks
|
|
87
|
+
|
|
88
|
+
| Check | Behavior |
|
|
89
|
+
| ----------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
|
90
|
+
| Ceremony type | `clientDataJSON.type` must be `webauthn.create` (register) or `webauthn.get` (authenticate) |
|
|
91
|
+
| User verification | AuthenticatorData UV bit must be set — UV=false is always rejected |
|
|
92
|
+
| Challenge binding | Challenge hash + `sessionId` must match; TTL ≤ 5 minutes; single-use |
|
|
93
|
+
| Signature counter | Stored per credential; if stored ≠ 0 and incoming `signCount` ≤ stored → delete credential, warn, `reregister_required` |
|
|
94
|
+
|
|
83
95
|
## Options
|
|
84
96
|
|
|
85
97
|
| Option | Type | Default | Meaning |
|
|
@@ -101,8 +113,8 @@ Challenges are single-use; wrong origin → `invalid_origin`; bad signature →
|
|
|
101
113
|
| `auth.passkeyAuthenticateOptions` | `POST /auth/passkey/authenticate/options` | `gate.public` + otp rate |
|
|
102
114
|
| `auth.passkeyAuthenticate` | `POST /auth/passkey/authenticate` | `gate.public` + otp rate |
|
|
103
115
|
|
|
104
|
-
**Consequence:** a stolen `credentialId` without the private key cannot mint a session.
|
|
105
|
-
`origins` to your real app origins before production.
|
|
116
|
+
**Consequence:** a stolen `credentialId` without the private key (and UV) cannot mint a session.
|
|
117
|
+
Set `origins` to your real app origins before production.
|
|
106
118
|
|
|
107
119
|
## Troubleshooting
|
|
108
120
|
|
|
@@ -116,11 +128,23 @@ Sign in with another method first. `userId` in the body must match the Bearer se
|
|
|
116
128
|
|
|
117
129
|
`clientDataJSON.origin` must be in `passkey({ origins })`. Default allows only localhost HTTP/S.
|
|
118
130
|
|
|
131
|
+
</Accordion>
|
|
132
|
+
<Accordion title="authenticate returns user_not_verified">
|
|
133
|
+
|
|
134
|
+
AuthenticatorData UV bit was not set. Require user verification on the client
|
|
135
|
+
(`userVerification: "required"`) — the server never accepts UV=false.
|
|
136
|
+
|
|
137
|
+
</Accordion>
|
|
138
|
+
<Accordion title="authenticate returns reregister_required">
|
|
139
|
+
|
|
140
|
+
Signature counter did not increase (possible cloned authenticator). That credential was
|
|
141
|
+
deleted — register a fresh passkey for the account.
|
|
142
|
+
|
|
119
143
|
</Accordion>
|
|
120
144
|
<Accordion title="authenticate returns invalid_credentials">
|
|
121
145
|
|
|
122
|
-
Unknown `credentialId`,
|
|
123
|
-
Re-run authenticate options for a fresh challenge
|
|
146
|
+
Unknown `credentialId`, wrong/expired/`sessionId` mismatch, bad signature, wrong ceremony
|
|
147
|
+
type, or rpId hash mismatch. Re-run authenticate options for a fresh challenge + `sessionId`.
|
|
124
148
|
|
|
125
149
|
</Accordion>
|
|
126
150
|
</Accordions>
|
|
@@ -129,7 +153,7 @@ Re-run authenticate options for a fresh challenge, then sign with the enrolled p
|
|
|
129
153
|
|
|
130
154
|
- [Two-factor](/docs/plugins/two-factor) — TOTP step-up
|
|
131
155
|
- [Gate](/docs/elements/gate) — `gate.auth`
|
|
132
|
-
- [Client](/docs/
|
|
156
|
+
- [Client · Auth](/docs/client/auth) — calling `/auth` from the browser
|
|
133
157
|
|
|
134
158
|
## Next
|
|
135
159
|
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Two-factor"
|
|
3
|
-
description: "Official plugin — TOTP
|
|
3
|
+
description: "Official plugin — method-locked TOTP or email OTP after first-factor sign-in, with step-up enrollment and method change."
|
|
4
4
|
icon: "LockKeyhole"
|
|
5
5
|
source: "docs/spec/unified-theory.md"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
`twoFactor()` adds
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
`twoFactor()` adds a second factor after password (or username) sign-in: RFC 6238
|
|
9
|
+
TOTP or email OTP. When 2FA is enabled, first-factor success withholds session
|
|
10
|
+
tokens and returns a **method-locked** challenge.
|
|
11
11
|
|
|
12
12
|
<Callout title="The one rule">
|
|
13
|
-
Enable `gate.auth`, then `.plug(twoFactor())`.
|
|
14
|
-
|
|
13
|
+
Enable `gate.auth`, then `.plug(twoFactor())`. Login challenges lock the configured method (`totp`
|
|
14
|
+
or `email_otp`). Mid-challenge QR enrollment returns `Forbidden`. Method change needs step-up
|
|
15
|
+
verify → provision → confirm.
|
|
15
16
|
</Callout>
|
|
16
17
|
|
|
17
18
|
## Quick start
|
|
@@ -35,69 +36,124 @@ export const app = oke({
|
|
|
35
36
|
</Step>
|
|
36
37
|
|
|
37
38
|
<Step>
|
|
38
|
-
###
|
|
39
|
+
### First-time enable (session required)
|
|
39
40
|
|
|
40
41
|
Wire Bearer on `createClient` (`auth.getToken` / `memorySession`) — calls take input only.
|
|
41
42
|
|
|
42
43
|
```typescript
|
|
43
44
|
const { data } = await api.auth.twoFactorEnable({});
|
|
44
|
-
// data.secret, data.otpauthUrl, data.recoveryCodes
|
|
45
|
+
// data.secret, data.otpauthUrl, data.recoveryCodes, data.method === "totp"
|
|
45
46
|
```
|
|
46
47
|
|
|
47
|
-
`POST /auth/two-factor/enable` — writes `oke_two_factor`. Store recovery codes once
|
|
48
|
-
|
|
48
|
+
`POST /auth/two-factor/enable` — writes `oke_two_factor`. Store recovery codes once.
|
|
49
|
+
Re-enrollment when 2FA is already enabled requires step-up first
|
|
50
|
+
(`twoFactorStepUp`). An active login challenge → `Forbidden` / `active_2fa_challenge`.
|
|
49
51
|
|
|
50
52
|
</Step>
|
|
51
53
|
|
|
52
54
|
<Step>
|
|
53
|
-
###
|
|
55
|
+
### Sign-in then verify
|
|
54
56
|
|
|
55
57
|
```typescript
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
+
const signIn = await api.auth.signInUsername({ username, password });
|
|
59
|
+
if ("twoFactorRequired" in signIn.data && signIn.data.twoFactorRequired) {
|
|
60
|
+
const { data } = await api.auth.twoFactorVerify({
|
|
61
|
+
challengeId: signIn.data.challengeId,
|
|
62
|
+
code: "123456",
|
|
63
|
+
});
|
|
64
|
+
// hybrid session tokens
|
|
65
|
+
}
|
|
58
66
|
```
|
|
59
67
|
|
|
60
|
-
`POST /auth/two-factor/verify
|
|
61
|
-
`
|
|
68
|
+
`POST /auth/two-factor/verify` takes `{ challengeId, code }` — not a bare
|
|
69
|
+
`userId`. Missing challenge, method mismatch, or bad code → `AuthFailed` /
|
|
70
|
+
`invalid_credentials`.
|
|
62
71
|
|
|
63
72
|
</Step>
|
|
64
73
|
|
|
65
74
|
</Steps>
|
|
66
75
|
|
|
76
|
+
## Method lock
|
|
77
|
+
|
|
78
|
+
When a login challenge is issued, the active method is recorded on the pending
|
|
79
|
+
challenge. Until that challenge is consumed or expires:
|
|
80
|
+
|
|
81
|
+
- Only codes for the **locked** method are accepted.
|
|
82
|
+
- `twoFactorEnable`, `twoFactorChangeMethod`, `twoFactorConfirmChange`, and
|
|
83
|
+
`twoFactorDisable` return `Forbidden` with `active_2fa_challenge` if called
|
|
84
|
+
for that user (including with an older Bearer session).
|
|
85
|
+
|
|
86
|
+
This closes the July–August 2026 method-switching bypass pattern (switch to
|
|
87
|
+
TOTP enrollment mid email-OTP challenge without proving the current factor).
|
|
88
|
+
|
|
89
|
+
## Step-up and method change
|
|
90
|
+
|
|
91
|
+
1. `POST /auth/two-factor/step-up` — `{ code, purpose }` verifies the **current**
|
|
92
|
+
method and grants a short-lived step-up (`enroll` | `change` | `disable`).
|
|
93
|
+
2. `POST /auth/two-factor/change-method` — `{ method, email? }` provisions the new
|
|
94
|
+
method in a pending state (TOTP QR or email OTP). Requires step-up.
|
|
95
|
+
3. `POST /auth/two-factor/confirm-change` — `{ code }` activates the new method and
|
|
96
|
+
**immediately invalidates** the old secret / recovery codes.
|
|
97
|
+
|
|
98
|
+
Disable also requires step-up when 2FA is already enabled.
|
|
99
|
+
|
|
67
100
|
## Options
|
|
68
101
|
|
|
69
|
-
| Option
|
|
70
|
-
|
|
|
71
|
-
| `secret`
|
|
72
|
-
| `sessions`
|
|
73
|
-
| `now`
|
|
74
|
-
| `factors`
|
|
75
|
-
| `issuer`
|
|
102
|
+
| Option | Type | Default | Meaning |
|
|
103
|
+
| --------------- | ----------------------- | ---------- | -------------------------------------------- |
|
|
104
|
+
| `secret` | `string` | active\* | HMAC secret (\*from `gate.auth`) |
|
|
105
|
+
| `sessions` | `SessionStore` | active\* | Session store |
|
|
106
|
+
| `now` | `() => number` | `Date.now` | Injectable clock |
|
|
107
|
+
| `factors` | `TwoFactorStore` | new | Per-user method + TOTP / recovery |
|
|
108
|
+
| `issuer` | `string` | `"oke"` | Label in `otpauth://` URLs |
|
|
109
|
+
| `pending` | `PendingTwoFactorStore` | active\* | Login challenges |
|
|
110
|
+
| `stepUp` | `StepUpStore` | active\* | Privileged-op grants |
|
|
111
|
+
| `verifications` | `VerificationStore` | active\* | Email OTP as second factor (`2fa:{userId}`) |
|
|
112
|
+
| `exposeDevOtp` | `boolean` | `false` | Return `devOtp` on email_otp challenge paths |
|
|
76
113
|
|
|
77
114
|
## Surfaces
|
|
78
115
|
|
|
79
|
-
| Flow
|
|
80
|
-
|
|
|
81
|
-
| `auth.twoFactorEnable`
|
|
82
|
-
| `auth.twoFactorVerify`
|
|
83
|
-
| `auth.
|
|
116
|
+
| Flow | Path | Gate |
|
|
117
|
+
| ------------------------------- | ----------------------------------------- | ------------------------ |
|
|
118
|
+
| `auth.twoFactorEnable` | `POST /auth/two-factor/enable` | session + bearer |
|
|
119
|
+
| `auth.twoFactorVerify` | `POST /auth/two-factor/verify` | `gate.public` + otp rate |
|
|
120
|
+
| `auth.twoFactorStepUp` | `POST /auth/two-factor/step-up` | session + bearer |
|
|
121
|
+
| `auth.twoFactorChangeMethod` | `POST /auth/two-factor/change-method` | session + bearer |
|
|
122
|
+
| `auth.twoFactorConfirmChange` | `POST /auth/two-factor/confirm-change` | session + bearer |
|
|
123
|
+
| `auth.twoFactorRequestEmailOtp` | `POST /auth/two-factor/request-email-otp` | public + otp rate |
|
|
124
|
+
| `auth.twoFactorDisable` | `POST /auth/two-factor/disable` | session + bearer |
|
|
84
125
|
|
|
85
|
-
**Consequence:**
|
|
86
|
-
|
|
126
|
+
**Consequence:** email/password and username sign-in return
|
|
127
|
+
`{ twoFactorRequired, challengeId, method, userId }` (no tokens) when the
|
|
128
|
+
account has 2FA enabled. Complete login only via `twoFactorVerify`.
|
|
87
129
|
|
|
88
130
|
## Troubleshooting
|
|
89
131
|
|
|
90
132
|
<Accordions>
|
|
133
|
+
<Accordion title="twoFactorEnable returns Forbidden active_2fa_challenge">
|
|
134
|
+
|
|
135
|
+
Finish or wait out the login 2FA challenge first. Enrollment is blocked while
|
|
136
|
+
an unresolved challenge exists for that user.
|
|
137
|
+
|
|
138
|
+
</Accordion>
|
|
139
|
+
<Accordion title="twoFactorEnable returns Forbidden step_up_required">
|
|
140
|
+
|
|
141
|
+
2FA is already enabled. Call `twoFactorStepUp` with a valid current-method code
|
|
142
|
+
(`purpose: "enroll"`), then enable again.
|
|
143
|
+
|
|
144
|
+
</Accordion>
|
|
91
145
|
<Accordion title="twoFactorEnable returns AuthFailed unauthenticated">
|
|
92
146
|
|
|
93
|
-
Wire a session into `createClient` (`auth.getToken` / `memorySession`)
|
|
94
|
-
|
|
147
|
+
Wire a session into `createClient` (`auth.getToken` / `memorySession`) first —
|
|
148
|
+
enable is gated on Bearer. First-time enroll must happen **before** 2FA is
|
|
149
|
+
required on sign-in (or after a full session from a completed challenge).
|
|
95
150
|
|
|
96
151
|
</Accordion>
|
|
97
152
|
<Accordion title="verify always fails">
|
|
98
153
|
|
|
99
|
-
|
|
100
|
-
|
|
154
|
+
Submit `{ challengeId, code }` from the sign-in challenge response. TOTP must be
|
|
155
|
+
six digits (±1 window). Email OTP uses the code from the locked `email_otp`
|
|
156
|
+
challenge. A recovery code works once for TOTP, then is consumed.
|
|
101
157
|
|
|
102
158
|
</Accordion>
|
|
103
159
|
</Accordions>
|
|
@@ -107,7 +163,8 @@ enable. A recovery code works once, then is consumed.
|
|
|
107
163
|
- [Passkey](/docs/plugins/passkey) — WebAuthn register / authenticate
|
|
108
164
|
- [Gate](/docs/elements/gate) — session + policies
|
|
109
165
|
- [Username](/docs/plugins/username) — first factor to enroll against
|
|
110
|
-
- OTP
|
|
166
|
+
- Primary OTP sign-in (not a second factor)? See [OTP](/docs/plugins/otp) /
|
|
167
|
+
[Magic link](/docs/plugins/magic-link)
|
|
111
168
|
|
|
112
169
|
## Next
|
|
113
170
|
|
|
@@ -102,7 +102,7 @@ on purpose — do not treat it as "exists" in the UI.
|
|
|
102
102
|
|
|
103
103
|
- [Gate](/docs/elements/gate) — `gate.auth` and posture
|
|
104
104
|
- [Plugins](/docs/plugins) — all auth method plugins
|
|
105
|
-
- [Client](/docs/
|
|
105
|
+
- [Client · Auth](/docs/client/auth) — `createClient` + `memorySession`
|
|
106
106
|
|
|
107
107
|
## Next
|
|
108
108
|
|
|
@@ -81,7 +81,7 @@ Caddy has **no** service-discovery story for multiple `app` replicas. Once you r
|
|
|
81
81
|
discovers replicas from Docker labels instead of a static `reverse_proxy` target.
|
|
82
82
|
|
|
83
83
|
Also set `allowedHosts` to your public hostname before exposing the edge — see
|
|
84
|
-
[
|
|
84
|
+
[Security](/docs/reference/security).
|
|
85
85
|
|
|
86
86
|
## What the recipe configures
|
|
87
87
|
|
|
@@ -111,8 +111,8 @@ Expected with the local CA. Install Caddy's local root for that machine, or set
|
|
|
111
111
|
|
|
112
112
|
## Learn more
|
|
113
113
|
|
|
114
|
-
- [
|
|
115
|
-
- [
|
|
114
|
+
- [Traefik](/docs/recipes/traefik) — multi-replica discovery via Docker labels
|
|
115
|
+
- [nginx](/docs/recipes/nginx) — static HTTP reverse proxy
|
|
116
116
|
- [Security](/docs/reference/security) — `allowedHosts`
|
|
117
117
|
|
|
118
118
|
## Next
|
|
@@ -12,7 +12,7 @@ These recipes are real, working infrastructure choices behind the same two templ
|
|
|
12
12
|
|
|
13
13
|
<Callout title="The one rule">
|
|
14
14
|
Vendor choice lives in `images[…]` — never in `drivers.*`, which only ever say protocol ids
|
|
15
|
-
(`postgres`, `redis`, `s3`, `smtp`, `meilisearch`, `openai-compatible`,
|
|
15
|
+
(`postgres`, `redis`, `s3`, `smtp`, `meilisearch`, `openai-compatible`, …).
|
|
16
16
|
</Callout>
|
|
17
17
|
|
|
18
18
|
## SQL
|
|
@@ -98,45 +98,35 @@ Opt-in via `images.proxy` — leave unset until you need an edge or `--scale app
|
|
|
98
98
|
/>
|
|
99
99
|
</Cards>
|
|
100
100
|
|
|
101
|
-
## AI
|
|
101
|
+
## AI
|
|
102
102
|
|
|
103
|
-
|
|
104
|
-
|
|
103
|
+
Compose does not manage inference. Prefer [OpenRouter](/docs/recipes/openrouter)
|
|
104
|
+
for zero-Docker `fx.ask`, or BYO any OpenAI-shaped `/v1` via `OKE_AI_URL` /
|
|
105
|
+
`baseUrl` — see [Models](/docs/elements/ai/models).
|
|
105
106
|
|
|
106
|
-
| Workload
|
|
107
|
-
|
|
|
108
|
-
|
|
|
109
|
-
|
|
|
110
|
-
|
|
|
111
|
-
| Structured / agent GPU workloads | [SGLang](/docs/recipes/sglang) |
|
|
112
|
-
| Managed cloud | Anthropic / OpenAI / … (no `images.ai`) |
|
|
113
|
-
|
|
114
|
-
All four recipes **loopback-publish only** and pin explicit tags (never
|
|
115
|
-
`latest`). Load models from curated registries — never arbitrary untrusted GGUF.
|
|
107
|
+
| Workload | Where |
|
|
108
|
+
| ------------------------------ | -------------------------------------- |
|
|
109
|
+
| Zero Docker / free cloud smoke | [OpenRouter](/docs/recipes/openrouter) |
|
|
110
|
+
| BYO OpenAI-compatible `/v1` | [Models](/docs/elements/ai/models) |
|
|
111
|
+
| Managed cloud (registry) | [Models](/docs/elements/ai/models) |
|
|
116
112
|
|
|
117
113
|
<Cards>
|
|
118
114
|
<Card
|
|
119
|
-
title="
|
|
120
|
-
description="
|
|
121
|
-
href="/docs/recipes/
|
|
122
|
-
/>
|
|
123
|
-
<Card
|
|
124
|
-
title="Ollama"
|
|
125
|
-
description="Fully supported alternative — library pull UX."
|
|
126
|
-
href="/docs/recipes/ollama"
|
|
115
|
+
title="OpenRouter"
|
|
116
|
+
description="Zero Docker — openrouter/free + auto baseUrl."
|
|
117
|
+
href="/docs/recipes/openrouter"
|
|
127
118
|
/>
|
|
128
|
-
<Card title="vLLM" description="Self-hosted multi-user GPU serving." href="/docs/recipes/vllm" />
|
|
129
119
|
<Card
|
|
130
|
-
title="
|
|
131
|
-
description="
|
|
132
|
-
href="/docs/
|
|
120
|
+
title="Models"
|
|
121
|
+
description="Registry providers + custom baseUrl / OKE_AI_URL."
|
|
122
|
+
href="/docs/elements/ai/models"
|
|
133
123
|
/>
|
|
134
124
|
</Cards>
|
|
135
125
|
|
|
136
126
|
## Learn more
|
|
137
127
|
|
|
138
128
|
- [Providers](/docs/providers) — managed cloud alternatives
|
|
139
|
-
- [
|
|
129
|
+
- [Configuration](/docs/reference/configuration) — `images` roles and compose derivation
|
|
140
130
|
- [Store](/docs/elements/store) — `sql` / `kv` / `files` / `index` facets
|
|
141
131
|
|
|
142
132
|
## Next
|
|
@@ -148,5 +138,5 @@ All four recipes **loopback-publish only** and pin explicit tags (never
|
|
|
148
138
|
href="/docs/recipes/postgres"
|
|
149
139
|
/>
|
|
150
140
|
<Card title="Providers" description="Managed cloud alternatives." href="/docs/providers" />
|
|
151
|
-
<Card title="
|
|
141
|
+
<Card title="Caddy" description="Automatic HTTPS edge." href="/docs/recipes/caddy" />
|
|
152
142
|
</Cards>
|
|
@@ -126,9 +126,9 @@ surfaces as `meilisearch index: unreachable at <url> — …`.
|
|
|
126
126
|
|
|
127
127
|
<Cards>
|
|
128
128
|
<Card
|
|
129
|
-
title="
|
|
130
|
-
description="
|
|
131
|
-
href="/docs/recipes/
|
|
129
|
+
title="OpenRouter"
|
|
130
|
+
description="Zero Docker cloud AI for the same stack."
|
|
131
|
+
href="/docs/recipes/openrouter"
|
|
132
132
|
/>
|
|
133
133
|
<Card
|
|
134
134
|
title="Postgres"
|
|
@@ -100,8 +100,8 @@ upstream block.
|
|
|
100
100
|
|
|
101
101
|
## Learn more
|
|
102
102
|
|
|
103
|
-
- [
|
|
104
|
-
- [
|
|
103
|
+
- [Caddy](/docs/recipes/caddy) — automatic HTTPS alternative
|
|
104
|
+
- [Traefik](/docs/recipes/traefik) — multi-replica discovery via Docker labels
|
|
105
105
|
- [Security](/docs/reference/security) — `allowedHosts`
|
|
106
106
|
|
|
107
107
|
## Next
|