okengine 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/AGENTS.md +2 -2
  2. package/README.md +98 -71
  3. package/package.json +4 -4
  4. package/site/content/docs/console/clock.mdx +25 -5
  5. package/site/content/docs/console/signals.mdx +13 -4
  6. package/site/content/docs/elements/ai.mdx +29 -2
  7. package/site/content/docs/elements/channel.mdx +90 -20
  8. package/site/content/docs/elements/clock.mdx +56 -24
  9. package/site/content/docs/elements/flow.mdx +2 -0
  10. package/site/content/docs/elements/gate.mdx +8 -1
  11. package/site/content/docs/elements/signal.mdx +173 -29
  12. package/site/content/docs/elements/store.mdx +464 -73
  13. package/site/content/docs/elements/vault.mdx +15 -5
  14. package/site/content/docs/get-started/basic-usage.mdx +13 -6
  15. package/site/content/docs/get-started/installation.mdx +49 -13
  16. package/site/content/docs/get-started/introduction.mdx +1 -1
  17. package/site/content/docs/reference/cli.md +30 -2
  18. package/site/content/docs/reference/configuration.mdx +18 -17
  19. package/site/content/docs/reference/environment-variables.mdx +44 -18
  20. package/site/content/docs/reference/errors.mdx +12 -11
  21. package/site/content/docs/reference/fx.mdx +15 -13
  22. package/site/content/docs/reference/security.md +3 -1
  23. package/src/cli/ai-setup/ai-setup.test.ts +144 -0
  24. package/src/cli/ai-setup/apply.ts +214 -0
  25. package/src/cli/ai-setup/catalog.ts +263 -0
  26. package/src/cli/ai-setup/detect-ollama.ts +166 -0
  27. package/src/cli/ai-setup/index.ts +228 -0
  28. package/src/cli/ai-setup/prompts.ts +649 -0
  29. package/src/cli/ai-setup/recommend.test.ts +100 -0
  30. package/src/cli/ai-setup/recommend.ts +203 -0
  31. package/src/cli/ai.ts +38 -0
  32. package/src/cli/db-auto-push.test.ts +6 -1
  33. package/src/cli/db-auto-push.ts +8 -3
  34. package/src/cli/dev-db-push.test.ts +59 -0
  35. package/src/cli/dev.test.ts +40 -0
  36. package/src/cli/dev.ts +55 -1
  37. package/src/cli/docker-clean.test.ts +179 -0
  38. package/src/cli/docker-clean.ts +406 -0
  39. package/src/cli/docker.ts +16 -4
  40. package/src/cli/ensure-drizzle-config.ts +1 -1
  41. package/src/cli/hero-meta.test.ts +6 -5
  42. package/src/cli/hero-meta.ts +7 -1
  43. package/src/cli/index.ts +5 -0
  44. package/src/cli/load-config.ts +4 -4
  45. package/src/cli/openbao-bootstrap.test.ts +1 -0
  46. package/src/cli/openbao-bootstrap.ts +9 -2
  47. package/src/cli/openbao-restart.integration.test.ts +106 -97
  48. package/src/cli/registry.ts +79 -1
  49. package/src/client/live-gap.test.ts +35 -0
  50. package/src/client/transport.test.ts +21 -0
  51. package/src/client/transport.ts +27 -5
  52. package/src/compiler/fixtures/skyport/oke.config.ts +1 -1
  53. package/src/config/define-config.test.ts +6 -6
  54. package/src/config/index.ts +1 -1
  55. package/src/console/server/console.test.ts +45 -0
  56. package/src/console/server/flows.ts +39 -9
  57. package/src/console/server/i18n.ts +41 -0
  58. package/src/console/server/vault.ts +12 -38
  59. package/src/console/ui/dist/assets/index-ClX0ZCe5.js +10 -0
  60. package/src/console/ui/dist/assets/{panel-access-BGv45snf.js → panel-access-ri1X9Otj.js} +1 -1
  61. package/src/console/ui/dist/assets/{panel-ai-B2S7LEii.js → panel-ai-DLHjRoJk.js} +1 -1
  62. package/src/console/ui/dist/assets/{panel-architecture-D7UJh91v.js → panel-architecture-DI3bQF3J.js} +1 -1
  63. package/src/console/ui/dist/assets/{panel-channels-9T3ybqRu.js → panel-channels-BDYWX29p.js} +1 -1
  64. package/src/console/ui/dist/assets/{panel-clock-Cb1UXGRQ.js → panel-clock-i-JpTjVB.js} +1 -1
  65. package/src/console/ui/dist/assets/{panel-diff-DmYbKWmN.js → panel-diff-CFuWxiXy.js} +1 -1
  66. package/src/console/ui/dist/assets/{panel-flows-PiHwT55z.js → panel-flows-B8TRhcoe.js} +1 -1
  67. package/src/console/ui/dist/assets/{panel-gates-BQGYXvjT.js → panel-gates-CfkBgco7.js} +1 -1
  68. package/src/console/ui/dist/assets/{panel-overview-BBnRO18l.js → panel-overview-DONDzBd2.js} +1 -1
  69. package/src/console/ui/dist/assets/{panel-plugins-D0PsmVw2.js → panel-plugins-DDJwTehL.js} +1 -1
  70. package/src/console/ui/dist/assets/{panel-runs-CWuRDe0r.js → panel-runs-D5zf-D9c.js} +1 -1
  71. package/src/console/ui/dist/assets/{panel-signals-Bbg4ewpP.js → panel-signals-Dsxdu_AR.js} +1 -1
  72. package/src/console/ui/dist/assets/{panel-store-CPCbsDRa.js → panel-store-hy7O8HOs.js} +1 -1
  73. package/src/console/ui/dist/assets/{panel-traces-DVAzuA_S.js → panel-traces-CBiAe5go.js} +1 -1
  74. package/src/console/ui/dist/assets/{panel-vault-D1_MvOmo.js → panel-vault-Za1GyJfM.js} +1 -1
  75. package/src/console/ui/dist/index.html +1 -1
  76. package/src/console/ui/shell/components/ui.tsx +5 -1
  77. package/src/console/ui/shell/setup/Wizard.tsx +26 -5
  78. package/src/docker/cleanup.test.ts +193 -0
  79. package/src/docker/cleanup.ts +355 -0
  80. package/src/docker/docker.test.ts +4 -0
  81. package/src/docker/dockerfile.integration.test.ts +126 -119
  82. package/src/docker/index.ts +19 -0
  83. package/src/docker/recipes/ollama.ts +10 -5
  84. package/src/docker/stack.integration.test.ts +118 -102
  85. package/src/drivers/ai-ollama-tools.integration.test.ts +8 -6
  86. package/src/drivers/ai-ollama.integration.test.ts +3 -19
  87. package/src/drivers/ai-ollama.ts +45 -2
  88. package/src/drivers/ai-providers.test.ts +31 -0
  89. package/src/drivers/channel-fcm.ts +49 -53
  90. package/src/drivers/channel-msegat.ts +61 -0
  91. package/src/drivers/channel-sently-map.ts +57 -0
  92. package/src/drivers/channel-sently.test.ts +99 -0
  93. package/src/drivers/channel-sndr.ts +28 -0
  94. package/src/drivers/channel-taqnyat.ts +57 -0
  95. package/src/drivers/channel-types.ts +79 -2
  96. package/src/drivers/channel-unifonic.ts +26 -43
  97. package/src/drivers/channel-wa-cloud.ts +33 -47
  98. package/src/drivers/channel-webpush.ts +39 -239
  99. package/src/drivers/index.ts +7 -0
  100. package/src/drivers/signal-engine.ts +98 -25
  101. package/src/drivers/signal-nats.ts +4 -4
  102. package/src/drivers/signal-postgres.ts +158 -64
  103. package/src/drivers/signal-redis.ts +4 -4
  104. package/src/drivers/signal-types.ts +61 -2
  105. package/src/drivers/vault-driver-removal.test.ts +5 -1
  106. package/src/elements/channel/costs.test.ts +2 -2
  107. package/src/elements/channel/costs.ts +14 -2
  108. package/src/elements/channel/mime.ts +11 -0
  109. package/src/elements/channel/runtime.ts +94 -0
  110. package/src/elements/channel/sndr-webhooks.test.ts +26 -0
  111. package/src/elements/channel.ts +10 -1
  112. package/src/elements/clock/chaos-child.ts +150 -0
  113. package/src/elements/clock/chaos.test.ts +204 -0
  114. package/src/elements/clock/reconcile.ts +110 -0
  115. package/src/elements/clock/runtime.ts +11 -0
  116. package/src/elements/clock.test.ts +164 -2
  117. package/src/elements/clock.ts +1 -0
  118. package/src/elements/gate/runtime.ts +6 -0
  119. package/src/elements/index.ts +9 -0
  120. package/src/elements/signal/chaos-child.ts +41 -4
  121. package/src/elements/signal/declare.ts +4 -1
  122. package/src/elements/signal/delivery-modes.test.ts +179 -0
  123. package/src/elements/signal/key-ordering.test.ts +306 -0
  124. package/src/elements/signal/lease-reclaim.test.ts +123 -0
  125. package/src/elements/signal/optional-emit.test.ts +76 -0
  126. package/src/elements/signal/order-lifecycle.test.ts +130 -0
  127. package/src/elements/signal/orphan-messages.test.ts +152 -0
  128. package/src/elements/signal/runtime.ts +11 -4
  129. package/src/elements/signal/schema-emit.test.ts +107 -0
  130. package/src/elements/signal.test.ts +89 -5
  131. package/src/elements/store/files-fx.ts +104 -0
  132. package/src/elements/store/files-image.test.ts +251 -0
  133. package/src/elements/store/files-image.ts +494 -0
  134. package/src/elements/store/runtime.ts +60 -0
  135. package/src/elements/store.ts +24 -0
  136. package/src/elements/vault/boot-chain.ts +150 -0
  137. package/src/elements/vault/runtime.ts +8 -0
  138. package/src/kernel/app.ts +3 -1
  139. package/src/kernel/boot-bind/channel.test.ts +68 -3
  140. package/src/kernel/boot-bind/channel.ts +93 -2
  141. package/src/kernel/boot-bind/clock.ts +52 -7
  142. package/src/kernel/boot-bind/gate.ts +71 -3
  143. package/src/kernel/boot-bind/honor-config.test.ts +247 -0
  144. package/src/kernel/boot-bind/signal.ts +64 -7
  145. package/src/kernel/boot-bind/vault.ts +27 -8
  146. package/src/kernel/boot.ts +10 -2
  147. package/src/kernel/errors.ts +8 -2
  148. package/src/kernel/fx.test.ts +13 -0
  149. package/src/kernel/fx.ts +27 -4
  150. package/src/plugins/auth-delivery.mailpit.integration.test.ts +10 -4
  151. package/src/release/exports.test.ts +26 -0
  152. package/src/release/exports.ts +64 -5
  153. package/src/release/index.ts +5 -0
  154. package/src/release/measure.exports.test.ts +13 -1
  155. package/src/release/measure.ts +76 -13
  156. package/src/release/official-plugins.ts +46 -0
  157. package/src/release/readme.test.ts +30 -2
  158. package/src/runtime/dev-request-log.test.ts +20 -1
  159. package/src/runtime/dev-request-log.ts +38 -0
  160. package/src/term.test.ts +15 -0
  161. package/src/term.ts +8 -3
  162. package/src/console/ui/dist/assets/index-CjxwRGVv.js +0 -10
@@ -83,6 +83,14 @@ still reaches `fx.log` is scrubbed by the boot redactor as before.
83
83
 
84
84
  That's the whole loop: declare → set → read. Everything below is what OKE guarantees on top of it.
85
85
 
86
+ ## Redacted until you reveal
87
+
88
+ <VaultRedacted />
89
+
90
+ Pass the wrapper freely — `fx.log`, `String()`, and `JSON.stringify` all yield
91
+ `[redacted]`. Call `.reveal()` once at the provider boundary that needs the
92
+ credential.
93
+
86
94
  ## Two kinds of contracts
87
95
 
88
96
  | Declaration | Sensitive? | Console shows | Use for |
@@ -143,17 +151,19 @@ In docker mode, `oke vault set` writes straight to the running OpenBao — no gi
143
151
  The **Console** (`:6533`) can also set and rotate values, but it is **write-only**: it shows a salted fingerprint (`sha256:…`) per secret, never the cleartext. When you rotate a key there, the panel shows the **blast radius** — which in-flight durable runs will wake up with the new value.
144
152
 
145
153
  <Callout title="Secrets never reach logs">
146
- Every loaded secret is registered with the redactor at boot. Even if you accidentally pass one to
147
- `fx.log`, the log line shows `***` the value, never. Traces and the Console get fingerprints.
154
+ `fx.vault` returns `Redacted` `fx.log` shows `[redacted]`. A revealed string that still reaches
155
+ a log is scrubbed by the boot redactor to `[redacted:secret]`. Traces and the Console get
156
+ fingerprints, never cleartext.
148
157
  </Callout>
149
158
 
150
159
  ## Per-environment drivers
151
160
 
152
- Which backend holds your values depends on the mode — configured once in `oke.config.ts`:
161
+ Which backend holds your values depends on the mode — configured once in `oke.config.ts`.
162
+ App boot and the Console share the same resolution-chain builder (`env` layers → backend):
153
163
 
154
164
  ```typescript title="oke.config.ts"
155
165
  vault: {
156
- local: "dotenv", // .env.local — zero ceremony
166
+ local: "env", // .env.local — zero ceremony
157
167
  docker: "openbao", // real vault in the compose stack
158
168
  test: "memory", // seeded map — deterministic tests
159
169
  prod: "openbao", // real vault in production
@@ -162,7 +172,7 @@ vault: {
162
172
 
163
173
  | Driver | Backend | Best for |
164
174
  | --------- | --------------- | ------------------------------------------ |
165
- | `dotenv` | `.env.local` | Local loop — no infra at all |
175
+ | `env` | `.env.local` | Local loop — no infra at all |
166
176
  | `openbao` | OpenBao (KV v2) | Docker + prod — durable, access-controlled |
167
177
  | `memory` | in-process map | Tests |
168
178
 
@@ -1,12 +1,12 @@
1
1
  ---
2
2
  title: Basic Usage
3
- description: Scaffold the standard starter, call the health Flow, and inspect it in the Console.
4
- source: packages/create-oke/template
3
+ description: Scaffold the standard Notes starter, call health and notes Flows, and inspect them in the Console.
4
+ source: packages/create-oke/templates/standard
5
5
  icon: SquareTerminal
6
6
  ---
7
7
 
8
8
  Write your first Flow, call it from a typed client, and prove it with
9
- `bun:test` — using the same standard starter `create-oke` scaffolds.
9
+ `bun:test` — using the same standard Notes starter `create-oke` scaffolds.
10
10
 
11
11
  <Callout title="The one rule">
12
12
  All reads, writes, emits, sends, and external calls belong behind `fx`. That is what makes effects
@@ -26,6 +26,9 @@ cd my-app
26
26
  oke dev
27
27
  ```
28
28
 
29
+ Use `--template advanced` for the same Notes domain with files attach, a daily
30
+ digest Clock, and an AI summarize stub.
31
+
29
32
  <Surfaces />
30
33
 
31
34
  </Step>
@@ -33,7 +36,7 @@ oke dev
33
36
  <Step>
34
37
  ### Read the health Flow
35
38
 
36
- The standard starter already exports a health Flow:
39
+ The standard starter already exports a named health Flow:
37
40
 
38
41
  ```typescript title="flows/main"
39
42
  import { on, flow, http, gate } from "okengine";
@@ -42,13 +45,16 @@ import { z } from "zod";
42
45
  export const health = on(
43
46
  http.get("/health").gate(gate.public),
44
47
  flow({
48
+ name: "main.health",
49
+ unit: "main",
45
50
  out: z.object({ ok: z.literal(true) }),
46
51
  do: () => ({ ok: true as const }),
47
52
  }),
48
53
  );
49
54
  ```
50
55
 
51
- Change the route or output and save. App and Console update together from the
56
+ Named flows show up as `main.health` (not `flow_1`) in `oke dev` logs.
57
+ Change the route or output and save — App and Console update together from the
52
58
  same Manifest.
53
59
 
54
60
  </Step>
@@ -61,8 +67,9 @@ Adopted modules become namespaces on the client:
61
67
  ```typescript title="app"
62
68
  import { oke } from "okengine";
63
69
  import * as main from "./flows/main";
70
+ import * as notes from "./flows/notes";
64
71
 
65
- export const app = oke({ name: "standard" }).adopt({ main });
72
+ export const app = oke({ name: "notes" }).adopt({ main, notes });
66
73
  export type App = typeof app;
67
74
  ```
68
75
 
@@ -6,11 +6,11 @@ icon: Download
6
6
  ---
7
7
 
8
8
  This page gets you from zero to a running app on Bun. The engine targets
9
- **Bun ≥ 1.3** — prefer it for install, scaffold, and `oke dev`.
9
+ **Bun ≥ 1.3.14** — prefer it for install, scaffold, and `oke dev`.
10
10
 
11
11
  <Callout title="The one rule">
12
- Prefer `bun add okengine` when you want the `oke` CLI on your PATH. JSR (`@omqkhafi/okengine`) is
13
- the library API only.
12
+ The npm package is `okengine`; the CLI binary is `oke`. Use `bun add` in a project or `bun install
13
+ -g okengine` for a global `oke` on PATH. JSR is the library API only.
14
14
  </Callout>
15
15
 
16
16
  ## Quick start
@@ -20,7 +20,7 @@ This page gets you from zero to a running app on Bun. The engine targets
20
20
  <Step>
21
21
  ### Prerequisites
22
22
 
23
- - [Bun](https://bun.sh) ≥ 1.3 (`bun --version`)
23
+ - [Bun](https://bun.sh) ≥ 1.3.14 (`bun --version`)
24
24
  - A terminal and a code editor
25
25
 
26
26
  </Step>
@@ -28,10 +28,18 @@ This page gets you from zero to a running app on Bun. The engine targets
28
28
  <Step>
29
29
  ### Install the package
30
30
 
31
+ Project dependency (framework + local `oke`):
32
+
31
33
  ```bash title="Terminal"
32
34
  bun add okengine
33
35
  ```
34
36
 
37
+ Global CLI (`oke` on PATH):
38
+
39
+ ```bash title="Terminal"
40
+ bun install -g okengine
41
+ ```
42
+
35
43
  Library-only via JSR:
36
44
 
37
45
  ```bash title="Terminal"
@@ -46,12 +54,23 @@ bunx jsr add @omqkhafi/okengine
46
54
  ```bash title="Terminal"
47
55
  bunx create-oke@latest my-app
48
56
  bunx create-oke@latest my-app --template standard
57
+ bunx create-oke@latest my-app --template advanced
49
58
  bunx create-oke@latest my-app --sql postgres
59
+ bunx create-oke@latest my-app --yes
50
60
  ```
51
61
 
52
- `standard` is the only starter. Default: local SQLite, Docker/production
53
- Postgres. `--sql postgres` pins `store.sql` for **local, Docker, and
54
- production** schema stays dialect-agnostic (`store.schema.table`).
62
+ Two Notes starters: **standard** (local-first) and **advanced** (docker-ready +
63
+ files/digest/AI). On a TTY: pick a template, then **recommended defaults**,
64
+ **customize** (choose **local** or **docker** first, walk that side’s facets,
65
+ optionally customize the other; saved to `~/.oke/create-defaults.json`), or
66
+ **reuse** when saved settings match that template. The project-name step rejects
67
+ a path that already exists and is not empty.
68
+
69
+ Non-TTY / `--yes` stay scriptable. Default pins: local SQLite · Docker/prod
70
+ Postgres. `--sql postgres` pins SQL envs; `--ai` runs the AI model wizard
71
+ **before** install. Scaffold writes `.oke/mode` from the primary side
72
+ (recommended: standard → `local`, advanced → `docker`) so the first `oke dev`
73
+ skips the mode prompt unless you delete that file or pass `--local` / `--docker`.
55
74
 
56
75
  </Step>
57
76
 
@@ -63,11 +82,14 @@ cd my-app
63
82
  oke dev
64
83
  ```
65
84
 
66
- Three ports come up together (mnemonic: **O·K·E = 6·5·3**):
85
+ Four ports come up together (mnemonic: **O·K·E = 6·5·3**):
67
86
 
68
87
  <Surfaces />
69
88
 
70
- Open `http://localhost:6533`. If the Console lists your flows, the install
89
+ Open `http://localhost:6533`. On first boot, claim the Console with the
90
+ **claim code** printed once in the `oke dev` log, then create the first
91
+ operator. Password policy matches Gate auth: **≥ 12 characters, with a letter
92
+ and a number.** After that, if the Console lists your flows, the install
71
93
  worked — **derived, not configured.**
72
94
 
73
95
  </Step>
@@ -103,8 +125,9 @@ Uses the `docker` driver profile in `oke.config.ts` (filled from `prod` when
103
125
  omitted). Compose and credentials land under `docker/` (`.env.docker` beside
104
126
  compose); host ports are unique per project.
105
127
 
106
- Bare `oke dev` prompts once on a TTY (saved in `.oke/mode`); non-TTY defaults
107
- to `local`.
128
+ Bare `oke dev` uses `.oke/mode` when present (seeded by create-oke). On a TTY
129
+ with no saved mode it prompts once and writes the file; non-TTY defaults to
130
+ `local`.
108
131
 
109
132
  | Protocol | Env shape (generated) |
110
133
  | ------------------- | --------------------------------------------------------------------------------------------------------------- |
@@ -122,8 +145,9 @@ controls are preserved when `oke dev --docker` regenerates the file.
122
145
  <Accordions>
123
146
 
124
147
  <Accordion title="oke: command not found after install">
125
- Use `bun add okengine` (npm package), not JSR alone the CLI ships with the npm distribution. Or
126
- invoke via `bunx oke dev` from the project.
148
+ Install the npm package `okengine` (not JSR alone): `bun add okengine` in the project, or `bun
149
+ install -g okengine` for a global binary. Or invoke via `bunx oke dev`. There is no separate `oke`
150
+ package name — only `okengine`.
127
151
  </Accordion>
128
152
 
129
153
  <Accordion title="Console is empty / no flows listed">
@@ -131,11 +155,23 @@ controls are preserved when `oke dev --docker` regenerates the file.
131
155
  boots and the Manifest is extracted — fix TypeScript errors in the terminal first.
132
156
  </Accordion>
133
157
 
158
+ <Accordion title="Create first operator fails (password / claim code)">
159
+ Use a password with at least 12 characters, including a letter and a number. Copy the claim code
160
+ from the current `oke dev` boot log (it expires in 30 minutes; restart mint a new one). The form
161
+ and the request log now show the reason — not a bare `TransportError` / `500`.
162
+ </Accordion>
163
+
134
164
  <Accordion title="Database out of sync after editing schema">
135
165
  Local `oke dev` auto-pushes by default. If you passed `--no-db-push` or set `db.autoPush: false`,
136
166
  run `oke db push` yourself, or re-enable auto-push.
137
167
  </Accordion>
138
168
 
169
+ <Accordion title="Docker containers left after deleting a project">
170
+ Each project gets its own `oke-dev-<id>` compose stack; `oke dev` stop keeps volumes. Run
171
+ `oke docker clean` (TTY picker) or `oke docker clean --all --yes` from anywhere to remove
172
+ leftover containers, networks, and volumes.
173
+ </Accordion>
174
+
139
175
  </Accordions>
140
176
 
141
177
  ## Learn more
@@ -60,7 +60,7 @@ on(db.table(users).changed("email"), reverify);
60
60
  <Step>
61
61
  ### See what was derived
62
62
 
63
- Run `oke dev`. Three surfaces come up together — nothing configured by hand in
63
+ Run `oke dev`. Four surfaces come up together — nothing configured by hand in
64
64
  a separate dashboard.
65
65
 
66
66
  <Surfaces />
@@ -8,13 +8,15 @@ source: "docs/cli.md"
8
8
  ### Commands
9
9
 
10
10
  ```bash
11
- bun add okengine # ONE package
11
+ bun add okengine # ONE package (project)
12
+ bun install -g okengine # global `oke` on PATH
12
13
 
13
14
  oke dev # watch · hot reload · Console :6533 · app :6530 · MCP :6535 · docs MCP :6536
14
15
  # → client types + domain schema push on save (local)
15
16
  oke dev --local # -l session-only local (in-memory; never writes .oke/mode)
16
- oke dev --no-db-push # opt out of auto oke db push on schema change
17
+ oke dev --no-db-push # opt out of auto oke db push on schema input save
17
18
  oke dev --docker # -d infra compose under docker/ (no app container; host Bun)
19
+ # Ctrl-C / close terminal → docker compose stop (volumes kept)
18
20
  oke dev -d store.sql,signal # partial: only these roles get real backends
19
21
  oke mode local|docker # get/set default oke dev mode (saved in .oke/mode)
20
22
 
@@ -31,10 +33,14 @@ oke client add <url> # types for a separate frontend repo
31
33
 
32
34
  oke docker # docker/Dockerfile + docker/compose.<role>.yml · …
33
35
  oke docker --prod # healthchecks, volumes, limits, secret refs, deploy.replicas
36
+ oke docker clean # TTY: pick oke-dev-* stacks → down -v (containers, networks, volumes)
37
+ oke docker clean --yes # non-TTY: current project stack only
38
+ oke docker clean --all --yes # non-TTY: every oke-dev-* stack on this machine
34
39
  oke images pin # tags → digests in oke.images.lock
35
40
 
36
41
  oke build --target edge # < 15 kB kernel profile
37
42
  oke eval # run prompt eval sets; fails CI on regression
43
+ oke ai setup # configure AI driver + models (TTY wizard or flags)
38
44
  oke branch prod --at "yesterday" # fork journaled state into a sandbox
39
45
  oke privacy erase --subject <id> # crypto-shredding: deletes the key, not the terabytes
40
46
  oke upgrade # run codemods for a breaking change, print the diff
@@ -44,6 +50,24 @@ In development, app, Console, MCP, and docs MCP prefer the canonical ports and m
44
50
  when occupied. Docker infrastructure uses stable per-project offsets in disjoint ranges for
45
51
  each built-in service, preventing one service's offset port from overlapping another's.
46
52
 
53
+ ### create-oke
54
+
55
+ ```bash
56
+ bunx create-oke@latest my-app # TTY: template → recommended / reuse / customize
57
+ bunx create-oke@latest my-app --template advanced
58
+ bunx create-oke@latest my-app --yes # non-interactive recommended defaults
59
+ bunx create-oke@latest my-app --sql postgres
60
+ bunx create-oke@latest my-app --ai # AI model wizard before install
61
+ bunx create-oke@latest my-app --no-ai
62
+ ```
63
+
64
+ On a TTY: pick **standard** or **advanced**, then recommended defaults or
65
+ customize. Customize asks **local** or **docker** first, walks facets for that
66
+ side only, then whether to customize the other (defaults if no). Writes
67
+ user-global `~/.oke/create-defaults.json` (reuse only when `template` matches).
68
+ Scaffold writes `.oke/mode` from the primary side. Non-TTY / `--yes` / explicit
69
+ `--template` never prompt.
70
+
47
71
  ### Additional commands
48
72
 
49
73
  ```bash
@@ -52,6 +76,8 @@ oke doctor --json # -j JSON on stdout; hints on stderr (agents /
52
76
  oke stack --json # -j
53
77
  oke images list # recipe · image · tag · digest · size (--json|-j)
54
78
  oke gates list # Module:Action catalogue (--json|-j)
79
+ oke ai setup --provider ollama --yes # non-interactive AI config
80
+ # TTY Ollama: needs quiz → RAM-tier panel → recommend / installed / manual (+ ← Back)
55
81
  ```
56
82
 
57
83
  ### Shell completion
@@ -74,6 +100,8 @@ Long form is canonical in docs; short form is convenience only. Shared letters f
74
100
  | `--docker` | `-d` | `dev` |
75
101
  | `--no-db-push` | | `dev` |
76
102
  | `--prod` | `-p` | `docker` |
103
+ | `--yes` | `-y` | `docker clean` |
104
+ | `--all` | | `docker clean` |
77
105
  | `--port` | `-p` | `start` |
78
106
  | `--check` | `-c` | `schema generate` |
79
107
  | `--config` | `-c` | `stack`, `docker`, `images`, `db *` (drizzle) |
@@ -25,11 +25,12 @@ drivers: {
25
25
  kv: { local: "memory", docker: "redis", test: "memory", prod: "redis" },
26
26
  files: { local: "fs", docker: "s3", test: "memory", prod: "s3" },
27
27
  },
28
- signal: { local: "memory", docker: "postgres", test: "memory", prod: "postgres" },
29
- clock: { local: "memory", docker: "postgres", test: "frozen", prod: "postgres" },
30
- vault: { local: "dotenv", docker: "openbao", test: "memory", prod: "openbao" },
28
+ signal: { local: "memory", docker: "redis", test: "memory", prod: "redis" },
29
+ clock: { local: "memory", docker: "file", test: "frozen", prod: "file" },
30
+ vault: { local: "env", docker: "openbao", test: "memory", prod: "openbao" },
31
31
  channel: {
32
32
  email: { local: "console", docker: "smtp", test: "console", prod: "smtp" },
33
+ sms: { local: "console", docker: "taqnyat", test: "console", prod: "taqnyat" },
33
34
  },
34
35
  },
35
36
  ```
@@ -40,13 +41,13 @@ drivers: {
40
41
  | `store.kv` | env driver map | `memory` · `redis` |
41
42
  | `store.files` | env driver map | `memory` · `fs` · `s3` |
42
43
  | `store.index` | env driver map | `memory` · `pgvector` · `libsql` · `meilisearch` |
43
- | `signal` | env driver map | `memory` · `postgres` · `redis` · `nats` |
44
- | `clock` | env driver map | `memory` · `postgres` · `frozen` |
45
- | `vault` | env driver map | `dotenv` · `openbao` · `memory` |
46
- | `channel.email` | env driver map | `console` · `smtp` · `resend` |
47
- | `channel.sms` | env driver map | `console` · `unifonic` |
48
- | `channel.whatsapp` | env driver map | medium declared; drivers added per provider |
49
- | `channel.push` | env driver map | medium declared; drivers added per provider |
44
+ | `signal` | env driver map | `memory` · `redis` (boot); `postgres` · `nats` fail loud until clients bind |
45
+ | `clock` | env driver map | `memory` · `file` · `frozen` |
46
+ | `vault` | env driver map | `env` · `openbao` · `memory` · `managed` |
47
+ | `channel.email` | env driver map | `console` · `smtp` · `resend` · `sndr` |
48
+ | `channel.sms` | env driver map | `console` · `taqnyat` · `msegat` · `unifonic` |
49
+ | `channel.whatsapp` | env driver map | `console` · `wa-cloud` |
50
+ | `channel.push` | env driver map | `console` · `webpush` · `fcm` |
50
51
  | `ai` | env driver map | `mock` · `anthropic` · `openai-compatible` · `bedrock` · `vertex` · `ollama` |
51
52
  | `runs` | env driver map | runs-store driver (distinct from top-level `runs`) |
52
53
  | `prod` | `string[]` | flat protocol list for the Manifest — nested maps are preferred |
@@ -127,13 +128,13 @@ Runs retention and redaction at the config surface — **not** the same key as `
127
128
 
128
129
  Domain schema sync for `oke db push | generate | migrate` (Drizzle). Unrelated to `oke schema generate`.
129
130
 
130
- | Option | Default | Meaning |
131
- | ----------- | --------------------------- | --------------------------------------------------------------------------- |
132
- | `autoPush` | `true` (local only) | Auto-run `db push` on schema change in `oke dev`; forced off in docker/prod |
133
- | `config` | `"drizzle.config.ts"` | Path to the drizzle-kit config |
134
- | `declare` | `"src/schema.decl.ts"` | Abstract schema module (`store.schema.table` exports) |
135
- | `generated` | `"src/schema.generated.ts"` | Where `oke db` emits dialect Drizzle |
136
- | `entry` | `src/app.ts` | App entry for collecting plugin table contributions |
131
+ | Option | Default | Meaning |
132
+ | ----------- | --------------------------- | ----------------------------------------------------------------------------------------------------------------- |
133
+ | `autoPush` | `true` (local only) | Auto-run `db push` when schema inputs change in `oke dev` (not on emit of `generated`); forced off in docker/prod |
134
+ | `config` | `"drizzle.config.ts"` | Path to the drizzle-kit config |
135
+ | `declare` | `"src/schema.decl.ts"` | Abstract schema module (`store.schema.table` exports) |
136
+ | `generated` | `"src/schema.generated.ts"` | Where `oke db` emits dialect Drizzle |
137
+ | `entry` | `src/app.ts` | App entry for collecting plugin table contributions |
137
138
 
138
139
  ## topology
139
140
 
@@ -61,27 +61,53 @@ Written by the first-boot bootstrap; you normally only set these when pointing a
61
61
  | `OKE_VAULT_TOKEN` | App token (least-privilege) | — |
62
62
  | `OKE_VAULT_MOUNT` | KV v2 mount path | `"secret"` |
63
63
 
64
- ## Channel (email)
65
-
66
- | Variable | Used for |
67
- | ----------------------- | ----------------------------------------- |
68
- | `SMTP_URL` | Full SMTP connection string (`smtp://…`) |
69
- | `SMTP_USER` | Overrides the user embedded in `SMTP_URL` |
70
- | `SMTP_PASSWORD` | Overrides the password in `SMTP_URL` |
71
- | `OKE_CHANNEL_EMAIL_URL` | OKE-specific alternative to `SMTP_URL` |
64
+ ## Channel (email) — boot binder
65
+
66
+ Read when `drivers.channel.email` resolves to that driver id.
67
+
68
+ | Variable | Used for |
69
+ | ----------------------- | ------------------------------------------------ |
70
+ | `SMTP_URL` | `smtp` full SMTP URL (`smtp://…`) |
71
+ | `SMTP_USER` | Overrides the user embedded in `SMTP_URL` |
72
+ | `SMTP_PASSWORD` | Overrides the password in `SMTP_URL` |
73
+ | `OKE_CHANNEL_EMAIL_URL` | Alternative to `SMTP_URL` |
74
+ | `RESEND_API_KEY` | `resend` API key |
75
+ | `SNDR_API_KEY` | `sndr` API key |
76
+ | `SNDR_BASE_URL` | Optional SNDR API origin (default `api.sndr.sh`) |
77
+
78
+ ## Channel (SMS) — boot binder
79
+
80
+ Read when `drivers.channel.sms` resolves to that driver id (`console` opens nothing).
81
+
82
+ | Variable | Used for |
83
+ | ---------------------- | -------------------------------- |
84
+ | `TAQNYAT_BEARER_TOKEN` | `taqnyat` bearer token |
85
+ | `TAQNYAT_TOKEN` | Alias for `TAQNYAT_BEARER_TOKEN` |
86
+ | `TAQNYAT_SENDER` | Taqnyat pre-approved sender id |
87
+ | `MSEGAT_USERNAME` | `msegat` account username |
88
+ | `MSEGAT_API_KEY` | `msegat` API key |
89
+ | `MSEGAT_SENDER` | Msegat pre-approved sender id |
90
+ | `UNIFONIC_APPSID` | `unifonic` AppSid |
91
+ | `UNIFONIC_APP_SID` | Alias for `UNIFONIC_APPSID` |
92
+ | `UNIFONIC_SENDER` | Unifonic SenderID (optional) |
93
+
94
+ WhatsApp (`wa-cloud`) and push (`webpush` / `fcm`) are not opened from env at
95
+ boot — pass them on `BootOptions.channel.drivers` with their open options.
72
96
 
73
97
  ## AI providers
74
98
 
75
- | Variable | Used for | Default when unset |
76
- | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- |
77
- | `ANTHROPIC_API_KEY` | `anthropic` driver credential | — |
78
- | `ANTHROPIC_MODEL` | Model override for the anthropic driver | — |
79
- | `OPENAI_API_KEY` | `openai-compatible` driver credential | — |
80
- | `OPENAI_BASE_URL` | `openai-compatible` base URL | OpenAI cloud |
81
- | `OKE_AI_DRIVER` | Force the AI driver id (honoured in docker mode) | config map |
82
- | `OKE_AI_URL` | Ollama (or openai-compatible) base URL | `http://127.0.0.1:11434` (ollama) |
83
- | `OKE_AI_MODEL` | Default model for `ollama` (any pulled model; `qwen3.5:9b` is a balanced local-dev starting point; on Apple Silicon try `qwen3.5:9b-mlx`) | `qwen3.5:9b` |
84
- | `OLLAMA_HOST` | Alternate Ollama host (`host:port` or URL) when `OKE_AI_URL` is unset | — |
99
+ | Variable | Used for | Default when unset |
100
+ | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- |
101
+ | `ANTHROPIC_API_KEY` | `anthropic` driver credential | — |
102
+ | `ANTHROPIC_MODEL` | Model override for the anthropic driver | — |
103
+ | `OPENAI_API_KEY` | `openai-compatible` driver credential | — |
104
+ | `OPENAI_BASE_URL` | `openai-compatible` base URL | OpenAI cloud |
105
+ | `OKE_AI_DRIVER` | Force the AI driver id (honoured in docker mode) | config map |
106
+ | `OKE_AI_URL` | Ollama (or openai-compatible) base URL | `http://127.0.0.1:11434` (ollama) |
107
+ | `OKE_AI_MODEL` | Default model for `ollama` (any pulled model; `qwen3.5:9b` is a balanced local-dev starting point; on Apple Silicon try `qwen3.5:9b-mlx`) | `qwen3.5:9b` |
108
+ | `OKE_AI_VISION_MODEL` | Vision model id written by `oke ai setup` (logical `ai.model("vision")`) | — |
109
+ | `OKE_AI_EMBED_MODEL` | Embedding model id written by `oke ai setup` | — |
110
+ | `OLLAMA_HOST` | Alternate Ollama host (`host:port` or URL) when `OKE_AI_URL` is unset | — |
85
111
 
86
112
  ## Framework behavior
87
113
 
@@ -29,17 +29,18 @@ string. Custom app codes stay message-less until registered.
29
29
 
30
30
  ## OKE numeric codes
31
31
 
32
- | Code | Name | Cause | Fix |
33
- | ------ | ----------------- | ------------------------------------------------ | ----------------------------------------------- |
34
- | `1001` | undeclared read | Flow reads a resource not in `effects.reads` | Add it to the flow's `effects.reads` |
35
- | `1002` | undeclared write | Flow writes a resource not in `effects.writes` | Add it to the flow's `effects.writes` |
36
- | `1003` | undeclared emit | Flow emits a signal not in `effects.emits` | Add it to the flow's `effects.emits` |
37
- | `1004` | undeclared send | Flow sends a template not in `effects.sends` | Add it to the flow's `effects.sends` |
38
- | `1005` | undeclared ask | Flow asks a prompt not in `effects.asks` | Add it to the flow's `effects.asks` |
39
- | `1006` | undeclared secret | Flow reads a secret not in `effects.secrets` | Add it to the flow's `effects.secrets` |
40
- | `1007` | undeclared call | Flow calls a flow not in `effects.calls` | Add it to the flow's `effects.calls` |
41
- | `1042` | orphan emit | Emitted signal has no subscriber | Add `on(signal, …)` or declare `optional: true` |
42
- | `1101` | schema missing | Domain table absent in docker/prod no auto-DDL | Run `oke db migrate` against this environment |
32
+ | Code | Name | Cause | Fix |
33
+ | ------ | ----------------- | ------------------------------------------------ | -------------------------------------------------- |
34
+ | `1001` | undeclared read | Flow reads a resource not in `effects.reads` | Add it to the flow's `effects.reads` |
35
+ | `1002` | undeclared write | Flow writes a resource not in `effects.writes` | Add it to the flow's `effects.writes` |
36
+ | `1003` | undeclared emit | Flow emits a signal not in `effects.emits` | Add it to the flow's `effects.emits` |
37
+ | `1004` | undeclared send | Flow sends a template not in `effects.sends` | Add it to the flow's `effects.sends` |
38
+ | `1005` | undeclared ask | Flow asks a prompt not in `effects.asks` | Add it to the flow's `effects.asks` |
39
+ | `1006` | undeclared secret | Flow reads a secret not in `effects.secrets` | Add it to the flow's `effects.secrets` |
40
+ | `1007` | undeclared call | Flow calls a flow not in `effects.calls` | Add it to the flow's `effects.calls` |
41
+ | `1042` | orphan emit | Emit with zero subscribers and `optional` false | Add `on(signal, …)` or declare `optional: true` |
42
+ | `1043` | signal schema | Emit payload failed the signal's `schema` | Pass a payload that matches `schema`, or remove it |
43
+ | `1101` | schema missing | Domain table absent in docker/prod — no auto-DDL | Run `oke db migrate` against this environment |
43
44
 
44
45
  <Callout title="Effects are usually inferred">
45
46
  The 1001–1007 family exists for flows that declare effects explicitly. Most apps never write an
@@ -16,24 +16,26 @@ flow({
16
16
 
17
17
  ## Stores
18
18
 
19
- | Signature | Records | Returns |
20
- | ------------------------------------------------------------------- | ------------ | --------------------------------------------------------- |
21
- | `fx.store(sqlDecl).select().from(t)…` | `read` | rows (`where` · `orderBy` · `limit` · `offset` chainable) |
22
- | `fx.store(sqlDecl).insert(t).values(v)` | `write` | `Promise<void>` |
23
- | `fx.store(sqlDecl).update(t).set(v).where(…)` | `write` | `Promise<void>` |
24
- | `fx.store(sqlDecl).delete(t).where(…)` | `write` | `Promise<void>` |
25
- | `fx.store(sqlDecl).findById(t, id)` | `read` | row \| undefined |
26
- | `fx.store(kv).get / set(key, value, ttl?) / delete / list(prefix?)` | read / write | per op |
27
- | `fx.store(files).put / get / delete / list(prefix?)` | read / write | per op |
28
- | `fx.store(index).upsert / search(vector, topK?) / delete` | read / write | per op |
19
+ | Signature | Records | Returns |
20
+ | ------------------------------------------------------------------- | ------------ | ------------------------------------------------------------------------------------------- |
21
+ | `fx.store(sqlDecl).select().from(t)…` | `read` | rows (`where` · `orderBy` · `limit` · `offset` chainable) |
22
+ | `fx.store(sqlDecl).insert(t).values(v)` | `write` | `Promise<void>` |
23
+ | `fx.store(sqlDecl).update(t).set(v).where(…)` | `write` | `Promise<void>` |
24
+ | `fx.store(sqlDecl).delete(t).where(…)` | `write` | `Promise<void>` |
25
+ | `fx.store(sqlDecl).findById(t, id)` | `read` | row \| undefined |
26
+ | `fx.store(kv).get / set(key, value, ttl?) / delete / list(prefix?)` | read / write | per op |
27
+ | `fx.store(files).put / get / delete / list(prefix?)` | read / write | per op |
28
+ | `fx.store(files).image(key\|bytes).…` | read / write | Bun.Image chain; terminals gate (see [Store](/docs/elements/store#images--image--putimage)) |
29
+ | `fx.store(files).putImage(key, data, opts?)` | `write` | original + variants (+ optional LQIP) |
30
+ | `fx.store(index).upsert / search(vector, topK?) / delete` | read / write | per op |
29
31
 
30
32
  See [Store](/docs/elements/store) for the query-builder surface.
31
33
 
32
34
  ## Signals
33
35
 
34
- | Signature | Records | Notes |
35
- | --------------------------- | ------- | ------------------------------------------------ |
36
- | `fx.emit(signal, payload?)` | `emit` | With the postgres driver, joins your transaction |
36
+ | Signature | Records | Notes |
37
+ | ------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
38
+ | `fx.emit(signal, payload?, { key? })` | `emit` | Commits the signal outbox when the call resolves; optional `key` serializes `once` per key; throws **OKE1042** (orphan) or **OKE1043** (schema) |
37
39
 
38
40
  ## Flows
39
41
 
@@ -45,7 +45,9 @@ Our path is concrete: a booking name containing "ignore previous instructions an
45
45
  - **Plugin panels** run in a sandboxed iframe without `allow-same-origin`, communicating only over a `postMessage` bridge with their own CSP, no access to the operator session token, and exposure limited to that plugin's declared flows.
46
46
  - **Session and framing:** `frame-ancestors 'none'`, `SameSite=Strict`, step-up authentication before destructive actions.
47
47
  - **Secret write path:** TLS required, no autocomplete, never echoed, never logged, not retained in browser memory after submission.
48
- - **The setup claim code** is rate-limited and compared in constant time.
48
+ - **The setup claim code** is rate-limited and compared in constant time. The first-operator password
49
+ uses the same policy as Gate auth (minLength 12, letter + number); weak passwords return
50
+ `ClaimFailed` / `password_policy`, not an opaque 500.
49
51
 
50
52
  #### 10.5 Reversibility governs the confirmation pattern
51
53