okengine 0.10.3 → 0.11.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/package.json +76 -10
- package/site/content/docs/deployment/docker-swarm.mdx +48 -111
- package/site/content/docs/deployment/docker.mdx +43 -36
- package/site/content/docs/deployment/index.mdx +9 -9
- package/site/content/docs/deployment/kubernetes.mdx +4 -4
- package/site/content/docs/elements/ai.mdx +7 -7
- package/site/content/docs/elements/channel.mdx +9 -7
- package/site/content/docs/elements/clock.mdx +8 -8
- package/site/content/docs/elements/flow.mdx +46 -47
- package/site/content/docs/elements/gate.mdx +6 -5
- package/site/content/docs/elements/signal.mdx +2 -2
- package/site/content/docs/elements/store.mdx +71 -78
- package/site/content/docs/elements/vault.mdx +89 -70
- package/site/content/docs/get-started/basic-usage.mdx +8 -8
- package/site/content/docs/get-started/installation.mdx +36 -36
- package/site/content/docs/get-started/introduction.mdx +11 -0
- package/site/content/docs/get-started/why.mdx +2 -2
- package/site/content/docs/plugins/anonymous.mdx +1 -1
- package/site/content/docs/plugins/magic-link.mdx +11 -11
- package/site/content/docs/plugins/otp.mdx +2 -2
- package/site/content/docs/plugins/passkey.mdx +1 -1
- package/site/content/docs/plugins/two-factor.mdx +1 -1
- package/site/content/docs/plugins/username.mdx +1 -1
- package/site/content/docs/recipes/caddy.mdx +1 -1
- package/site/content/docs/recipes/cockroachdb.mdx +2 -2
- package/site/content/docs/recipes/dragonfly.mdx +1 -1
- package/site/content/docs/recipes/index.mdx +1 -2
- package/site/content/docs/recipes/llama-cpp.mdx +21 -23
- package/site/content/docs/recipes/mailpit.mdx +11 -11
- package/site/content/docs/recipes/meilisearch.mdx +10 -10
- package/site/content/docs/recipes/meta.json +0 -1
- package/site/content/docs/recipes/ollama.mdx +1 -1
- package/site/content/docs/recipes/pgdog.mdx +12 -9
- package/site/content/docs/recipes/postgres.mdx +3 -3
- package/site/content/docs/recipes/redis.mdx +2 -2
- package/site/content/docs/recipes/rustfs.mdx +10 -7
- package/site/content/docs/recipes/sglang.mdx +1 -1
- package/site/content/docs/recipes/supabase-docker.mdx +1 -1
- package/site/content/docs/recipes/timescale.mdx +2 -2
- package/site/content/docs/recipes/valkey.mdx +1 -1
- package/site/content/docs/recipes/vllm.mdx +1 -1
- package/site/content/docs/recipes/yugabytedb.mdx +2 -2
- package/site/content/docs/reference/cli.md +61 -38
- package/site/content/docs/reference/configuration.mdx +145 -70
- package/site/content/docs/reference/environment-variables.mdx +36 -28
- package/site/content/docs/reference/errors.mdx +28 -29
- package/site/content/docs/reference/fx.mdx +11 -4
- package/site/content/docs/reference/i18n.mdx +17 -6
- package/site/content/docs/reference/index.mdx +5 -0
- package/site/content/docs/reference/meta.json +1 -0
- package/site/content/docs/reference/migrating-environments.mdx +158 -0
- package/site/content/docs/reference/plugins.mdx +2 -2
- package/src/auth/gate-auth.test.ts +12 -10
- package/src/cli/ai-setup/ai-setup.test.ts +27 -15
- package/src/cli/ai-setup/apply.ts +111 -15
- package/src/cli/ai-setup/catalog.ts +3 -3
- package/src/cli/ai-setup/index.ts +1 -1
- package/src/cli/ai-setup/prompts.ts +1 -1
- package/src/cli/ai-setup/recommend.test.ts +1 -1
- package/src/cli/ask-seed.test.ts +3 -3
- package/src/cli/build.ts +1 -1
- package/src/cli/db-auto-push.test.ts +6 -3
- package/src/cli/db-auto-push.ts +7 -5
- package/src/cli/db-seed.ts +7 -8
- package/src/cli/db.test.ts +70 -71
- package/src/cli/db.ts +86 -12
- package/src/cli/dev-controller.test.ts +35 -0
- package/src/cli/dev-controller.ts +298 -0
- package/src/cli/dev-controls.ts +11 -65
- package/src/cli/dev-db-push.test.ts +47 -15
- package/src/cli/dev-schema-sync.test.ts +11 -11
- package/src/cli/dev-schema-sync.ts +10 -10
- package/src/cli/dev-session-lock.test.ts +106 -0
- package/src/cli/dev-session-lock.ts +209 -0
- package/src/cli/dev.test.ts +166 -121
- package/src/cli/dev.ts +138 -192
- package/src/cli/doc-staleness.test.ts +3 -6
- package/src/cli/docker-clean.test.ts +33 -1
- package/src/cli/docker-clean.ts +28 -33
- package/src/cli/docker-cli.test.ts +12 -11
- package/src/cli/docker.ts +95 -20
- package/src/cli/doctor.test.ts +40 -0
- package/src/cli/doctor.ts +55 -15
- package/src/cli/drizzle-env.test.ts +11 -13
- package/src/cli/drizzle-env.ts +18 -11
- package/src/cli/ensure-drizzle-config.ts +11 -14
- package/src/cli/hero-meta.test.ts +15 -15
- package/src/cli/hero-meta.ts +10 -9
- package/src/cli/index.ts +36 -6
- package/src/cli/load-config.images.test.ts +16 -12
- package/src/cli/load-config.source.test.ts +50 -0
- package/src/cli/load-config.ts +34 -28
- package/src/cli/meilisearch-local.ts +1 -2
- package/src/cli/mode.ts +7 -58
- package/src/cli/registry.ts +175 -19
- package/src/cli/resolve-dev-sql-env.test.ts +8 -16
- package/src/cli/resolve-dev-sql-env.ts +13 -15
- package/src/cli/safe-defaults.test.ts +28 -10
- package/src/cli/schema.ts +65 -16
- package/src/cli/test.ts +58 -0
- package/src/cli/tui/App.tsx +252 -0
- package/src/cli/tui/DevLive.tsx +116 -0
- package/src/cli/tui/components/ConfirmDialog.tsx +48 -0
- package/src/cli/tui/components/HeaderBar.tsx +67 -0
- package/src/cli/tui/components/LogStream.tsx +47 -0
- package/src/cli/tui/components/PanelCard.tsx +39 -0
- package/src/cli/tui/components/SelectRow.tsx +37 -0
- package/src/cli/tui/components/SlashPalette.tsx +172 -0
- package/src/cli/tui/components/Spinner.tsx +32 -0
- package/src/cli/tui/components/StatusBar.tsx +51 -0
- package/src/cli/tui/components/StatusDot.tsx +37 -0
- package/src/cli/tui/components/TabBar.tsx +49 -0
- package/src/cli/tui/components/Wordmark.tsx +43 -0
- package/src/cli/tui/format.ts +38 -0
- package/src/cli/tui/keys.test.ts +18 -0
- package/src/cli/tui/keys.ts +26 -0
- package/src/cli/tui/launch.test.ts +18 -0
- package/src/cli/tui/launch.ts +57 -0
- package/src/cli/tui/panels/Dashboard.tsx +201 -0
- package/src/cli/tui/panels/Database.tsx +198 -0
- package/src/cli/tui/panels/Dev.tsx +257 -0
- package/src/cli/tui/panels/Docker.tsx +357 -0
- package/src/cli/tui/panels/Navigator.test.ts +22 -0
- package/src/cli/tui/panels/Navigator.tsx +213 -0
- package/src/cli/tui/slash-catalog.test.ts +31 -0
- package/src/cli/tui/slash-catalog.ts +184 -0
- package/src/cli/tui/slash-run.ts +54 -0
- package/src/cli/tui/state/ports.ts +95 -0
- package/src/cli/tui/theme.ts +24 -0
- package/src/cli/vault-cmd.test.ts +402 -0
- package/src/cli/vault-cmd.ts +727 -69
- package/src/cli/vault-secure-input.test.ts +56 -0
- package/src/cli/vault-secure-input.ts +124 -0
- package/src/compiler/effects-infer.ts +7 -0
- package/src/compiler/fixtures/skyport/oke.config.ts +3 -3
- package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +1 -1
- package/src/compiler/fixtures/skyport.expected.json +1 -1
- package/src/compiler-entry.ts +7 -0
- package/src/config/define-config.test.ts +111 -47
- package/src/config/driver-defaults.test.ts +16 -23
- package/src/config/driver-defaults.ts +33 -63
- package/src/config/index.ts +169 -144
- package/src/config/resolve-driver.test.ts +14 -19
- package/src/console/server/app.ts +4 -2
- package/src/console/server/console.test.ts +58 -1
- package/src/console/server/flows.ts +22 -2
- package/src/console/server/index.ts +3 -0
- package/src/console/server/operator-db.test.ts +55 -13
- package/src/console/server/operator-db.ts +305 -263
- package/src/console/server/serve.ts +16 -7
- package/src/console/server/state.ts +31 -9
- package/src/console/server/vault.test.ts +120 -5
- package/src/console/server/vault.ts +208 -12
- package/src/console/ui/dist/assets/{index-D-9KFce5.js → index--Y0Uaz-m.js} +2 -2
- package/src/console/ui/dist/assets/panel-vault-BkZdJPX8.js +1 -0
- package/src/console/ui/dist/assets/style-C3L349iZ.css +3 -0
- package/src/console/ui/dist/index.html +2 -2
- package/src/console/ui/shell/client.ts +16 -0
- package/src/console/ui/shell/panels/vault/VaultPanel.tsx +54 -1
- package/src/console/ui/vault/VaultA11yView.tsx +33 -1
- package/src/console/ui/vault/backend.test.ts +75 -0
- package/src/console/ui/vault/backend.ts +117 -0
- package/src/console/ui/vault/fixture.ts +21 -2
- package/src/console/ui/vault/index.ts +11 -1
- package/src/console/ui/vault/types.ts +26 -0
- package/src/docker/compose.ts +164 -61
- package/src/docker/derive.ts +106 -12
- package/src/docker/docker.test.ts +156 -110
- package/src/docker/helpers.ts +98 -0
- package/src/docker/images-config.test.ts +3 -8
- package/src/docker/index.ts +30 -5
- package/src/docker/recipes/index.ts +0 -3
- package/src/docker/recipes/llama-cpp.ts +4 -4
- package/src/docker/recipes/pgdog.ts +9 -2
- package/src/docker/resources.test.ts +71 -0
- package/src/docker/resources.ts +176 -0
- package/src/docker/stack-id.test.ts +11 -4
- package/src/docker/stack-id.ts +42 -32
- package/src/docker/stack.integration.test.ts +4 -15
- package/src/docker/types.ts +35 -3
- package/src/drivers/ai-ollama.integration.test.ts +2 -2
- package/src/drivers/conformance.test.ts +11 -12
- package/src/drivers/drizzle-dialect.test.ts +0 -4
- package/src/drivers/drizzle-dialect.ts +7 -13
- package/src/drivers/index.ts +24 -7
- package/src/drivers/journal-postgres.test.ts +3 -1
- package/src/drivers/journal-postgres.ts +4 -2
- package/src/drivers/meilisearch.ts +1 -1
- package/src/drivers/pglite.ts +28 -5
- package/src/drivers/types.ts +9 -11
- package/src/drivers/vault-aws-secrets-manager.test.ts +159 -0
- package/src/drivers/vault-aws-secrets-manager.ts +333 -0
- package/src/drivers/vault-builtin.test.ts +110 -0
- package/src/drivers/vault-builtin.ts +184 -0
- package/src/drivers/vault-driver-removal.test.ts +13 -0
- package/src/drivers/vault-managed.test.ts +95 -0
- package/src/drivers/vault-managed.ts +101 -25
- package/src/drivers/vault-types.ts +25 -5
- package/src/elements/ai/runtime.ts +1 -1
- package/src/elements/clock/chaos.test.ts +4 -1
- package/src/elements/clock/durable.ts +57 -4
- package/src/elements/clock.test.ts +13 -14
- package/src/elements/gate/constants.ts +18 -0
- package/src/elements/gate.test.ts +1 -1
- package/src/elements/gate.ts +2 -2
- package/src/elements/store/domain-ddl.test.ts +5 -12
- package/src/elements/store/emit-drizzle.ts +44 -19
- package/src/elements/store/index-boot.test.ts +15 -116
- package/src/elements/store/load-plugin-tables.ts +1 -1
- package/src/elements/store/resource-list-docs.fixture.ts +12 -6
- package/src/elements/store/resource-list-docs.test.ts +64 -44
- package/src/elements/store/resource.test.ts +17 -10
- package/src/elements/store/runtime.ts +9 -12
- package/src/elements/store/schema-decl.test.ts +16 -32
- package/src/elements/store/seed.test.ts +1 -2
- package/src/elements/store/seed.ts +7 -8
- package/src/elements/store/sql-condition.test.ts +2 -2
- package/src/elements/store/sql-session.test.ts +8 -10
- package/src/elements/store/sql-session.ts +10 -2
- package/src/elements/store.test.ts +13 -13
- package/src/elements/vault/audit.ts +233 -0
- package/src/elements/vault/boot-chain.test.ts +78 -0
- package/src/elements/vault/boot-chain.ts +43 -39
- package/src/elements/vault/builtin-adapter.test.ts +479 -0
- package/src/elements/vault/builtin-adapter.ts +1184 -0
- package/src/elements/vault/crypto.test.ts +237 -0
- package/src/elements/vault/crypto.ts +431 -0
- package/src/elements/vault/declare.ts +164 -3
- package/src/elements/vault/errors.ts +56 -0
- package/src/elements/vault/kms-unseal.test.ts +76 -0
- package/src/elements/vault/kms-unseal.ts +181 -0
- package/src/elements/vault/path.ts +90 -0
- package/src/elements/vault/resilience.test.ts +102 -0
- package/src/elements/vault/resilience.ts +107 -0
- package/src/elements/vault/runtime.ts +36 -4
- package/src/elements/vault/security-checklist.test.ts +130 -0
- package/src/elements/vault/storage.ts +448 -0
- package/src/elements/vault/types.ts +288 -0
- package/src/elements/vault/unseal.ts +150 -0
- package/src/elements/vault.test.ts +72 -4
- package/src/elements/vault.ts +91 -2
- package/src/full.ts +248 -0
- package/src/http.ts +41 -0
- package/src/i18n/interpolate-simple.ts +37 -0
- package/src/i18n/messages.ts +51 -4
- package/src/i18n-entry.ts +7 -0
- package/src/index.ts +35 -145
- package/src/journal-entry.ts +20 -0
- package/src/kernel/adopt-barrel-fresh.test.ts +25 -10
- package/src/kernel/app.ts +40 -42
- package/src/kernel/auto-registry.test.ts +2 -2
- package/src/kernel/boot-bind/ai.test.ts +5 -5
- package/src/kernel/boot-bind/ai.ts +2 -1
- package/src/kernel/boot-bind/channel.test.ts +6 -6
- package/src/kernel/boot-bind/channel.ts +3 -10
- package/src/kernel/boot-bind/clock.ts +2 -2
- package/src/kernel/boot-bind/gate.ts +5 -9
- package/src/kernel/boot-bind/honor-config.test.ts +42 -42
- package/src/kernel/boot-bind/journal.ts +2 -2
- package/src/kernel/boot-bind/store.test.ts +43 -44
- package/src/kernel/boot-bind/store.ts +16 -41
- package/src/kernel/boot-bind/vault.ts +16 -1
- package/src/kernel/boot.test.ts +12 -4
- package/src/kernel/boot.ts +31 -19
- package/src/kernel/budget-entry.ts +1 -1
- package/src/kernel/compensate.test.ts +288 -1
- package/src/kernel/compensate.ts +126 -0
- package/src/kernel/concurrency.test.ts +10 -9
- package/src/kernel/effects-stamping.test.ts +27 -2
- package/src/kernel/element-registries.ts +5 -3
- package/src/kernel/flow.ts +4 -2
- package/src/kernel/fx.test.ts +119 -3
- package/src/kernel/fx.ts +215 -40
- package/src/kernel/index.ts +14 -0
- package/src/kernel/journal.ts +130 -7
- package/src/kernel/pipeline.test.ts +20 -6
- package/src/kernel/redacted.test.ts +35 -0
- package/src/kernel/redacted.ts +24 -2
- package/src/kernel/router/create-default.ts +16 -0
- package/src/kernel/router/create-edge.ts +18 -0
- package/src/kernel/router/create.ts +23 -0
- package/src/kernel/router/index.ts +23 -0
- package/src/kernel/router/linear.ts +88 -0
- package/src/kernel/router/regexp.ts +188 -0
- package/src/kernel/router/smart.ts +120 -0
- package/src/kernel/router/trie.ts +108 -0
- package/src/kernel/router/types.ts +56 -0
- package/src/kernel/router.ts +18 -532
- package/src/kernel-entry.ts +43 -0
- package/src/manifest/fixtures/skyport.excerpt.json +1 -1
- package/src/manifest/fixtures/skyport.manifest.json +1 -1
- package/src/plugins/config-source.test.ts +7 -7
- package/src/release/build-lib.ts +58 -0
- package/src/release/exports.test.ts +4 -2
- package/src/release/exports.ts +30 -4
- package/src/release/measure.ts +101 -22
- package/src/runs/duckdb.ts +7 -2
- package/src/shared/optional-peer.ts +41 -0
- package/src/term.test.ts +3 -44
- package/src/test/create-test-app.ts +12 -0
- package/src/test/provisions.integration.test.ts +1 -1
- package/src/test/reset-element-registries.ts +2 -0
- package/src/upgrade/codemods.test.ts +7 -20
- package/src/upgrade/codemods.ts +93 -60
- package/site/content/docs/recipes/openbao.mdx +0 -141
- package/src/cli/ask-dev-mode.ts +0 -40
- package/src/cli/dev-mode.test.ts +0 -45
- package/src/cli/dev-mode.ts +0 -78
- package/src/cli/openbao-bootstrap.test.ts +0 -150
- package/src/cli/openbao-bootstrap.ts +0 -321
- package/src/cli/openbao-restart.integration.test.ts +0 -145
- package/src/console/ui/dist/assets/panel-vault-Za1GyJfM.js +0 -1
- package/src/console/ui/dist/assets/style-C8MxEWPd.css +0 -3
- package/src/docker/recipes/openbao.ts +0 -47
- package/src/drivers/libsql.ts +0 -179
- package/src/drivers/sqlite.ts +0 -44
- package/src/drivers/vault-openbao.test.ts +0 -97
- package/src/drivers/vault-openbao.ts +0 -143
|
@@ -31,7 +31,7 @@ sticky network id to preserve. Scale with `replicas: N`.
|
|
|
31
31
|
<Step>
|
|
32
32
|
### Share backing services
|
|
33
33
|
|
|
34
|
-
Pin
|
|
34
|
+
Pin `dev`/`prod` drivers to shared stores:
|
|
35
35
|
|
|
36
36
|
| Concern | Driver | Shared backend |
|
|
37
37
|
| ----------------------------- | ---------------------------- | ------------------------------------------------- |
|
|
@@ -59,7 +59,7 @@ Wire `readinessProbe` to `/_/ready` and `livenessProbe` to `/health`.
|
|
|
59
59
|
Bun.SQL defaults to **10** connections per process — fine for one pod.
|
|
60
60
|
Scale the Deployment and `N × pool` can exceed Postgres `max_connections`.
|
|
61
61
|
|
|
62
|
-
When both `store.sql` and `pgdog` are pinned (
|
|
62
|
+
When both `store.sql` and `pgdog` are pinned (`dev`/`prod` default in templates),
|
|
63
63
|
`DATABASE_URL` points at PgDog on port `6432` — transaction pooling, wire-protocol
|
|
64
64
|
transparent to Bun.SQL / Drizzle. Point the cluster Service at that pooler.
|
|
65
65
|
|
|
@@ -137,12 +137,12 @@ so traffic does not hit a pod mid-takeover.
|
|
|
137
137
|
|
|
138
138
|
`drivers.store.kv` must be `redis` with `REDIS_URL` set. A missing URL now
|
|
139
139
|
**fails boot** (no silent memory fallback). Declaring `memory` for local is fine;
|
|
140
|
-
|
|
140
|
+
`dev`/`prod` should stay on redis.
|
|
141
141
|
|
|
142
142
|
</Accordion>
|
|
143
143
|
<Accordion title="Files uploaded on pod A are missing on pod B">
|
|
144
144
|
|
|
145
|
-
`drivers.store.files: "fs"` is single-host. Switch
|
|
145
|
+
`drivers.store.files: "fs"` is single-host. Switch `dev`/`prod` to `s3`
|
|
146
146
|
(create-oke templates already do).
|
|
147
147
|
|
|
148
148
|
</Accordion>
|
|
@@ -132,7 +132,7 @@ Use `keyBy: "ip"` on public unauthenticated AI edges. Cost caps stay on prompt/a
|
|
|
132
132
|
|
|
133
133
|
## Secrets stay out of prompts
|
|
134
134
|
|
|
135
|
-
`Redacted` values (e.g. from `fx.vault`) stringify to `[redacted]` in provider-facing messages — the same placeholder as logs. Never cleartext to a model.
|
|
135
|
+
`Redacted` values (e.g. from `fx.vault.get`) stringify to `[redacted]` in provider-facing messages — the same placeholder as logs. Never cleartext to a model.
|
|
136
136
|
|
|
137
137
|
## Streaming
|
|
138
138
|
|
|
@@ -176,8 +176,8 @@ oke ai setup --provider ollama --yes
|
|
|
176
176
|
```
|
|
177
177
|
|
|
178
178
|
Writes `drivers.ai`, commented AI overrides in `.env.local` (so they do not
|
|
179
|
-
shadow `docker/.env.docker`), and `src/core
|
|
180
|
-
lines only for a host-managed AI endpoint.
|
|
179
|
+
shadow `docker/.env.docker`), and AI models into `src/core.ts`. Uncomment
|
|
180
|
+
`.env.local` lines only for a host-managed AI endpoint.
|
|
181
181
|
|
|
182
182
|
**Recommended** defaults to llama.cpp (lightest local footprint). Customize
|
|
183
183
|
offers llama.cpp · Ollama · vLLM · SGLang · cloud providers.
|
|
@@ -209,7 +209,7 @@ import { defineConfig } from "okengine/config";
|
|
|
209
209
|
|
|
210
210
|
export default defineConfig({
|
|
211
211
|
drivers: {
|
|
212
|
-
ai: {
|
|
212
|
+
ai: { dev: "openai-compatible", test: "mock", prod: "anthropic" },
|
|
213
213
|
},
|
|
214
214
|
images: {
|
|
215
215
|
ai: "ghcr.io/ggml-org/llama.cpp:server-b10290", // ≥ b8146; never :latest
|
|
@@ -217,10 +217,10 @@ export default defineConfig({
|
|
|
217
217
|
});
|
|
218
218
|
```
|
|
219
219
|
|
|
220
|
-
```typescript title="src/core
|
|
220
|
+
```typescript title="src/core.ts"
|
|
221
221
|
export const smart = ai.model("smart", {
|
|
222
222
|
provider: "openai-compatible",
|
|
223
|
-
model: process.env.OKE_AI_MODEL ?? "
|
|
223
|
+
model: process.env.OKE_AI_MODEL ?? "granite3.3:2b", // Docker Hub ai/ id
|
|
224
224
|
});
|
|
225
225
|
```
|
|
226
226
|
|
|
@@ -270,7 +270,7 @@ The one you declared. There is no fallback or guess — if `prod` has no AI driv
|
|
|
270
270
|
</Accordion>
|
|
271
271
|
<Accordion title="oke boot: ollama driver needs OKE_AI_URL">
|
|
272
272
|
|
|
273
|
-
Docker mode expects `OKE_AI_URL` from `docker/.env.docker` (written when `images.ai` is pinned). Keep `OKE_AI_*` in `.env.local` commented — an active `OKE_AI_URL` there wins and shadows the compose URL. Locally, the Ollama default is `http://127.0.0.1:11434`. If asks fail with `OllamaUnavailableError`, confirm the server answers at that URL and that the model is listed under `GET /api/tags` (boot pulls via `POST /api/pull` against the container — do not rely on a host `ollama pull`, which may talk to a different install). Prefer `oke ai setup` to wire driver, commented env overrides, and `src/core
|
|
273
|
+
Docker mode expects `OKE_AI_URL` from `docker/.env.docker` (written when `images.ai` is pinned). Keep `OKE_AI_*` in `.env.local` commented — an active `OKE_AI_URL` there wins and shadows the compose URL. Locally, the Ollama default is `http://127.0.0.1:11434`. If asks fail with `OllamaUnavailableError`, confirm the server answers at that URL and that the model is listed under `GET /api/tags` (boot pulls via `POST /api/pull` against the container — do not rely on a host `ollama pull`, which may talk to a different install). Prefer `oke ai setup` to wire driver, commented env overrides, and AI models in `src/core.ts` in one pass.
|
|
274
274
|
|
|
275
275
|
</Accordion>
|
|
276
276
|
<Accordion title="A prompt edit made answers worse — how do I catch that pre-merge?">
|
|
@@ -54,7 +54,9 @@ do: async (input, fx) => {
|
|
|
54
54
|
<Step>
|
|
55
55
|
### Read it in development
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
Under `oke test` the `console` driver captures mail into an inbox instead of sending; under `oke
|
|
58
|
+
dev` Compose runs **Mailpit**, a real SMTP catcher with a web UI — so you see the exact rendered
|
|
59
|
+
message without ever touching a real mailbox.
|
|
58
60
|
|
|
59
61
|
</Step>
|
|
60
62
|
|
|
@@ -199,20 +201,20 @@ push drivers are passed via `BootOptions.channel.drivers` when you need them.
|
|
|
199
201
|
```typescript title="oke.config.ts"
|
|
200
202
|
drivers: {
|
|
201
203
|
channel: {
|
|
202
|
-
email: {
|
|
204
|
+
email: { dev: "smtp", test: "console", prod: "smtp" },
|
|
203
205
|
// optional — omit until you need SMS at boot
|
|
204
|
-
sms: {
|
|
206
|
+
sms: { test: "console", prod: "taqnyat" },
|
|
205
207
|
},
|
|
206
208
|
},
|
|
207
209
|
images: {
|
|
208
|
-
|
|
210
|
+
channel: { email: "axllent/mailpit:v1.22.3" },
|
|
209
211
|
},
|
|
210
212
|
```
|
|
211
213
|
|
|
212
214
|
| Driver | Medium | Behavior |
|
|
213
215
|
| -------------- | -------- | ------------------------------------------------------------------- |
|
|
214
|
-
| `console` | any | Captures into a readable inbox —
|
|
215
|
-
| `smtp` | email | Real SMTP — Mailpit
|
|
216
|
+
| `console` | any | Captures into a readable inbox — tests |
|
|
217
|
+
| `smtp` | email | Real SMTP — Mailpit under `oke dev`, your provider in prod |
|
|
216
218
|
| `resend` | email | Resend HTTP API (`RESEND_API_KEY`) |
|
|
217
219
|
| `sndr` | email | SNDR HTTP API (`SNDR_API_KEY`) |
|
|
218
220
|
| `taqnyat-mail` | email | Taqnyat Mail API (`TAQNYAT_MAIL_TOKEN` + `TAQNYAT_CAMPAIGN`) |
|
|
@@ -258,7 +260,7 @@ openWebPushChannel({
|
|
|
258
260
|
<Accordions>
|
|
259
261
|
<Accordion title="I sent an email in dev but nothing arrived">
|
|
260
262
|
|
|
261
|
-
Nothing _should_ arrive — the `console` driver captures mail instead of sending. Read the dev inbox, or run `oke dev
|
|
263
|
+
Nothing _should_ arrive — the `console` driver captures mail instead of sending. Read the dev inbox, or run `oke dev` and open Mailpit's web UI to see the rendered message.
|
|
262
264
|
|
|
263
265
|
</Accordion>
|
|
264
266
|
<Accordion title="A user says they stopped receiving messages">
|
|
@@ -124,7 +124,7 @@ reclaimed after its lease expires and continues at the next unfinished step.
|
|
|
124
124
|
|
|
125
125
|
**Leader election needs a shared store.** The default in-memory CronStore is per process. Multi-replica
|
|
126
126
|
exclusivity holds when every instance opens the same durable CronStore — `postgres` (SKIP LOCKED +
|
|
127
|
-
lease reclaim,
|
|
127
|
+
lease reclaim, `dev`/`prod` default) or `file` (shared volume, single-host).
|
|
128
128
|
|
|
129
129
|
**Catch-up example:** an hourly clock down for five hours reports `missedRuns: 5` and
|
|
130
130
|
`catchUp: "one"`, then a single tick runs the handler once.
|
|
@@ -141,16 +141,16 @@ Detection attaches `dstAmbiguity` on the cron row and the Console shows it. `oke
|
|
|
141
141
|
|
|
142
142
|
```typescript title="oke.config.ts"
|
|
143
143
|
drivers: {
|
|
144
|
-
clock: {
|
|
144
|
+
clock: { dev: "postgres", test: "frozen", prod: "postgres" },
|
|
145
145
|
},
|
|
146
146
|
```
|
|
147
147
|
|
|
148
|
-
| Driver | Behavior
|
|
149
|
-
| ---------- |
|
|
150
|
-
| `memory` | In-process CronStore + timers — single process; lost on exit
|
|
151
|
-
| `postgres` | Postgres CronStore (`oke_crons`) — multi-host leader election via SKIP LOCKED + lease reclaim
|
|
152
|
-
| `file` | On-disk CronStore (`.oke/crons.json`) — multi-process on a shared volume (
|
|
153
|
-
| `frozen` | Deterministic test clock — time advances only when the test says so
|
|
148
|
+
| Driver | Behavior |
|
|
149
|
+
| ---------- | --------------------------------------------------------------------------------------------- |
|
|
150
|
+
| `memory` | In-process CronStore + timers — single process; lost on exit |
|
|
151
|
+
| `postgres` | Postgres CronStore (`oke_crons`) — multi-host leader election via SKIP LOCKED + lease reclaim |
|
|
152
|
+
| `file` | On-disk CronStore (`.oke/crons.json`) — multi-process on a shared volume (single-host) |
|
|
153
|
+
| `frozen` | Deterministic test clock — time advances only when the test says so |
|
|
154
154
|
|
|
155
155
|
`frozen` is why the no-`Date.now()` rule pays off: tests inject time travel through `fx.clock` and every flow obeys it automatically.
|
|
156
156
|
|
|
@@ -71,15 +71,15 @@ curl -X POST localhost:6530/orders -d '{"sku":"SKU-1","qty":2}' -H 'content-type
|
|
|
71
71
|
|
|
72
72
|
## Anatomy of a Flow
|
|
73
73
|
|
|
74
|
-
| Part | Role
|
|
75
|
-
| ------------ |
|
|
76
|
-
| trigger | What starts the flow — `http`, a signal, `every`, a row change
|
|
77
|
-
| `in` | Input contract — validated before `do` runs; bad input is a 422
|
|
78
|
-
| `out` | Output contract — the return value is checked against it
|
|
79
|
-
| `errors` | Typed failures — **returned** with `fx.fail`, never thrown
|
|
80
|
-
| `retry` | Optional whole-`do` backoff on thrown errors (same journal)
|
|
81
|
-
| `do` | The work — every read, write, emit, and call goes through `fx`
|
|
82
|
-
| `compensate` | Durable only —
|
|
74
|
+
| Part | Role |
|
|
75
|
+
| ------------ | -------------------------------------------------------------------------- |
|
|
76
|
+
| trigger | What starts the flow — `http`, a signal, `every`, a row change |
|
|
77
|
+
| `in` | Input contract — validated before `do` runs; bad input is a 422 |
|
|
78
|
+
| `out` | Output contract — the return value is checked against it |
|
|
79
|
+
| `errors` | Typed failures — **returned** with `fx.fail`, never thrown |
|
|
80
|
+
| `retry` | Optional whole-`do` backoff on thrown errors (same journal) |
|
|
81
|
+
| `do` | The work — every read, write, emit, and call goes through `fx` |
|
|
82
|
+
| `compensate` | Durable only — optional hook after auto per-step `{ undo }` (same journal) |
|
|
83
83
|
|
|
84
84
|
Failures are values, not exceptions:
|
|
85
85
|
|
|
@@ -181,22 +181,22 @@ const order = await fx.call(getOrder, { id: orderId }); // from any other flow
|
|
|
181
181
|
|
|
182
182
|
Everything a flow may touch, on one object:
|
|
183
183
|
|
|
184
|
-
| Surface | Effect recorded | What it does
|
|
185
|
-
| ------------------------------------------- | --------------- |
|
|
186
|
-
| `fx.store(db).select/insert/…` | read / write | SQL, KV, files, index sessions
|
|
187
|
-
| `fx.emit(signal, payload)` | emit | Publish a signal (transactional with writes)
|
|
188
|
-
| `fx.send(template, opts)` | send | Reach a human (email · SMS · …)
|
|
189
|
-
| `fx.ask(prompt, input)` | ask | Call a versioned AI prompt
|
|
190
|
-
| `fx.run(agent, input)` | ask | Run a bounded agent
|
|
191
|
-
| `fx.call(flow, input)` | call | Invoke another flow
|
|
192
|
-
| `fx.vault(contract)`
|
|
193
|
-
| `fx.clock.now()` / `.sleep(…)` | — | Injected time / durable sleep
|
|
194
|
-
| `fx.cache.get/set` | — | Shared cache with effect-aware invalidation
|
|
195
|
-
| `fx.step(name, fn)`
|
|
196
|
-
| `fx.all` / `fx.race` / `fx.retry` | — | Structured concurrency + backoff retry
|
|
197
|
-
| `fx.signal` | — | Ambient `AbortSignal` for the current branch
|
|
198
|
-
| `fx.id()` · `fx.log` · `fx.t` · `fx.locale` | — | UUIDs, redacting logger, ICU i18n
|
|
199
|
-
| `fx.auth` · `fx.operator` · `fx.tenant` | — | Who is calling (user / operator / tenant)
|
|
184
|
+
| Surface | Effect recorded | What it does |
|
|
185
|
+
| ------------------------------------------- | --------------- | -------------------------------------------------- |
|
|
186
|
+
| `fx.store(db).select/insert/…` | read / write | SQL, KV, files, index sessions |
|
|
187
|
+
| `fx.emit(signal, payload)` | emit | Publish a signal (transactional with writes) |
|
|
188
|
+
| `fx.send(template, opts)` | send | Reach a human (email · SMS · …) |
|
|
189
|
+
| `fx.ask(prompt, input)` | ask | Call a versioned AI prompt |
|
|
190
|
+
| `fx.run(agent, input)` | ask | Run a bounded agent |
|
|
191
|
+
| `fx.call(flow, input)` | call | Invoke another flow |
|
|
192
|
+
| `fx.vault.get(contract)` | secret | Read a secret (`Redacted`; logs masked) |
|
|
193
|
+
| `fx.clock.now()` / `.sleep(…)` | — | Injected time / durable sleep |
|
|
194
|
+
| `fx.cache.get/set` | — | Shared cache with effect-aware invalidation |
|
|
195
|
+
| `fx.step(name, fn, { undo? })` | — | Named durable step — optional LIFO undo on failure |
|
|
196
|
+
| `fx.all` / `fx.race` / `fx.retry` | — | Structured concurrency + backoff retry |
|
|
197
|
+
| `fx.signal` | — | Ambient `AbortSignal` for the current branch |
|
|
198
|
+
| `fx.id()` · `fx.log` · `fx.t` · `fx.locale` | — | UUIDs, redacting logger, ICU i18n |
|
|
199
|
+
| `fx.auth` · `fx.operator` · `fx.tenant` | — | Who is calling (user / operator / tenant) |
|
|
200
200
|
|
|
201
201
|
<Callout title="Why this strictness pays off">
|
|
202
202
|
Effects are inferred from `fx` usage, so the Manifest knows exactly which flows read `orders` or
|
|
@@ -216,28 +216,27 @@ export const chargeOrder = flow("payments.chargeOrder", {
|
|
|
216
216
|
in: OrderRef,
|
|
217
217
|
out: z.boolean(),
|
|
218
218
|
do: async ({ orderId }, fx) => {
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
);
|
|
219
|
+
const key = await fx.vault.get(stripeKey);
|
|
220
|
+
const intent = await fx.step("create-intent", () => stripe(key.reveal()).create(orderId));
|
|
222
221
|
|
|
223
222
|
await fx.clock.sleep("verify-window", "2m"); // journals wakeAt in a durable flow
|
|
224
223
|
|
|
225
|
-
return fx.step("confirm", () => stripe(
|
|
224
|
+
return fx.step("confirm", () => stripe(key.reveal()).confirm(intent));
|
|
226
225
|
},
|
|
227
226
|
});
|
|
228
227
|
```
|
|
229
228
|
|
|
230
229
|
**Consequence:** completed steps replay from the journal — on resume, `create-intent` does not re-run, so the card is not charged twice.
|
|
231
230
|
|
|
232
|
-
**Crash recovery needs a shared journal.** Set `drivers.journal: "postgres"` (the
|
|
231
|
+
**Crash recovery needs a shared journal.** Set `drivers.journal: "postgres"` (the `dev`/`prod` template default) and every run persists in `oke_journal_runs` under a per-run lease: boot scans for orphaned `running` / `compensating` / due `sleeping` runs and resumes them, and the lease lets exactly one instance execute a run at a time. The default `memory` journal is per process — replay holds for that process only.
|
|
233
232
|
|
|
234
233
|
Replay is **at-least-once** for a step whose body never persisted (crash mid-step, lease overrun): that step re-runs on reclaim, so keep step bodies short or internally idempotent. A client transport retry starts a _new_ run with a fresh `runId` — request-level idempotency is an application concern, not the journal's.
|
|
235
234
|
|
|
236
235
|
For flaky sub-steps, wrap the work in `fx.retry` **inside** `fx.step` so a completed charge is never retried on resume. Coarse whole-body retry is also available as `flow(name, { retry: { retries, delay, backoff, jitter } })` and reuses the same journal session.
|
|
237
236
|
|
|
238
|
-
After a **terminal** failure (`fx.fail` or exhausted retries / uncaught throw),
|
|
239
|
-
|
|
240
|
-
|
|
237
|
+
After a **terminal** failure (`fx.fail` or exhausted retries / uncaught throw), declare
|
|
238
|
+
undo next to each step. Only **persisted** steps undo (LIFO) — if `charge` throws before
|
|
239
|
+
it journals, `undo:charge` does not run:
|
|
241
240
|
|
|
242
241
|
```typescript
|
|
243
242
|
export const chargeOrder = flow("payments.chargeOrder", {
|
|
@@ -245,23 +244,23 @@ export const chargeOrder = flow("payments.chargeOrder", {
|
|
|
245
244
|
in: OrderRef,
|
|
246
245
|
out: z.boolean(),
|
|
247
246
|
do: async ({ orderId }, fx) => {
|
|
248
|
-
await fx.step("reserve", () => reserve(orderId)
|
|
249
|
-
|
|
247
|
+
await fx.step("reserve", () => reserve(orderId), {
|
|
248
|
+
undo: () => release(orderId),
|
|
249
|
+
});
|
|
250
|
+
await fx.step("charge", () => charge(orderId), {
|
|
251
|
+
undo: (intent) => refund(intent), // journaled value — resume-safe
|
|
252
|
+
});
|
|
250
253
|
return true;
|
|
251
254
|
},
|
|
252
|
-
compensate: async ({ input, completedSteps }, fx) => {
|
|
253
|
-
if (completedSteps.includes("charge")) {
|
|
254
|
-
await fx.step("undo:charge", () => refund(input.orderId));
|
|
255
|
-
}
|
|
256
|
-
if (completedSteps.includes("reserve")) {
|
|
257
|
-
await fx.step("undo:reserve", () => release(input.orderId));
|
|
258
|
-
}
|
|
259
|
-
},
|
|
260
255
|
});
|
|
261
256
|
```
|
|
262
257
|
|
|
263
|
-
|
|
264
|
-
`
|
|
258
|
+
The runtime journals undos as `undo:<name>` under status `compensating`, then commits
|
|
259
|
+
`failed`. Orphan reclaim resumes that phase without re-entering forward work.
|
|
260
|
+
`flow.compensate` remains an optional hook after auto undos for cross-cutting cleanup.
|
|
261
|
+
`JournalSuspend` (sleep) is park, not failure — undo does not run.
|
|
262
|
+
Use `fx.using` for same-attempt cleanup; `{ undo }` for durable external effects — not both
|
|
263
|
+
for the same release.
|
|
265
264
|
|
|
266
265
|
## Composition is just calls
|
|
267
266
|
|
|
@@ -298,9 +297,9 @@ Use `fx.all` (or `fx.race`) with thunks. The first rejection aborts siblings thr
|
|
|
298
297
|
Put `fx.retry` **inside** `fx.step`, not around it. Completed steps replay from the journal and never re-run. `flow(name, { retry })` is for coarse whole-body retries on the same session.
|
|
299
298
|
|
|
300
299
|
</Accordion>
|
|
301
|
-
<Accordion title="When does compensate run?">
|
|
300
|
+
<Accordion title="When does undo / compensate run?">
|
|
302
301
|
|
|
303
|
-
Only after terminal failure for that journal attempt — not on sleep, not mid-retry, not on success.
|
|
302
|
+
Only after terminal failure for that journal attempt — not on sleep, not mid-retry, not on success. Per-step `{ undo }` runs first (LIFO, only for persisted steps), then optional `flow.compensate`. The run stays `compensating` until undos finish so a crash mid-undo resumes the undo phase, not forward `do`. Undo bodies must be idempotent. Irreversible effects (`send` / `ask`) need developer-declared undos or an explicit no-op.
|
|
304
303
|
|
|
305
304
|
</Accordion>
|
|
306
305
|
<Accordion title="How do I share logic between flows — a private function?">
|
|
@@ -48,7 +48,7 @@ import { member, canBook, fair } from "./gates";
|
|
|
48
48
|
|
|
49
49
|
export const app = oke({
|
|
50
50
|
name: "shop",
|
|
51
|
-
env: "
|
|
51
|
+
env: "dev",
|
|
52
52
|
gate: { policies: [member, canBook, fair] },
|
|
53
53
|
});
|
|
54
54
|
```
|
|
@@ -166,7 +166,7 @@ Fine-grained authorization (`gate.scope`, roles) stays opt-in — attach it only
|
|
|
166
166
|
|
|
167
167
|
All five run as atomic Lua on the kv driver — correct under concurrency, identical on the memory
|
|
168
168
|
driver in tests. Boot opens a dedicated `oke:gates` namespace on **`drivers.store.kv`**
|
|
169
|
-
(`memory`
|
|
169
|
+
(`memory` in test · `redis` in `dev`/`prod`) so multi-instance rate limits share counters.
|
|
170
170
|
|
|
171
171
|
## Auth posture at boot
|
|
172
172
|
|
|
@@ -183,7 +183,7 @@ on(http.post("/bookings").gate(member, canBook), flow("bookings.create", {/* …
|
|
|
183
183
|
posture but does **not** authenticate.
|
|
184
184
|
|
|
185
185
|
`gate: { unguardedHttp: "allow" }` skips the audit **only** when `env === "test"`.
|
|
186
|
-
In `
|
|
186
|
+
In `dev` / `prod` migrate with per-trigger `gate.public`.
|
|
187
187
|
|
|
188
188
|
## Built-in auth (`gate.auth`)
|
|
189
189
|
|
|
@@ -196,7 +196,7 @@ import { member, canBook } from "./gates";
|
|
|
196
196
|
|
|
197
197
|
export const app = oke({
|
|
198
198
|
name: "shop",
|
|
199
|
-
env: "
|
|
199
|
+
env: "dev",
|
|
200
200
|
gate: {
|
|
201
201
|
auth: {
|
|
202
202
|
emailAndPassword: { enabled: true },
|
|
@@ -248,7 +248,8 @@ gate: {
|
|
|
248
248
|
}
|
|
249
249
|
```
|
|
250
250
|
|
|
251
|
-
Customize tables, then run `oke schema generate` (
|
|
251
|
+
Customize tables, then run `oke schema generate` (writes `.oke/schema/oke.ts`;
|
|
252
|
+
supports `--check` in CI). Extra sign-in
|
|
252
253
|
shapes: [Plugins](/docs/plugins) (username, magic link, OTP, TOTP, passkeys). Call auth from
|
|
253
254
|
[createClient](/docs/reference/client) — one client, helpers in `okengine/client/auth`.
|
|
254
255
|
|
|
@@ -238,13 +238,13 @@ When a signal disappears from code, reconciliation marks its config row `orphane
|
|
|
238
238
|
|
|
239
239
|
```typescript title="oke.config.ts"
|
|
240
240
|
drivers: {
|
|
241
|
-
signal: {
|
|
241
|
+
signal: { dev: "redis", test: "memory", prod: "redis" },
|
|
242
242
|
},
|
|
243
243
|
```
|
|
244
244
|
|
|
245
245
|
| Driver | Runs as | Boot today |
|
|
246
246
|
| ---------- | ------------------------------------ | --------------------------------------------------------------------------- |
|
|
247
|
-
| `memory` | in-process |
|
|
247
|
+
| `memory` | in-process | Tests — zero infrastructure |
|
|
248
248
|
| `redis` | Redis / Valkey / Dragonfly image pin | Boot-bound — **emit relay** via Streams / pub/sub; consume is process-local |
|
|
249
249
|
| `postgres` | Postgres | Driver exists; boot **fails loud** until a LISTEN/NOTIFY SQL client binds |
|
|
250
250
|
| `nats` | NATS | Driver exists; boot **fails loud** until a production NATS client binds |
|