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
|
@@ -5,74 +5,122 @@ icon: "BookOpen"
|
|
|
5
5
|
source: "docs/spec/unified-theory.md"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
Welcome to the
|
|
8
|
+
Welcome to the OKEngine handbook. Keep the model small; let the backend grow without growing the mental model.
|
|
9
9
|
|
|
10
|
-
<Callout title="The
|
|
10
|
+
<Callout title="The Law">
|
|
11
11
|
Every backend behavior is a Flow: `on(Trigger) → Effects`. One species; triggers are typed values.
|
|
12
12
|
</Callout>
|
|
13
13
|
|
|
14
14
|
```ts
|
|
15
|
-
on(http.post(
|
|
16
|
-
on(
|
|
15
|
+
on(http.post(), createBooking); // path + name from the file tree
|
|
16
|
+
on(expireStaleClock, expireStale);
|
|
17
17
|
on(orderPlaced, sendReceipt);
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
## Understand
|
|
20
|
+
## 01 Understand
|
|
21
|
+
|
|
22
|
+
Master the mental model before exploring features:
|
|
21
23
|
|
|
22
24
|
<Cards>
|
|
23
25
|
<Card
|
|
24
|
-
title="
|
|
25
|
-
description="
|
|
26
|
-
href="/docs/
|
|
26
|
+
title="The Problem"
|
|
27
|
+
description="Three unrelated features that hit the exact same wall, and the timeline that shows why."
|
|
28
|
+
href="/docs/understand/the-problem"
|
|
27
29
|
/>
|
|
28
30
|
<Card
|
|
29
|
-
title="
|
|
30
|
-
description="The
|
|
31
|
-
href="/docs/
|
|
31
|
+
title="The Model"
|
|
32
|
+
description="The one rule that removes the disagreement between systems — stated plainly, in two parts."
|
|
33
|
+
href="/docs/understand/the-model"
|
|
34
|
+
/>
|
|
35
|
+
<Card
|
|
36
|
+
title="The Vocabulary"
|
|
37
|
+
description="The eight things the door recognizes — what each one replaces, and why nothing else made the cut."
|
|
38
|
+
href="/docs/understand/the-vocabulary"
|
|
32
39
|
/>
|
|
33
40
|
<Card
|
|
34
|
-
title="
|
|
35
|
-
description="The
|
|
36
|
-
href="/docs/
|
|
41
|
+
title="The Anatomy"
|
|
42
|
+
description="The five pieces behind on(trigger, flow) — on, trigger, flow, do, and fx — explained one at a time."
|
|
43
|
+
href="/docs/understand/the-anatomy"
|
|
44
|
+
/>
|
|
45
|
+
<Card
|
|
46
|
+
title="Try It"
|
|
47
|
+
description="From an empty folder to a Flow running in the Console — one sitting, minimal detour."
|
|
48
|
+
href="/docs/ai/try-it"
|
|
37
49
|
/>
|
|
38
50
|
</Cards>
|
|
39
51
|
|
|
40
|
-
##
|
|
52
|
+
## 02 Elements
|
|
53
|
+
|
|
54
|
+
Deep primitives and execution law:
|
|
41
55
|
|
|
42
56
|
<Cards>
|
|
43
57
|
<Card
|
|
44
|
-
title="
|
|
45
|
-
description="
|
|
46
|
-
href="/docs/
|
|
58
|
+
title="Elements"
|
|
59
|
+
description="Flow, Signal, Store, Clock, Gate, Vault, Channel, AI."
|
|
60
|
+
href="/docs/elements"
|
|
61
|
+
/>
|
|
62
|
+
<Card
|
|
63
|
+
title="Reference"
|
|
64
|
+
description="CLI, configuration, fx, errors, security, and plugins."
|
|
65
|
+
href="/docs/reference"
|
|
66
|
+
/>
|
|
67
|
+
<Card
|
|
68
|
+
title="AI Resources"
|
|
69
|
+
description="Runtime MCP (:6535), docs MCP (:6536), agent skills, and machine-readable docs."
|
|
70
|
+
href="/docs/ai"
|
|
47
71
|
/>
|
|
72
|
+
</Cards>
|
|
73
|
+
|
|
74
|
+
## 03 Extend
|
|
75
|
+
|
|
76
|
+
Connect to infrastructure and extend the model:
|
|
77
|
+
|
|
78
|
+
<Cards>
|
|
48
79
|
<Card
|
|
49
80
|
title="Plugins"
|
|
50
|
-
description="
|
|
81
|
+
description="Official extensions that add capabilities without creating a ninth element."
|
|
51
82
|
href="/docs/plugins"
|
|
52
83
|
/>
|
|
53
84
|
<Card
|
|
54
85
|
title="Providers"
|
|
55
|
-
description="
|
|
86
|
+
description="Managed cloud infrastructure behind protocol-named drivers."
|
|
56
87
|
href="/docs/providers"
|
|
57
88
|
/>
|
|
58
89
|
<Card
|
|
59
90
|
title="Recipes"
|
|
60
|
-
description="
|
|
91
|
+
description="Self-hosted Docker compose recipes for SQL, KV, search, and local AI."
|
|
61
92
|
href="/docs/recipes"
|
|
62
93
|
/>
|
|
94
|
+
</Cards>
|
|
95
|
+
|
|
96
|
+
## 04 Client
|
|
97
|
+
|
|
98
|
+
Call your Flows from a browser, CLI, or another service — typed, zero separate codegen:
|
|
99
|
+
|
|
100
|
+
<Cards>
|
|
63
101
|
<Card
|
|
64
|
-
title="
|
|
65
|
-
description="
|
|
66
|
-
href="/docs/
|
|
102
|
+
title="Overview"
|
|
103
|
+
description="createClient, envelopes as values, and how Client relates to the eight elements."
|
|
104
|
+
href="/docs/client"
|
|
67
105
|
/>
|
|
68
106
|
<Card
|
|
69
|
-
title="
|
|
70
|
-
description="
|
|
71
|
-
href="/docs/
|
|
107
|
+
title="Calling"
|
|
108
|
+
description="REST vs RPC, options, result helpers, resources, and remote types."
|
|
109
|
+
href="/docs/client/calling"
|
|
110
|
+
/>
|
|
111
|
+
<Card
|
|
112
|
+
title="Auth"
|
|
113
|
+
description="Bearer sessions, refresh once on 401, and gate denials as values."
|
|
114
|
+
href="/docs/client/auth"
|
|
115
|
+
/>
|
|
116
|
+
<Card
|
|
117
|
+
title="Live"
|
|
118
|
+
description="api.live SSE, resume, and live resource queries."
|
|
119
|
+
href="/docs/client/live"
|
|
72
120
|
/>
|
|
73
121
|
<Card
|
|
74
|
-
title="
|
|
75
|
-
description="
|
|
76
|
-
href="/docs/
|
|
122
|
+
title="React"
|
|
123
|
+
description="useSession, useLive, and useLiveQuery from okengine/client-react."
|
|
124
|
+
href="/docs/client/react"
|
|
77
125
|
/>
|
|
78
126
|
</Cards>
|
|
@@ -3,16 +3,19 @@
|
|
|
3
3
|
"icon": "BookOpen",
|
|
4
4
|
"pages": [
|
|
5
5
|
"index",
|
|
6
|
-
"---Understand
|
|
6
|
+
"---01 Understand---",
|
|
7
|
+
"understand",
|
|
8
|
+
"---02 Elements---",
|
|
7
9
|
"elements",
|
|
8
|
-
"---
|
|
9
|
-
"get-started",
|
|
10
|
-
"ai",
|
|
11
|
-
"deployment",
|
|
12
|
-
"reference",
|
|
10
|
+
"---03 Extend---",
|
|
13
11
|
"plugins",
|
|
14
|
-
"---Drivers---",
|
|
15
12
|
"providers",
|
|
16
|
-
"recipes"
|
|
13
|
+
"recipes",
|
|
14
|
+
"---04 Client---",
|
|
15
|
+
"client",
|
|
16
|
+
"---05 Reference---",
|
|
17
|
+
"reference",
|
|
18
|
+
"---06 AI Resources---",
|
|
19
|
+
"ai"
|
|
17
20
|
]
|
|
18
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
|