okengine 0.9.1 → 0.10.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 (166) 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-controls.test.ts +107 -0
  66. package/src/cli/dev-controls.ts +354 -0
  67. package/src/cli/dev-schema-sync.ts +12 -4
  68. package/src/cli/dev.test.ts +50 -30
  69. package/src/cli/dev.ts +607 -66
  70. package/src/cli/drizzle-env.test.ts +40 -6
  71. package/src/cli/drizzle-env.ts +22 -1
  72. package/src/cli/ensure-drizzle-config.ts +6 -4
  73. package/src/cli/hero-meta.test.ts +13 -1
  74. package/src/cli/hero-meta.ts +28 -6
  75. package/src/cli/index.ts +5 -0
  76. package/src/cli/registry.ts +24 -0
  77. package/src/cli/replay.test.ts +105 -0
  78. package/src/cli/replay.ts +206 -0
  79. package/src/compiler/extract.ts +3 -3
  80. package/src/config/index.ts +3 -3
  81. package/src/console/server/claim.ts +3 -1
  82. package/src/console/ui/dist/assets/{index-ClX0ZCe5.js → index-D-9KFce5.js} +2 -2
  83. package/src/console/ui/dist/assets/panel-overview-CW0CQEc2.js +1 -0
  84. package/src/console/ui/dist/assets/panel-runs-BxuyDI3x.js +1 -0
  85. package/src/console/ui/dist/assets/{panel-signals-Dsxdu_AR.js → panel-signals-BXofbj7v.js} +1 -1
  86. package/src/console/ui/dist/assets/{panel-store-hy7O8HOs.js → panel-store-B8mH-arU.js} +1 -1
  87. package/src/console/ui/dist/assets/{panel-traces-CBiAe5go.js → panel-traces-DSA-ZWnf.js} +1 -1
  88. package/src/console/ui/dist/index.html +1 -1
  89. package/src/console/ui/overview/slo.ts +31 -3
  90. package/src/console/ui/overview/types.ts +6 -0
  91. package/src/console/ui/runs/errors.test.ts +53 -0
  92. package/src/console/ui/runs/errors.ts +88 -0
  93. package/src/console/ui/runs/index.ts +8 -0
  94. package/src/console/ui/runs/search.ts +16 -0
  95. package/src/console/ui/shell/panels/runs/RunsPanel.tsx +72 -1
  96. package/src/docker/ai-model-status.test.ts +101 -0
  97. package/src/docker/ai-model-status.ts +276 -0
  98. package/src/docker/compose-health.test.ts +115 -0
  99. package/src/docker/compose-health.ts +234 -0
  100. package/src/docker/compose.ts +24 -8
  101. package/src/docker/derive.ts +35 -9
  102. package/src/docker/docker.test.ts +304 -12
  103. package/src/docker/helpers.ts +56 -0
  104. package/src/docker/index.ts +52 -0
  105. package/src/docker/ollama-pull.ts +232 -0
  106. package/src/docker/recipes/cockroach.ts +24 -0
  107. package/src/docker/recipes/index.ts +32 -0
  108. package/src/docker/recipes/llama-cpp.ts +273 -0
  109. package/src/docker/recipes/ollama.ts +19 -23
  110. package/src/docker/recipes/pgdog.ts +4 -1
  111. package/src/docker/recipes/postgres.ts +5 -18
  112. package/src/docker/recipes/sglang.ts +55 -0
  113. package/src/docker/recipes/supabase.ts +21 -0
  114. package/src/docker/recipes/timescale.ts +18 -0
  115. package/src/docker/recipes/vllm.ts +44 -0
  116. package/src/docker/recipes/yugabyte.ts +23 -0
  117. package/src/docker/stack-id.test.ts +22 -0
  118. package/src/docker/stack-id.ts +38 -4
  119. package/src/docker/types.ts +15 -0
  120. package/src/drivers/ai-ollama.integration.test.ts +7 -4
  121. package/src/drivers/signal-engine.ts +10 -0
  122. package/src/drivers/signal-postgres.ts +24 -2
  123. package/src/drivers/signal-types.ts +10 -0
  124. package/src/elements/channel/fallback.test.ts +91 -0
  125. package/src/elements/channel/hard-bounce.test.ts +82 -0
  126. package/src/elements/channel/injection.test.ts +153 -0
  127. package/src/elements/channel/locale-ar.test.ts +102 -0
  128. package/src/elements/channel/otp-delivery.ts +5 -2
  129. package/src/elements/channel/receipts-ledger.test.ts +100 -0
  130. package/src/elements/channel/runtime.ts +24 -1
  131. package/src/elements/channel/test-helpers.ts +60 -0
  132. package/src/elements/channel/whatsapp-compliance.test.ts +124 -0
  133. package/src/elements/store/cache.ts +1 -1
  134. package/src/elements/store/emit-drizzle.ts +43 -9
  135. package/src/elements/store/runtime.ts +1 -1
  136. package/src/elements/store/schema-decl.test.ts +2 -0
  137. package/src/elements/store/schema-decl.ts +15 -0
  138. package/src/elements/store/seed.ts +1 -1
  139. package/src/index.ts +1 -0
  140. package/src/kernel/app.ts +117 -10
  141. package/src/kernel/boot-bind/signal.ts +6 -2
  142. package/src/kernel/boot-bind/store.ts +2 -1
  143. package/src/kernel/boot.test.ts +6 -5
  144. package/src/kernel/boot.ts +8 -3
  145. package/src/kernel/compensate.test.ts +131 -0
  146. package/src/kernel/correlation.test.ts +108 -0
  147. package/src/kernel/effect-timing.test.ts +82 -0
  148. package/src/kernel/flow.ts +38 -0
  149. package/src/kernel/fx-runs.test.ts +49 -0
  150. package/src/kernel/fx.test.ts +14 -0
  151. package/src/kernel/fx.ts +111 -1
  152. package/src/manifest/types.ts +8 -2
  153. package/src/runs/collect.ts +3 -0
  154. package/src/runs/export-otlp.ts +64 -0
  155. package/src/runs/index.ts +16 -0
  156. package/src/runs/parquet.ts +9 -0
  157. package/src/runs/types.ts +5 -0
  158. package/src/runs/window.test.ts +68 -0
  159. package/src/runs/window.ts +148 -0
  160. package/src/runtime/boot-warn.test.ts +45 -0
  161. package/src/runtime/boot-warn.ts +17 -0
  162. package/src/runtime/dev-request-log.ts +5 -5
  163. package/src/term.test.ts +95 -10
  164. package/src/term.ts +369 -14
  165. package/src/console/ui/dist/assets/panel-overview-DONDzBd2.js +0 -1
  166. package/src/console/ui/dist/assets/panel-runs-D5zf-D9c.js +0 -1
@@ -0,0 +1,139 @@
1
+ ---
2
+ title: "Meilisearch"
3
+ description: "Full-text store.index image — OKE_STORE_INDEX_URL, OKE_STORE_INDEX_KEY / MEILI_MASTER_KEY, /meili_data volume."
4
+ icon: "Search"
5
+ source: "docs/spec/unified-theory.md"
6
+ ---
7
+
8
+ Meilisearch backs the `meilisearch` index driver — typo-tolerant full-text search over
9
+ HTTP. Pin `images["store.index"]` and set `drivers.store.index` to `meilisearch` where
10
+ you need it (index does **not** auto-promote in docker).
11
+
12
+ <Callout title="The one rule">
13
+ Index stays `memory` in every environment until you set the driver map explicitly — there is no
14
+ silent docker fallback to Meilisearch or pgvector.
15
+ </Callout>
16
+
17
+ ## Quick start
18
+
19
+ <Steps>
20
+
21
+ <Step>
22
+ ### Declare driver + image
23
+
24
+ ```typescript title="oke.config.ts"
25
+ drivers: {
26
+ store: {
27
+ index: { local: "memory", docker: "meilisearch", prod: "meilisearch" },
28
+ },
29
+ },
30
+ images: {
31
+ "store.index": "getmeili/meilisearch:v1.14",
32
+ },
33
+ ```
34
+
35
+ </Step>
36
+
37
+ <Step>
38
+ ### Keys written by oke docker
39
+
40
+ ```bash title="docker/.env.docker"
41
+ OKE_STORE_INDEX_URL=http://127.0.0.1:7700
42
+ OKE_STORE_INDEX_KEY=… # also accepted as MEILI_MASTER_KEY
43
+ ```
44
+
45
+ Recipe injects into the container:
46
+
47
+ - `MEILI_MASTER_KEY=${OKE_STORE_INDEX_KEY}`
48
+ - `MEILI_ENV=${OKE_MEILI_ENV:-production}`
49
+ - `MEILI_NO_ANALYTICS=true`
50
+
51
+ </Step>
52
+
53
+ <Step>
54
+ ### Search from a flow
55
+
56
+ Use `store.index(…)` without `{ dims }` for full-text (vector dims select pgvector /
57
+ libsql). Writes wait on Meilisearch tasks before returning.
58
+
59
+ </Step>
60
+
61
+ </Steps>
62
+
63
+ ## Required env
64
+
65
+ | Variable | Required? | Meaning |
66
+ | --------------------- | -------------------- | ------------------------------------------------ |
67
+ | `OKE_STORE_INDEX_URL` | **Yes** | Base URL — boot fails without it |
68
+ | `OKE_STORE_INDEX_KEY` | **Yes** (production) | Master/API key; falls back to `MEILI_MASTER_KEY` |
69
+ | `MEILI_MASTER_KEY` | Alternate | Same secret, container-side name |
70
+ | `OKE_MEILI_ENV` | Optional | Passed as `MEILI_ENV` (default `production`) |
71
+
72
+ ## Data and backup
73
+
74
+ | Volume | Path | What it stores |
75
+ | ------------------ | ------------- | ---------------------------- |
76
+ | `store-index-data` | `/meili_data` | Indexes, documents, settings |
77
+
78
+ **Backup means:** snapshot/restore that named volume (or Meilisearch dumps). Recreating
79
+ the container **keeps** data if the volume remains; deleting the volume wipes every index.
80
+
81
+ The master key is **not** inside the volume — it lives in `.env.docker` / secrets.
82
+ Losing the key without a backup blocks admin API access even if data remains.
83
+
84
+ ## Production note
85
+
86
+ Always set a non-empty `OKE_STORE_INDEX_KEY` when `MEILI_ENV=production` — Meilisearch
87
+ refuses insecure production mode without a master key. Prefer network isolation (compose
88
+ network only) plus the key; do not publish `:7700` on the public internet without auth.
89
+
90
+ Vector `ai.embed` into a meilisearch index fails loud — embeddings need `pgvector` /
91
+ `libsql`, not FTS. See [Store · Index](/docs/elements/store#index).
92
+
93
+ ## What the recipe configures
94
+
95
+ | Field | Value |
96
+ | -------------- | ---------------------------------------------- |
97
+ | Container port | `7700` |
98
+ | Healthcheck | `wget` against `/health`, every 5s, 12 retries |
99
+ | Connection URL | `http://host:7700` |
100
+
101
+ ## Troubleshooting
102
+
103
+ <Accordions>
104
+ <Accordion title="oke boot: meilisearch index needs OKE_STORE_INDEX_URL">
105
+
106
+ No silent memory fallback. Re-run `oke dev --docker` or export `OKE_STORE_INDEX_URL`.
107
+ Docker-mode wording asks whether `docker/.env.docker` was written.
108
+
109
+ </Accordion>
110
+ <Accordion title="meilisearch index: GET/POST … failed (403) — or master key errors">
111
+
112
+ `OKE_STORE_INDEX_KEY` does not match `MEILI_MASTER_KEY` inside the container, or the key
113
+ was rotated without updating the app env. Align both sides and restart. Unreachable host
114
+ surfaces as `meilisearch index: unreachable at <url> — …`.
115
+
116
+ </Accordion>
117
+ </Accordions>
118
+
119
+ ## Learn more
120
+
121
+ - [Store · Index](/docs/elements/store#index) — FTS vs vector drivers
122
+ - [Environment variables](/docs/reference/environment-variables) — index URL / key
123
+ - [Configuration](/docs/reference/configuration) — `drivers.store.index` union
124
+
125
+ ## Next
126
+
127
+ <Cards>
128
+ <Card
129
+ title="llama.cpp"
130
+ description="Default local AI for the same stack."
131
+ href="/docs/recipes/llama-cpp"
132
+ />
133
+ <Card
134
+ title="Postgres"
135
+ description="pgvector alternative for embeddings."
136
+ href="/docs/recipes/postgres"
137
+ />
138
+ <Card title="Store" description="Index facet API." href="/docs/elements/store" />
139
+ </Cards>
@@ -0,0 +1,31 @@
1
+ {
2
+ "title": "Recipes",
3
+ "icon": "Container",
4
+ "pages": [
5
+ "index",
6
+ "---SQL---",
7
+ "postgres",
8
+ "supabase-docker",
9
+ "cockroachdb",
10
+ "yugabytedb",
11
+ "timescale",
12
+ "---KV---",
13
+ "redis",
14
+ "valkey",
15
+ "dragonfly",
16
+ "---Proxy---",
17
+ "caddy",
18
+ "traefik",
19
+ "---Services---",
20
+ "pgdog",
21
+ "rustfs",
22
+ "mailpit",
23
+ "openbao",
24
+ "meilisearch",
25
+ "---AI---",
26
+ "llama-cpp",
27
+ "ollama",
28
+ "vllm",
29
+ "sglang"
30
+ ]
31
+ }
@@ -0,0 +1,142 @@
1
+ ---
2
+ title: "Ollama"
3
+ description: "Fully supported local AI alternative — OKE_AI_URL, pinned ≥ 0.17.1, loopback-only publish, library models only."
4
+ icon: "Bot"
5
+ source: "docs/spec/unified-theory.md"
6
+ ---
7
+
8
+ Ollama remains a **fully supported** local model server (not deprecated). The
9
+ default local pin is [llama.cpp](/docs/recipes/llama-cpp); choose Ollama when you
10
+ want its library pull UX. Pin `images.ai` and set `drivers.ai` to `ollama`.
11
+
12
+ <Callout title="The one rule">
13
+ Pin ≥ `0.17.1` (never `latest`), publish only on loopback, and load models only from Ollama’s
14
+ library — never arbitrary untrusted GGUF via `/api/create`.
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: "ollama", docker: "ollama", test: "mock" }, // prod: set deliberately
27
+ },
28
+ images: {
29
+ ai: "ollama/ollama:0.32.6", // never :latest — floor 0.17.1 (CVE-2026-7482)
30
+ },
31
+ ```
32
+
33
+ Or run `oke ai setup --provider ollama --yes`.
34
+
35
+ </Step>
36
+
37
+ <Step>
38
+ ### Env written by the stack
39
+
40
+ ```bash title="docker/.env.docker"
41
+ OKE_AI_URL=http://127.0.0.1:11434
42
+ OKE_AI_MODEL=qwen3.5:9b
43
+ ```
44
+
45
+ Host publish is `127.0.0.1:11434:11434`. Inside the container
46
+ `OLLAMA_HOST=0.0.0.0:11434` so Docker networking works.
47
+
48
+ </Step>
49
+
50
+ <Step>
51
+ ### Boot pulls against the container API
52
+
53
+ After compose is healthy, `oke dev -d` shows `OKE_AI_MODEL` in the hero / Docker
54
+ summary, POSTs `/api/pull` to **this container’s**
55
+ loopback URL when the recipe is Ollama — never a host `ollama` CLI.
56
+
57
+ </Step>
58
+
59
+ </Steps>
60
+
61
+ ## Security (hard requirements)
62
+
63
+ | Control | Requirement |
64
+ | -------------- | -------------------------------------------------------------------------- |
65
+ | Host publish | `127.0.0.1:11434:11434` only — never `0.0.0.0` |
66
+ | Image pin | Explicit tag ≥ **0.17.1** (CVE-2026-7482); never `latest` |
67
+ | Model source | [Ollama library](https://ollama.com/library) only |
68
+ | Untrusted GGUF | **Forbidden** — `/api/create` with crafted files is the CVE-2026-7482 path |
69
+
70
+ Ollama and llama.cpp share GGUF-parser risk; Ollama also has its own Go loader
71
+ CVE. Network exposure of `:11434` turns that into a remote attack.
72
+
73
+ <Callout type="warn" title="Host `ollama` CLI is unrelated">
74
+ A host `ollama pull` often talks to a separately installed daemon. Trust boot ensure against
75
+ `OKE_AI_URL`.
76
+ </Callout>
77
+
78
+ ## Required env
79
+
80
+ | Variable | Required? | Meaning |
81
+ | -------------------------------------------- | ----------------- | ------------------------------------------------------------------ |
82
+ | `OKE_AI_URL` | **Yes** in docker | Base URL — `oke boot: ollama driver needs OKE_AI_URL` when missing |
83
+ | `OKE_AI_MODEL` | Recommended | Model tag to pull/serve (default `qwen3.5:9b`) |
84
+ | `OLLAMA_HOST` | Set by recipe | `0.0.0.0:11434` inside the container |
85
+ | `OKE_AI_VISION_MODEL` / `OKE_AI_EMBED_MODEL` | Optional | Written by `oke ai setup` for logical models |
86
+
87
+ ## Data and backup
88
+
89
+ | Volume | Path | What it stores |
90
+ | --------- | --------------- | -------------------------------------- |
91
+ | `ai-data` | `/root/.ollama` | Pulled model weights + Ollama metadata |
92
+
93
+ ## Production note
94
+
95
+ Local Ollama is for docker/dev (and air-gapped prod you deliberately choose).
96
+ For multi-user GPU self-hosting see [vLLM](/docs/recipes/vllm) /
97
+ [SGLang](/docs/recipes/sglang). Cloud drivers stay separate.
98
+
99
+ PII egress rules treat `ollama` as local — see [AI · PII](/docs/elements/ai).
100
+
101
+ ## What the recipe configures
102
+
103
+ | Field | Value |
104
+ | -------------- | ---------------------------------------------- |
105
+ | Container port | `11434` |
106
+ | Host publish | `127.0.0.1:11434:11434` |
107
+ | Entrypoint | Image default (`ollama serve`) |
108
+ | Healthcheck | `ollama list`, every 5s, 24 retries, 10s start |
109
+ | Connection URL | `http://host:11434` |
110
+ | Model pull | Host-side `POST /api/pull` after healthy |
111
+
112
+ ## Troubleshooting
113
+
114
+ <Accordions>
115
+ <Accordion title="oke boot: ollama driver needs OKE_AI_URL">
116
+
117
+ Docker mode expects `OKE_AI_URL` from `docker/.env.docker` when `images.ai` is pinned.
118
+ Locally the default is `http://127.0.0.1:11434`. Prefer `oke ai setup` to wire everything
119
+ in one pass.
120
+
121
+ </Accordion>
122
+ <Accordion title="Model missing / I already ran ollama pull on the host">
123
+
124
+ The host CLI likely filled a **different** Ollama. Check `GET $OKE_AI_URL/api/tags`,
125
+ then recreate with `oke dev -d`.
126
+
127
+ </Accordion>
128
+ </Accordions>
129
+
130
+ ## Learn more
131
+
132
+ - [llama.cpp](/docs/recipes/llama-cpp) — default local AI
133
+ - [AI](/docs/elements/ai) — `fx.ask`, prompts, evals
134
+ - [CLI · oke ai setup](/docs/reference/cli) — non-interactive provider wiring
135
+
136
+ ## Next
137
+
138
+ <Cards>
139
+ <Card title="llama.cpp" description="Default local AI." href="/docs/recipes/llama-cpp" />
140
+ <Card title="Meilisearch" description="Full-text index peer." href="/docs/recipes/meilisearch" />
141
+ <Card title="AI" description="Prompts, models, guardrails." href="/docs/elements/ai" />
142
+ </Cards>
@@ -0,0 +1,141 @@
1
+ ---
2
+ title: "OpenBao"
3
+ description: "Durable single-node vault — Raft under /openbao/file, OKE_VAULT_URL / OKE_VAULT_TOKEN, host unseal key SPOF."
4
+ icon: "KeyRound"
5
+ source: "docs/spec/unified-theory.md"
6
+ ---
7
+
8
+ OpenBao is the docker/prod Vault driver target — a real secrets service with Raft
9
+ storage, **not** an in-memory `-dev` server. Templates pin
10
+ `vault: "openbao/openbao:2.6.1"` and `drivers.vault.docker/prod: "openbao"`.
11
+
12
+ <Callout title="The one rule">
13
+ Never run OpenBao `-dev` in docker/prod. Data belongs on the Raft volume; init + unseal stay on
14
+ the host under `.oke/openbao/`.
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
+ vault: { local: "env", docker: "openbao", test: "memory", prod: "openbao" },
27
+ },
28
+ images: {
29
+ vault: "openbao/openbao:2.6.1",
30
+ },
31
+ ```
32
+
33
+ </Step>
34
+
35
+ <Step>
36
+ ### First boot is automatic
37
+
38
+ `oke dev --docker` initializes OpenBao (Shamir 1-of-1), unseals it, writes a
39
+ least-privilege policy for **declared** secrets only, and mints an app token. You do not
40
+ run raw `bao` commands.
41
+
42
+ </Step>
43
+
44
+ <Step>
45
+ ### App sees only the least-privilege token
46
+
47
+ ```bash title="docker/.env.docker"
48
+ OKE_VAULT_URL=http://127.0.0.1:8200
49
+ OKE_VAULT_TOKEN=… # app token — not the root token
50
+ OKE_VAULT_MOUNT=secret # KV v2 mount (default)
51
+ ```
52
+
53
+ </Step>
54
+
55
+ </Steps>
56
+
57
+ ## Required env
58
+
59
+ | Variable | Who holds it | Meaning |
60
+ | ------------------ | ------------------- | ---------------------------------------- |
61
+ | `OKE_VAULT_URL` | App / stack | OpenBao base URL |
62
+ | `OKE_VAULT_TOKEN` | App (`.env.docker`) | Least-privilege app token |
63
+ | `OKE_VAULT_MOUNT` | Optional | KV v2 mount path (default `"secret"`) |
64
+ | `BAO_LOCAL_CONFIG` | Container (recipe) | Listener + Raft JSON — set by the recipe |
65
+
66
+ Host-only material (mode `0600`, gitignored) — **not** in compose YAML:
67
+
68
+ | File | Holds |
69
+ | ------------------------- | ---------------------------------------- |
70
+ | `.oke/openbao/unseal.key` | Single unseal share |
71
+ | `.oke/openbao/root.token` | Root token (CLI policy sync / mint only) |
72
+ | `.oke/openbao/app.token` | Same least-privilege token the app uses |
73
+
74
+ ## Data and backup
75
+
76
+ | Volume | Path | What it stores |
77
+ | ------------ | --------------- | ---------------------------- |
78
+ | `vault-data` | `/openbao/file` | Raft storage (node id `oke`) |
79
+
80
+ Recipe note: Raft path is `/openbao/file` (the stock entrypoint chowns that directory).
81
+ Healthcheck hits `/v1/sys/init` — **200 even while sealed**; bootstrap owns unseal.
82
+
83
+ **Backup means two things:**
84
+
85
+ 1. The Raft volume (`/openbao/file`) — encrypted secret store
86
+ 2. `.oke/openbao/unseal.key` on the host — without it the volume is unrecoverable
87
+
88
+ ## Production note
89
+
90
+ <Callout title="Single point of failure — read this once" type="warn">
91
+ Back up `.oke/openbao/unseal.key` to a separate safe location. Losing it means losing every secret
92
+ permanently, with no recovery. Restated from [Vault ·
93
+ OpenBao](/docs/elements/vault#openbao-in-docker-and-prod).
94
+ </Callout>
95
+
96
+ Self-hosted single-node, Shamir 1-of-1, no cloud KMS — by design. If OpenBao is
97
+ initialized but the key is gone, boot fails with an explicit permanent-loss error; it
98
+ never silently starts empty.
99
+
100
+ ## What the recipe configures
101
+
102
+ | Field | Value |
103
+ | -------------- | -------------------------------------------------------------------------------------- |
104
+ | Container port | `8200` |
105
+ | Command | `server` (with `BAO_LOCAL_CONFIG`) |
106
+ | TLS | Disabled on the listener for compose (`tls_disable: true`) — rely on network isolation |
107
+ | Healthcheck | `wget` `/v1/sys/init`, every 5s, 12 retries |
108
+
109
+ ## Troubleshooting
110
+
111
+ <Accordions>
112
+ <Accordion title="openbao vault: GET … failed (… ) — sealed or unauthorized?">
113
+
114
+ `OpenBaoUnavailableError` — vault sealed, wrong token, or unreachable. Re-run
115
+ `oke dev --docker` so bootstrap unseals with the host key. If the vault is initialized
116
+ but `.oke/openbao/unseal.key` is missing, restore the key from backup.
117
+
118
+ </Accordion>
119
+ <Accordion title="Permanent-loss / initialized but key missing">
120
+
121
+ Starting empty would look like “no secrets.” The CLI refuses that path. Restore
122
+ `unseal.key` or treat the Raft volume as destroyed and re-init only if you accept wiping
123
+ secrets.
124
+
125
+ </Accordion>
126
+ </Accordions>
127
+
128
+ ## Learn more
129
+
130
+ - [Vault · OpenBao in docker and prod](/docs/elements/vault#openbao-in-docker-and-prod) —
131
+ full bootstrap, key table, SPOF callout
132
+ - [Errors](/docs/reference/errors) — `OpenBaoUnavailableError`, `OpenBaoBootstrapError`
133
+ - [CLI · oke vault](/docs/reference/cli) — `set` / `list` / `import`
134
+
135
+ ## Next
136
+
137
+ <Cards>
138
+ <Card title="Vault" description="Resolution chain and rotation." href="/docs/elements/vault" />
139
+ <Card title="Mailpit" description="Local email catcher." href="/docs/recipes/mailpit" />
140
+ <Card title="RustFS" description="S3-compatible files." href="/docs/recipes/rustfs" />
141
+ </Cards>
@@ -0,0 +1,137 @@
1
+ ---
2
+ title: "PgDog"
3
+ description: "Postgres wire-protocol pooler — transaction mode on :6432, generated pgdog.toml / users.toml, DATABASE_URL rewrite."
4
+ icon: "Package"
5
+ source: "docs/spec/unified-theory.md"
6
+ ---
7
+
8
+ Bun.SQL defaults to **10** connections per process. Scale to several app instances and
9
+ `N × pool` can exceed Postgres `max_connections`. PgDog sits in front of `store.sql` as a
10
+ transaction-pooling proxy — same wire protocol, no app code changes.
11
+
12
+ <Callout title="The one rule">
13
+ Pin `pgdog` alongside `store.sql` and `DATABASE_URL` automatically points at the pooler on `:6432`
14
+ instead of Postgres directly — Bun.SQL and Drizzle see no difference.
15
+ </Callout>
16
+
17
+ ## Quick start
18
+
19
+ <Steps>
20
+
21
+ <Step>
22
+ ### Pin both images
23
+
24
+ ```typescript title="oke.config.ts"
25
+ images: {
26
+ "store.sql": "postgres:18-alpine",
27
+ pgdog: "ghcr.io/pgdogdev/pgdog:v0.1.51",
28
+ },
29
+ ```
30
+
31
+ </Step>
32
+
33
+ <Step>
34
+ ### Derive compose
35
+
36
+ ```bash
37
+ oke docker
38
+ ```
39
+
40
+ Writes `./pgdog.toml` (listen + one primary database) and `./users.toml` (same
41
+ user/password/database as Postgres). The pooler waits on `store-sql` health before start.
42
+
43
+ </Step>
44
+
45
+ <Step>
46
+ ### Confirm the rewrite
47
+
48
+ ```bash
49
+ echo "$DATABASE_URL"
50
+ # …@host:6432/db ← pooler, not :5432
51
+ echo "$OKE_STORE_SQL_URL" # still the direct Postgres URL when you need it
52
+ ```
53
+
54
+ </Step>
55
+
56
+ </Steps>
57
+
58
+ ## Required env
59
+
60
+ | Variable | Role |
61
+ | ---------------------------------------- | ---------------------------------------------------------------- |
62
+ | `OKE_STORE_SQL_USER` / `PASSWORD` / `DB` | Copied into `users.toml` for client + server auth |
63
+ | `DATABASE_URL` | Rewritten to the pooler URL (`:6432`) when PgDog is in the stack |
64
+ | `OKE_PGDOG_URL` | Same value as pooled `DATABASE_URL` when both are present |
65
+ | `OKE_STORE_SQL_URL` | Direct Postgres host — bypass the pooler for admin / migrations |
66
+
67
+ PgDog itself has no separate credential env — it mounts the generated TOML files read-only.
68
+
69
+ ## Data and backup
70
+
71
+ | Mount | What it is |
72
+ | ----------------------------------- | ------------------------------------------------------------------ |
73
+ | `./pgdog.toml:/pgdog/pgdog.toml:ro` | Generated listen + `[[databases]]` (host `store-sql`, port `5432`) |
74
+ | `./users.toml:/pgdog/users.toml:ro` | Generated `[[users]]` credentials |
75
+
76
+ **No database state lives in the PgDog container.** Backing up PgDog means keeping those
77
+ two config files (they are regenerated by `oke docker`). Durable data stays on the
78
+ [Postgres](/docs/recipes/postgres) volume / dump.
79
+
80
+ ## Production note
81
+
82
+ Pooling mode is `transaction` — set explicitly (also PgDog's upstream default). Naive
83
+ poolers can leak session state (`SET`, RLS vars, `LISTEN`/`NOTIFY`) across clients;
84
+ PgDog re-applies that state per transaction.
85
+
86
+ **Do not stack** PgDog in front of a managed pooler (Neon `-pooler`, Supabase Supavisor
87
+ `:6543`). Pick one pooler. Use `OKE_STORE_SQL_URL` (direct) for migrations and anything
88
+ that needs session features.
89
+
90
+ Read-replica routing (`BEGIN READ ONLY` → replica) is documented readiness in PgDog —
91
+ not wired into the generated stack yet.
92
+
93
+ ## What the recipe configures
94
+
95
+ | Field | Value |
96
+ | -------------- | ------------------------------------------------------- |
97
+ | Container port | `6432` |
98
+ | `dependsOn` | `store-sql` healthy |
99
+ | Healthcheck | `pg_isready -h 127.0.0.1 -p 6432`, every 5s, 12 retries |
100
+ | Connection URL | `postgres://user:pass@host:6432/db` |
101
+
102
+ ## Troubleshooting
103
+
104
+ <Accordions>
105
+ <Accordion title="pgdog unhealthy — pg_isready on :6432 fails">
106
+
107
+ Usually the backend is not ready, or `users.toml` credentials do not match Postgres.
108
+ Confirm `store-sql` is healthy first, then re-run `oke docker` so TOML matches
109
+ `OKE_STORE_SQL_*`. Logs often show auth failures against `store-sql:5432`.
110
+
111
+ </Accordion>
112
+ <Accordion title="Session features break through the pooler">
113
+
114
+ Transaction mode does not preserve session-scoped state the way a direct connection
115
+ does. Point migrations / `LISTEN` / session `SET` at `OKE_STORE_SQL_URL` (direct
116
+ Postgres), keep `DATABASE_URL` on the pooler for app traffic.
117
+
118
+ </Accordion>
119
+ </Accordions>
120
+
121
+ ## Learn more
122
+
123
+ - [Docker · PgDog pooling](/docs/deployment/docker#pgdog-pooling) — compose `-f` layer order
124
+ - [Postgres](/docs/recipes/postgres) — the backend PgDog fronts
125
+ - [Store · SQL](/docs/elements/store#sql) — “Connection pooling is infrastructure”
126
+
127
+ ## Next
128
+
129
+ <Cards>
130
+ <Card title="Postgres" description="The backend PgDog fronts." href="/docs/recipes/postgres" />
131
+ <Card
132
+ title="Supabase"
133
+ description="Also Postgres wire — same pooler applies."
134
+ href="/docs/recipes/supabase-docker"
135
+ />
136
+ <Card title="Caddy" description="TLS at the edge, once you need it." href="/docs/recipes/caddy" />
137
+ </Cards>