okengine 0.9.1 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/AGENTS.md +1 -1
  2. package/manifest.v1.schema.json +9 -1
  3. package/package.json +3 -3
  4. package/site/content/docs/console/runs.mdx +10 -0
  5. package/site/content/docs/console/traces.mdx +4 -0
  6. package/site/content/docs/elements/ai.mdx +54 -32
  7. package/site/content/docs/elements/channel.mdx +97 -9
  8. package/site/content/docs/elements/clock.mdx +1 -0
  9. package/site/content/docs/elements/flow.mdx +42 -8
  10. package/site/content/docs/get-started/basic-usage.mdx +5 -1
  11. package/site/content/docs/get-started/installation.mdx +9 -5
  12. package/site/content/docs/meta.json +2 -0
  13. package/site/content/docs/providers/azure-redis.mdx +85 -0
  14. package/site/content/docs/providers/cockroachdb.mdx +86 -0
  15. package/site/content/docs/providers/digitalocean-caching.mdx +88 -0
  16. package/site/content/docs/providers/dragonfly-cloud.mdx +82 -0
  17. package/site/content/docs/providers/elasticache.mdx +92 -0
  18. package/site/content/docs/providers/index.mdx +101 -0
  19. package/site/content/docs/providers/memorystore.mdx +89 -0
  20. package/site/content/docs/providers/meta.json +20 -0
  21. package/site/content/docs/providers/neon.mdx +115 -0
  22. package/site/content/docs/providers/redis-cloud.mdx +93 -0
  23. package/site/content/docs/providers/supabase.mdx +117 -0
  24. package/site/content/docs/providers/upstash.mdx +91 -0
  25. package/site/content/docs/providers/yugabytedb.mdx +95 -0
  26. package/site/content/docs/recipes/caddy.mdx +128 -0
  27. package/site/content/docs/recipes/cockroachdb.mdx +127 -0
  28. package/site/content/docs/recipes/dragonfly.mdx +125 -0
  29. package/site/content/docs/recipes/index.mdx +146 -0
  30. package/site/content/docs/recipes/llama-cpp.mdx +153 -0
  31. package/site/content/docs/recipes/mailpit.mdx +136 -0
  32. package/site/content/docs/recipes/meilisearch.mdx +139 -0
  33. package/site/content/docs/recipes/meta.json +31 -0
  34. package/site/content/docs/recipes/ollama.mdx +142 -0
  35. package/site/content/docs/recipes/openbao.mdx +141 -0
  36. package/site/content/docs/recipes/pgdog.mdx +137 -0
  37. package/site/content/docs/recipes/postgres.mdx +136 -0
  38. package/site/content/docs/recipes/redis.mdx +130 -0
  39. package/site/content/docs/recipes/rustfs.mdx +148 -0
  40. package/site/content/docs/recipes/sglang.mdx +105 -0
  41. package/site/content/docs/recipes/supabase-docker.mdx +145 -0
  42. package/site/content/docs/recipes/timescale.mdx +129 -0
  43. package/site/content/docs/recipes/traefik.mdx +136 -0
  44. package/site/content/docs/recipes/valkey.mdx +121 -0
  45. package/site/content/docs/recipes/vllm.mdx +106 -0
  46. package/site/content/docs/recipes/yugabytedb.mdx +135 -0
  47. package/site/content/docs/reference/cli.md +42 -6
  48. package/site/content/docs/reference/configuration.mdx +5 -3
  49. package/site/content/docs/reference/environment-variables.mdx +12 -12
  50. package/site/content/docs/reference/fx.mdx +38 -4
  51. package/src/cli/ai-setup/ai-setup.test.ts +32 -12
  52. package/src/cli/ai-setup/apply.ts +65 -23
  53. package/src/cli/ai-setup/catalog.ts +1316 -35
  54. package/src/cli/ai-setup/detect-ollama.ts +47 -0
  55. package/src/cli/ai-setup/index.ts +44 -7
  56. package/src/cli/ai-setup/prompts.ts +430 -486
  57. package/src/cli/ai-setup/recommend.test.ts +150 -50
  58. package/src/cli/ai-setup/recommend.ts +118 -101
  59. package/src/cli/ask-seed.test.ts +68 -0
  60. package/src/cli/ask-seed.ts +79 -0
  61. package/src/cli/db-seed-redact.test.ts +25 -0
  62. package/src/cli/db-seed.ts +36 -7
  63. package/src/cli/db.test.ts +1 -1
  64. package/src/cli/db.ts +2 -2
  65. package/src/cli/dev-app-runner.ts +6 -0
  66. package/src/cli/dev-controls.test.ts +107 -0
  67. package/src/cli/dev-controls.ts +354 -0
  68. package/src/cli/dev-schema-sync.ts +12 -4
  69. package/src/cli/dev.test.ts +50 -30
  70. package/src/cli/dev.ts +633 -66
  71. package/src/cli/docker-cli.test.ts +15 -0
  72. package/src/cli/drizzle-env.test.ts +40 -6
  73. package/src/cli/drizzle-env.ts +22 -1
  74. package/src/cli/ensure-drizzle-config.ts +6 -4
  75. package/src/cli/hero-meta.test.ts +13 -1
  76. package/src/cli/hero-meta.ts +28 -6
  77. package/src/cli/index.ts +5 -0
  78. package/src/cli/registry.ts +24 -0
  79. package/src/cli/replay.test.ts +105 -0
  80. package/src/cli/replay.ts +206 -0
  81. package/src/cli/start.ts +12 -1
  82. package/src/compiler/effects-infer.ts +37 -8
  83. package/src/compiler/extract.test.ts +125 -0
  84. package/src/compiler/extract.ts +31 -6
  85. package/src/config/index.ts +3 -3
  86. package/src/console/server/claim.ts +3 -1
  87. package/src/console/ui/dist/assets/{index-ClX0ZCe5.js → index-D-9KFce5.js} +2 -2
  88. package/src/console/ui/dist/assets/panel-overview-CW0CQEc2.js +1 -0
  89. package/src/console/ui/dist/assets/panel-runs-BxuyDI3x.js +1 -0
  90. package/src/console/ui/dist/assets/{panel-signals-Dsxdu_AR.js → panel-signals-BXofbj7v.js} +1 -1
  91. package/src/console/ui/dist/assets/{panel-store-hy7O8HOs.js → panel-store-B8mH-arU.js} +1 -1
  92. package/src/console/ui/dist/assets/{panel-traces-CBiAe5go.js → panel-traces-DSA-ZWnf.js} +1 -1
  93. package/src/console/ui/dist/index.html +1 -1
  94. package/src/console/ui/overview/slo.ts +31 -3
  95. package/src/console/ui/overview/types.ts +6 -0
  96. package/src/console/ui/runs/errors.test.ts +53 -0
  97. package/src/console/ui/runs/errors.ts +88 -0
  98. package/src/console/ui/runs/index.ts +8 -0
  99. package/src/console/ui/runs/search.ts +16 -0
  100. package/src/console/ui/shell/panels/runs/RunsPanel.tsx +72 -1
  101. package/src/docker/ai-model-status.test.ts +101 -0
  102. package/src/docker/ai-model-status.ts +276 -0
  103. package/src/docker/compose-health.test.ts +115 -0
  104. package/src/docker/compose-health.ts +234 -0
  105. package/src/docker/compose.ts +24 -8
  106. package/src/docker/derive.ts +35 -9
  107. package/src/docker/docker.test.ts +304 -12
  108. package/src/docker/helpers.ts +56 -0
  109. package/src/docker/index.ts +52 -0
  110. package/src/docker/ollama-pull.ts +232 -0
  111. package/src/docker/recipes/cockroach.ts +24 -0
  112. package/src/docker/recipes/index.ts +32 -0
  113. package/src/docker/recipes/llama-cpp.ts +273 -0
  114. package/src/docker/recipes/ollama.ts +19 -23
  115. package/src/docker/recipes/pgdog.ts +4 -1
  116. package/src/docker/recipes/postgres.ts +5 -18
  117. package/src/docker/recipes/sglang.ts +55 -0
  118. package/src/docker/recipes/supabase.ts +21 -0
  119. package/src/docker/recipes/timescale.ts +18 -0
  120. package/src/docker/recipes/vllm.ts +44 -0
  121. package/src/docker/recipes/yugabyte.ts +23 -0
  122. package/src/docker/stack-id.test.ts +22 -0
  123. package/src/docker/stack-id.ts +38 -4
  124. package/src/docker/types.ts +15 -0
  125. package/src/drivers/ai-ollama.integration.test.ts +7 -4
  126. package/src/drivers/signal-engine.ts +10 -0
  127. package/src/drivers/signal-postgres.ts +24 -2
  128. package/src/drivers/signal-types.ts +10 -0
  129. package/src/elements/channel/fallback.test.ts +91 -0
  130. package/src/elements/channel/hard-bounce.test.ts +82 -0
  131. package/src/elements/channel/injection.test.ts +153 -0
  132. package/src/elements/channel/locale-ar.test.ts +102 -0
  133. package/src/elements/channel/otp-delivery.ts +5 -2
  134. package/src/elements/channel/receipts-ledger.test.ts +100 -0
  135. package/src/elements/channel/runtime.ts +24 -1
  136. package/src/elements/channel/test-helpers.ts +60 -0
  137. package/src/elements/channel/whatsapp-compliance.test.ts +124 -0
  138. package/src/elements/store/cache.test.ts +28 -0
  139. package/src/elements/store/cache.ts +1 -1
  140. package/src/elements/store/emit-drizzle.ts +43 -9
  141. package/src/elements/store/runtime.ts +1 -1
  142. package/src/elements/store/schema-decl.test.ts +2 -0
  143. package/src/elements/store/schema-decl.ts +15 -0
  144. package/src/elements/store/seed.ts +1 -1
  145. package/src/index.ts +1 -0
  146. package/src/kernel/app.ts +131 -10
  147. package/src/kernel/boot-bind/signal.ts +6 -2
  148. package/src/kernel/boot-bind/store.ts +2 -1
  149. package/src/kernel/boot.test.ts +6 -5
  150. package/src/kernel/boot.ts +113 -10
  151. package/src/kernel/compensate.test.ts +131 -0
  152. package/src/kernel/correlation.test.ts +108 -0
  153. package/src/kernel/effect-timing.test.ts +82 -0
  154. package/src/kernel/effects-stamping.test.ts +207 -0
  155. package/src/kernel/errors.ts +11 -0
  156. package/src/kernel/flow.ts +38 -0
  157. package/src/kernel/fx-runs.test.ts +49 -0
  158. package/src/kernel/fx.test.ts +14 -0
  159. package/src/kernel/fx.ts +153 -14
  160. package/src/manifest/sql-resource.ts +41 -0
  161. package/src/manifest/types.ts +8 -2
  162. package/src/runs/collect.ts +3 -0
  163. package/src/runs/export-otlp.ts +64 -0
  164. package/src/runs/index.ts +16 -0
  165. package/src/runs/parquet.ts +9 -0
  166. package/src/runs/types.ts +5 -0
  167. package/src/runs/window.test.ts +68 -0
  168. package/src/runs/window.ts +148 -0
  169. package/src/runtime/boot-warn.test.ts +45 -0
  170. package/src/runtime/boot-warn.ts +17 -0
  171. package/src/runtime/dev-request-log.ts +5 -5
  172. package/src/term.test.ts +95 -10
  173. package/src/term.ts +369 -14
  174. package/src/console/ui/dist/assets/panel-overview-DONDzBd2.js +0 -1
  175. package/src/console/ui/dist/assets/panel-runs-D5zf-D9c.js +0 -1
@@ -0,0 +1,125 @@
1
+ ---
2
+ title: "Dragonfly"
3
+ description: "Multi-threaded Redis-wire runtime — OKE_STORE_KV_PASSWORD, memlock ulimit, no maxmemory-policy flag."
4
+ icon: "Zap"
5
+ source: "docs/spec/unified-theory.md"
6
+ ---
7
+
8
+ Dragonfly is a multi-threaded, memory-efficient reimplementation of the Redis protocol —
9
+ same `redis://` wire format, different engine. Slots behind the same `redis` driver with
10
+ zero Flow changes.
11
+
12
+ <Callout title="The one rule">
13
+ Driver id stays `redis`. Dragonfly is an image choice — every KV call through `fx.store` behaves
14
+ identically to Redis or Valkey at the protocol layer.
15
+ </Callout>
16
+
17
+ ## Quick start
18
+
19
+ <Steps>
20
+
21
+ <Step>
22
+ ### Pin the image
23
+
24
+ ```typescript title="oke.config.ts"
25
+ images: {
26
+ "store.kv": "docker.dragonflydb.io/dragonflydb/dragonfly",
27
+ },
28
+ ```
29
+
30
+ </Step>
31
+
32
+ <Step>
33
+ ### Password + memory
34
+
35
+ ```bash
36
+ OKE_STORE_KV_PASSWORD=…
37
+ OKE_STORE_KV_MAXMEMORY=256mb # optional; recipe passes --maxmemory
38
+ REDIS_URL=redis://:…@127.0.0.1:6379
39
+ ```
40
+
41
+ Command: `dragonfly --requirepass "$OKE_STORE_KV_PASSWORD" --maxmemory "…"`.
42
+
43
+ Unlike Redis/Valkey, this recipe does **not** pass `--maxmemory-policy` — Dragonfly's
44
+ own defaults apply.
45
+
46
+ </Step>
47
+
48
+ <Step>
49
+ ### memlock ulimit
50
+
51
+ The recipe sets `ulimits.memlock: -1` — required for Dragonfly's shared-nothing design.
52
+ If the host forbids unlimited memlock, the container may fail to start.
53
+
54
+ </Step>
55
+
56
+ </Steps>
57
+
58
+ ## Required env
59
+
60
+ | Variable | Required? | Meaning |
61
+ | ------------------------ | ------------- | --------------------------------------------------- |
62
+ | `OKE_STORE_KV_PASSWORD` | **Yes** | `--requirepass` |
63
+ | `REDIS_URL` | **Yes** (app) | `redis://` URL for the driver |
64
+ | `OKE_STORE_KV_MAXMEMORY` | Optional | `--maxmemory` (default `0` in the command template) |
65
+ | `HEALTHCHECK_PORT` | Set by recipe | `6379` — used by the image's healthcheck script |
66
+
67
+ ## Data and backup
68
+
69
+ No named volume — ephemeral by default, same as Redis/Valkey. **Backup means:** do not
70
+ treat this container as durable storage until you add a volume and Dragonfly persistence
71
+ settings; rebuild from SQL/files, or use [Dragonfly Cloud](/docs/providers/dragonfly-cloud).
72
+
73
+ ## Production note
74
+
75
+ BSL 1.1 — converts to Apache 2.0 on a published change date per release. Free for
76
+ self-hosting; the BSL terms restrict offering Dragonfly itself as a commercial managed
77
+ service to third parties — running it for your own app is unaffected.
78
+
79
+ Multi-threaded architecture makes better use of multi-core hosts than single-threaded
80
+ Redis under heavy concurrent load. For a managed endpoint with TLS + passkey, see
81
+ [Dragonfly Cloud](/docs/providers/dragonfly-cloud).
82
+
83
+ ## What the recipe configures
84
+
85
+ | Field | Value |
86
+ | -------------- | ----------------------------------------------------- |
87
+ | Container port | `6379` |
88
+ | `ulimits` | `memlock: -1` |
89
+ | Healthcheck | `/usr/local/bin/healthcheck.sh`, every 5s, 10 retries |
90
+ | Connection URL | `redis://:pass@host:6379` |
91
+
92
+ ## Troubleshooting
93
+
94
+ <Accordions>
95
+ <Accordion title="Container exits — cannot lock memory / memlock">
96
+
97
+ Host `ulimit -l` is too low. The recipe requests unlimited memlock. Raise the Docker
98
+ daemon / systemd limit, or run on a host that allows `memlock: -1`.
99
+
100
+ </Accordion>
101
+ <Accordion title="oke boot: redis driver needs REDIS_URL">
102
+
103
+ Same driver failure as Redis — export `REDIS_URL` or let docker mode write it. Wrong
104
+ password yields Redis-protocol `NOAUTH` / `WRONGPASS` from the server.
105
+
106
+ </Accordion>
107
+ </Accordions>
108
+
109
+ ## Learn more
110
+
111
+ - [Store · KV](/docs/elements/store#kv) — full Redis-protocol image license table
112
+ - [Dragonfly Cloud](/docs/providers/dragonfly-cloud) — the managed equivalent
113
+ - [Redis](/docs/recipes/redis) · [Valkey](/docs/recipes/valkey) — the other two peers
114
+
115
+ ## Next
116
+
117
+ <Cards>
118
+ <Card
119
+ title="Dragonfly Cloud"
120
+ description="Managed Dragonfly."
121
+ href="/docs/providers/dragonfly-cloud"
122
+ />
123
+ <Card title="Valkey" description="Permissive-licensed peer." href="/docs/recipes/valkey" />
124
+ <Card title="PgDog" description="Pooling for the SQL side." href="/docs/recipes/pgdog" />
125
+ </Cards>
@@ -0,0 +1,146 @@
1
+ ---
2
+ title: "Recipes"
3
+ description: "Docker recipes already shipped in oke docker — SQL (including Cockroach, Yugabyte, Timescale), KV, proxy, and services."
4
+ icon: "Container"
5
+ source: "docs/spec/unified-theory.md"
6
+ ---
7
+
8
+ Every recipe below is already wired into `oke docker` — pin an image in `oke.config.ts`
9
+ and get env, healthcheck, and a connection URL for free.
10
+
11
+ <Callout title="The one rule">
12
+ Vendor choice lives in `images[…]` — never in `drivers.*`, which only ever say protocol ids
13
+ (`postgres`, `redis`, `s3`, `smtp`, `openbao`, `meilisearch`, `openai-compatible`, `ollama`, …).
14
+ </Callout>
15
+
16
+ ## SQL
17
+
18
+ <Cards>
19
+ <Card
20
+ title="Postgres"
21
+ description="Default store.sql — POSTGRES_*, PGDATA, DATABASE_URL."
22
+ href="/docs/recipes/postgres"
23
+ />
24
+ <Card
25
+ title="Supabase"
26
+ description="supabase/postgres extension bundle only."
27
+ href="/docs/recipes/supabase-docker"
28
+ />
29
+ <Card
30
+ title="CockroachDB"
31
+ description="Self-hosted single-node — port 26257, COCKROACH_*."
32
+ href="/docs/recipes/cockroachdb"
33
+ />
34
+ <Card
35
+ title="YugabyteDB"
36
+ description="Self-hosted YSQL — port 5433, YSQL_*."
37
+ href="/docs/recipes/yugabytedb"
38
+ />
39
+ <Card
40
+ title="Timescale"
41
+ description="Postgres + hypertables — same POSTGRES_* contract."
42
+ href="/docs/recipes/timescale"
43
+ />
44
+ </Cards>
45
+
46
+ ## KV
47
+
48
+ Driver id stays `redis` for every image below.
49
+
50
+ <Cards>
51
+ <Card
52
+ title="Redis"
53
+ description="Default store.kv — requirepass + maxmemory."
54
+ href="/docs/recipes/redis"
55
+ />
56
+ <Card title="Valkey" description="BSD-licensed Redis-wire fork." href="/docs/recipes/valkey" />
57
+ <Card
58
+ title="Dragonfly"
59
+ description="Multi-threaded Redis-wire runtime."
60
+ href="/docs/recipes/dragonfly"
61
+ />
62
+ </Cards>
63
+
64
+ ## Proxy
65
+
66
+ Opt-in via `images.proxy` — leave unset until you need HTTPS or `--scale app=N`.
67
+
68
+ <Cards>
69
+ <Card title="Caddy" description="Automatic-HTTPS, single instance." href="/docs/recipes/caddy" />
70
+ <Card
71
+ title="Traefik"
72
+ description="Docker-label discovery + socket-proxy."
73
+ href="/docs/recipes/traefik"
74
+ />
75
+ </Cards>
76
+
77
+ ## Services
78
+
79
+ <Cards>
80
+ <Card title="PgDog" description="Transaction pooler on :6432." href="/docs/recipes/pgdog" />
81
+ <Card
82
+ title="RustFS"
83
+ description="S3-compatible store.files — /data volume."
84
+ href="/docs/recipes/rustfs"
85
+ />
86
+ <Card title="Mailpit" description="SMTP catcher — UI on :8025." href="/docs/recipes/mailpit" />
87
+ <Card title="OpenBao" description="Raft vault — unseal key SPOF." href="/docs/recipes/openbao" />
88
+ <Card
89
+ title="Meilisearch"
90
+ description="Full-text store.index on :7700."
91
+ href="/docs/recipes/meilisearch"
92
+ />
93
+ </Cards>
94
+
95
+ ## AI (local / self-hosted)
96
+
97
+ Default local pin is **llama.cpp**. Ollama stays fully supported. vLLM / SGLang
98
+ are production-tier self-host options — most apps still use a managed provider.
99
+
100
+ | Workload | Recipe |
101
+ | -------------------------------- | ---------------------------------------------- |
102
+ | Single-user / dev (lightest) | [llama.cpp](/docs/recipes/llama-cpp) (default) |
103
+ | Local library UX | [Ollama](/docs/recipes/ollama) |
104
+ | Multi-user GPU concurrency | [vLLM](/docs/recipes/vllm) |
105
+ | Structured / agent GPU workloads | [SGLang](/docs/recipes/sglang) |
106
+ | Managed cloud | Anthropic / OpenAI / … (no `images.ai`) |
107
+
108
+ All four recipes **loopback-publish only** and pin explicit tags (never
109
+ `latest`). Load models from curated registries — never arbitrary untrusted GGUF.
110
+
111
+ <Cards>
112
+ <Card
113
+ title="llama.cpp"
114
+ description="Default local AI — OpenAI /v1, lightest footprint."
115
+ href="/docs/recipes/llama-cpp"
116
+ />
117
+ <Card
118
+ title="Ollama"
119
+ description="Fully supported alternative — library pull UX."
120
+ href="/docs/recipes/ollama"
121
+ />
122
+ <Card title="vLLM" description="Self-hosted multi-user GPU serving." href="/docs/recipes/vllm" />
123
+ <Card
124
+ title="SGLang"
125
+ description="Self-hosted structured / agent GPU serving."
126
+ href="/docs/recipes/sglang"
127
+ />
128
+ </Cards>
129
+
130
+ ## Learn more
131
+
132
+ - [Providers](/docs/providers) — managed cloud alternatives
133
+ - [Docker](/docs/deployment/docker) — compose derivation, PgDog, reverse proxy
134
+ - [Store](/docs/elements/store) — `sql` / `kv` / `files` / `index` facets
135
+
136
+ ## Next
137
+
138
+ <Cards>
139
+ <Card
140
+ title="Postgres"
141
+ description="Start with the default SQL recipe."
142
+ href="/docs/recipes/postgres"
143
+ />
144
+ <Card title="Providers" description="Managed cloud alternatives." href="/docs/providers" />
145
+ <Card title="Deployment" description="Compose, Swarm, or Kubernetes." href="/docs/deployment" />
146
+ </Cards>
@@ -0,0 +1,153 @@
1
+ ---
2
+ title: "llama.cpp"
3
+ description: "Default local AI image — OpenAI-compatible llama-server, loopback-only publish, pinned build ≥ b8146."
4
+ icon: "Bot"
5
+ source: "docs/spec/unified-theory.md"
6
+ ---
7
+
8
+ llama.cpp is the default `images.ai` pin for local inference. It runs
9
+ `llama-server` with an OpenAI-compatible `/v1` API — lightest resource footprint
10
+ for single-user / dev. Driver id stays `openai-compatible`.
11
+
12
+ <Callout title="The one rule">
13
+ Pin a verified build ≥ `b8146`, publish only on loopback, and load models only from curated
14
+ registries (Docker Hub `ai/`) — never arbitrary untrusted GGUF.
15
+ </Callout>
16
+
17
+ ## Quick start
18
+
19
+ <Steps>
20
+
21
+ <Step>
22
+ ### Pin driver + image
23
+
24
+ ```typescript title="oke.config.ts"
25
+ drivers: {
26
+ ai: { local: "openai-compatible", docker: "openai-compatible", test: "mock" },
27
+ },
28
+ images: {
29
+ ai: "ghcr.io/ggml-org/llama.cpp:server-b10290", // never :latest
30
+ },
31
+ ```
32
+
33
+ Or `oke ai setup --provider llama-cpp` (TTY: banner → Ultra Fast / Fast /
34
+ Balanced / Smart → up to 20 curated Docker Hub `ai/` picks per tier) / `--yes`
35
+ for `smollm2`.
36
+
37
+ </Step>
38
+
39
+ <Step>
40
+ ### Env written by the stack
41
+
42
+ ```bash title="docker/.env.docker"
43
+ OKE_AI_URL=http://127.0.0.1:8080/v1
44
+ OKE_AI_MODEL=smollm2
45
+ ```
46
+
47
+ `OKE_AI_MODEL` is a Docker Hub `ai/<id>` model. The recipe writes
48
+ `docker/llama-entrypoint.py`, which Hub-pulls into the cache volume (native
49
+ `llama download -dr`, then a CNCF `model.weight` fallback for newer tags like
50
+ gemma4), then starts `llama-server -m <gguf> --alias <id>` (single-model — not
51
+ router `--models-preset` / bare `--docker-repo`, which hang on b10290+). Never
52
+ set `LLAMA_ARG_MODELS_PRESET` / `LLAMA_ARG_DOCKER_REPO` in the container env.
53
+
54
+ `oke dev -d` shows the model id in the hero / Docker summary and polls
55
+ `/v1/models` in the background until status is `ready` (first boot may take
56
+ minutes while the Hub image downloads).
57
+
58
+ </Step>
59
+
60
+ <Step>
61
+ ### Ask through the OpenAI-compatible driver
62
+
63
+ ```typescript
64
+ import { ai } from "okengine";
65
+
66
+ export const smart = ai.model("smart", {
67
+ provider: "openai-compatible",
68
+ model: process.env.OKE_AI_MODEL ?? "smollm2",
69
+ });
70
+ ```
71
+
72
+ </Step>
73
+
74
+ </Steps>
75
+
76
+ ## Security (hard requirements)
77
+
78
+ | Control | Requirement |
79
+ | -------------------- | ---------------------------------------------------------------------------- |
80
+ | Host publish | `127.0.0.1:8080:8080` only — never `0.0.0.0` |
81
+ | Image pin | Explicit build ≥ **b8146** (CVE-2026-27940 / CVE-2026-33298); never `latest` |
82
+ | Model source | Docker Hub [`ai/`](https://hub.docker.com/u/ai) namespace (or known HF GGUF) |
83
+ | Untrusted GGUF | **Forbidden** — crafted files can RCE the parser on unpatched builds |
84
+ | RPC / extra backends | Not published — OpenAI HTTP only |
85
+
86
+ Inside the container the server listens on `0.0.0.0` so the Compose network and
87
+ loopback map work. The **host** bind is loopback — same isolation idea as
88
+ Postgres/Redis, stricter on the published interface because of GGUF CVE risk.
89
+
90
+ ## Required env
91
+
92
+ | Variable | Required? | Meaning |
93
+ | -------------- | ----------------- | --------------------------------------- |
94
+ | `OKE_AI_URL` | **Yes** in docker | Base URL including `/v1` |
95
+ | `OKE_AI_MODEL` | Recommended | Docker Hub `ai/` id (default `smollm2`) |
96
+
97
+ ## What the recipe configures
98
+
99
+ | Field | Value |
100
+ | -------------- | --------------------------------------------------------------- |
101
+ | Container port | `8080` |
102
+ | Host publish | `127.0.0.1:8080:8080` |
103
+ | Healthcheck | `GET /health`, every 5s, 24 retries, 900s start |
104
+ | Connection URL | `http://host:8080/v1` |
105
+ | Model load | `llama-entrypoint.py` → Hub pull (incl. CNCF weights) then `-m` |
106
+
107
+ ## When to choose something else
108
+
109
+ | Workload | Prefer |
110
+ | ------------------------------------------ | --------------------------------------- |
111
+ | Single-user / dev, lightest footprint | **llama.cpp** (this recipe) |
112
+ | Local DX with library pull UX | [Ollama](/docs/recipes/ollama) |
113
+ | Multi-user / production concurrency on GPU | [vLLM](/docs/recipes/vllm) |
114
+ | Structured / agent workloads on GPU | [SGLang](/docs/recipes/sglang) |
115
+ | Managed cloud | Anthropic / OpenAI / … (no `images.ai`) |
116
+
117
+ ## Troubleshooting
118
+
119
+ <Accordions>
120
+ <Accordion title="Connection refused on :8080">
121
+
122
+ Confirm compose published `127.0.0.1:8080:8080` (not bare `8080:8080` on all
123
+ interfaces). `OKE_AI_URL` must include `/v1`.
124
+
125
+ </Accordion>
126
+ <Accordion title="Model fails to load / slow first boot">
127
+
128
+ First start Hub-pulls the curated `ai/` model into the named cache volume
129
+ (see logs: `oke ai: ensuring…` / download %). Prefer `oke ai setup` tiers
130
+ (Ultra Fast → `smollm2`) for smoke tests. Confirm `OKE_AI_MODEL` is active in
131
+ `.env.local` / `docker/.env.docker`. Watch `oke dev` (`AI … — waiting…` →
132
+ `ready`). If status stays `loading` forever on an old stack that still uses
133
+ `--models-preset`, regenerate `docker/` and recreate AI. Newer Hub tags use
134
+ CNCF weight layers — the entrypoint falls back when native `llama download`
135
+ cannot find `vnd.docker.ai.gguf.v3`. Do not mount random GGUF files. Never put
136
+ `LLAMA_ARG_MODELS_PRESET` / `LLAMA_ARG_DOCKER_REPO` in the container env.
137
+
138
+ </Accordion>
139
+ </Accordions>
140
+
141
+ ## Learn more
142
+
143
+ - [AI](/docs/elements/ai) — `fx.ask`, prompts, drivers
144
+ - [Ollama](/docs/recipes/ollama) — fully supported alternative
145
+ - [Environment variables](/docs/reference/environment-variables) — `OKE_AI_*`
146
+
147
+ ## Next
148
+
149
+ <Cards>
150
+ <Card title="Ollama" description="Library UX alternative." href="/docs/recipes/ollama" />
151
+ <Card title="vLLM" description="Production GPU serving." href="/docs/recipes/vllm" />
152
+ <Card title="AI" description="Prompts, models, guardrails." href="/docs/elements/ai" />
153
+ </Cards>
@@ -0,0 +1,136 @@
1
+ ---
2
+ title: "Mailpit"
3
+ description: "Local SMTP catcher for channel.email — SMTP_URL on :1025, UI on :8025, MAILPIT_UI_URL."
4
+ icon: "Mail"
5
+ source: "docs/spec/unified-theory.md"
6
+ ---
7
+
8
+ Mailpit is the docker-mode email catcher — every message the `smtp` channel driver sends
9
+ lands in a web UI instead of a real inbox. Pin it as `images["channel.email"]` when
10
+ `drivers.channel.email.docker` is `smtp`.
11
+
12
+ <Callout title="The one rule">
13
+ Docker ≈ prod for the **SMTP protocol** — same `SMTP_URL` shape. Mailpit is not a production MTA;
14
+ swap the image / URL for a real relay when you leave local docker.
15
+ </Callout>
16
+
17
+ ## Quick start
18
+
19
+ <Steps>
20
+
21
+ <Step>
22
+ ### Pin the image (templates already do)
23
+
24
+ ```typescript title="oke.config.ts"
25
+ drivers: {
26
+ channel: {
27
+ email: { local: "console", docker: "smtp", test: "console", prod: "smtp" },
28
+ },
29
+ },
30
+ images: {
31
+ "channel.email": "axllent/mailpit:v1.22.3",
32
+ },
33
+ ```
34
+
35
+ </Step>
36
+
37
+ <Step>
38
+ ### Connect the smtp driver
39
+
40
+ ```bash title="docker/.env.docker (written by oke docker)"
41
+ SMTP_URL=smtp://127.0.0.1:1025
42
+ MAILPIT_UI_URL=http://127.0.0.1:8025
43
+ ```
44
+
45
+ No `SMTP_USER` / `SMTP_PASSWORD` required for Mailpit's open local listener.
46
+
47
+ </Step>
48
+
49
+ <Step>
50
+ ### Send and inspect
51
+
52
+ Trigger any flow that uses `fx.email` / channel email. Open `MAILPIT_UI_URL` (port
53
+ **8025**) to read the caught message — subject, body, headers.
54
+
55
+ </Step>
56
+
57
+ </Steps>
58
+
59
+ ## Required env
60
+
61
+ | Variable | Required? | Meaning |
62
+ | ----------------------------- | -------------------------- | ------------------------------------------------------- |
63
+ | `SMTP_URL` | **Yes** (app, smtp driver) | `smtp://host:1025` — boot fails without it |
64
+ | `OKE_CHANNEL_EMAIL_URL` | Alternative | Same role as `SMTP_URL` |
65
+ | `MAILPIT_UI_URL` | Written by stack | Browser UI — not read by the smtp driver |
66
+ | `SMTP_USER` / `SMTP_PASSWORD` | No for Mailpit | Used when pointing `SMTP_URL` at an authenticated relay |
67
+
68
+ ## Data and backup
69
+
70
+ Mailpit declares **no volume**. Caught messages live in container memory/disk for that
71
+ instance only.
72
+
73
+ **Backup means:** nothing durable to back up — this is a catcher. For forensic copies,
74
+ export from the UI before recreate. Production mail never uses this image as the store of
75
+ record.
76
+
77
+ ## Production note
78
+
79
+ Templates set `prod: "smtp"` but still pin Mailpit in `images` for docker. In real prod,
80
+ point `SMTP_URL` at your provider and set `SMTP_USER` / `SMTP_PASSWORD` as needed.
81
+
82
+ Leaving Mailpit reachable on a public host exposes an open relay UI — bind to the compose
83
+ network only.
84
+
85
+ Healthcheck probes `http://127.0.0.1:8025/api/v1/info` — UI down ⇒ service unhealthy even
86
+ if SMTP still accepts mail.
87
+
88
+ ## What the recipe configures
89
+
90
+ | Field | Value |
91
+ | -------------- | --------------------------------------------------- |
92
+ | SMTP port | `1025` (published) |
93
+ | Extra port | `8025` → UI |
94
+ | Healthcheck | `wget` against `/api/v1/info`, every 5s, 10 retries |
95
+ | Connection URL | `smtp://host:1025` |
96
+
97
+ ## Troubleshooting
98
+
99
+ <Accordions>
100
+ <Accordion title="oke boot: smtp driver needs SMTP_URL">
101
+
102
+ The smtp binder refuses to start without `SMTP_URL` (or `OKE_CHANNEL_EMAIL_URL`). In
103
+ docker mode the error asks whether `oke dev -d` wrote `docker/.env.docker`. Also rejects
104
+ non-`smtp://` schemes: `oke boot: SMTP_URL must use smtp://`.
105
+
106
+ </Accordion>
107
+ <Accordion title="Messages send but UI is empty">
108
+
109
+ Wrong UI port, or you opened a different instance's Mailpit. Confirm `MAILPIT_UI_URL`
110
+ matches the published `8025` mapping for **this** compose project (instance-id offsets
111
+ shift the host port).
112
+
113
+ </Accordion>
114
+ </Accordions>
115
+
116
+ ## Learn more
117
+
118
+ - [Channel](/docs/elements/channel) — email delivery physics
119
+ - [Environment variables](/docs/reference/environment-variables) — `SMTP_*` map
120
+ - [Configuration](/docs/reference/configuration) — default `channel.email` image pin
121
+
122
+ ## Next
123
+
124
+ <Cards>
125
+ <Card
126
+ title="RustFS"
127
+ description="S3-compatible files for the same stack."
128
+ href="/docs/recipes/rustfs"
129
+ />
130
+ <Card
131
+ title="OpenBao"
132
+ description="Vault recipe for docker/prod secrets."
133
+ href="/docs/recipes/openbao"
134
+ />
135
+ <Card title="Channel" description="How fx.email reaches humans." href="/docs/elements/channel" />
136
+ </Cards>