okengine 0.9.0 → 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 (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 +105 -14
  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/plugins/otp.mdx +58 -46
  14. package/site/content/docs/providers/azure-redis.mdx +85 -0
  15. package/site/content/docs/providers/cockroachdb.mdx +86 -0
  16. package/site/content/docs/providers/digitalocean-caching.mdx +88 -0
  17. package/site/content/docs/providers/dragonfly-cloud.mdx +82 -0
  18. package/site/content/docs/providers/elasticache.mdx +92 -0
  19. package/site/content/docs/providers/index.mdx +101 -0
  20. package/site/content/docs/providers/memorystore.mdx +89 -0
  21. package/site/content/docs/providers/meta.json +20 -0
  22. package/site/content/docs/providers/neon.mdx +115 -0
  23. package/site/content/docs/providers/redis-cloud.mdx +93 -0
  24. package/site/content/docs/providers/supabase.mdx +117 -0
  25. package/site/content/docs/providers/upstash.mdx +91 -0
  26. package/site/content/docs/providers/yugabytedb.mdx +95 -0
  27. package/site/content/docs/recipes/caddy.mdx +128 -0
  28. package/site/content/docs/recipes/cockroachdb.mdx +127 -0
  29. package/site/content/docs/recipes/dragonfly.mdx +125 -0
  30. package/site/content/docs/recipes/index.mdx +146 -0
  31. package/site/content/docs/recipes/llama-cpp.mdx +153 -0
  32. package/site/content/docs/recipes/mailpit.mdx +136 -0
  33. package/site/content/docs/recipes/meilisearch.mdx +139 -0
  34. package/site/content/docs/recipes/meta.json +31 -0
  35. package/site/content/docs/recipes/ollama.mdx +142 -0
  36. package/site/content/docs/recipes/openbao.mdx +141 -0
  37. package/site/content/docs/recipes/pgdog.mdx +137 -0
  38. package/site/content/docs/recipes/postgres.mdx +136 -0
  39. package/site/content/docs/recipes/redis.mdx +130 -0
  40. package/site/content/docs/recipes/rustfs.mdx +148 -0
  41. package/site/content/docs/recipes/sglang.mdx +105 -0
  42. package/site/content/docs/recipes/supabase-docker.mdx +145 -0
  43. package/site/content/docs/recipes/timescale.mdx +129 -0
  44. package/site/content/docs/recipes/traefik.mdx +136 -0
  45. package/site/content/docs/recipes/valkey.mdx +121 -0
  46. package/site/content/docs/recipes/vllm.mdx +106 -0
  47. package/site/content/docs/recipes/yugabytedb.mdx +135 -0
  48. package/site/content/docs/reference/cli.md +42 -6
  49. package/site/content/docs/reference/configuration.mdx +5 -3
  50. package/site/content/docs/reference/environment-variables.mdx +12 -12
  51. package/site/content/docs/reference/fx.mdx +38 -4
  52. package/src/auth/otp-capability.ts +14 -14
  53. package/src/cli/ai-setup/ai-setup.test.ts +32 -12
  54. package/src/cli/ai-setup/apply.ts +65 -23
  55. package/src/cli/ai-setup/catalog.ts +1316 -35
  56. package/src/cli/ai-setup/detect-ollama.ts +47 -0
  57. package/src/cli/ai-setup/index.ts +44 -7
  58. package/src/cli/ai-setup/prompts.ts +430 -486
  59. package/src/cli/ai-setup/recommend.test.ts +150 -50
  60. package/src/cli/ai-setup/recommend.ts +118 -101
  61. package/src/cli/ask-seed.test.ts +68 -0
  62. package/src/cli/ask-seed.ts +79 -0
  63. package/src/cli/db-seed-redact.test.ts +25 -0
  64. package/src/cli/db-seed.ts +36 -7
  65. package/src/cli/db.test.ts +1 -1
  66. package/src/cli/db.ts +2 -2
  67. package/src/cli/dev-controls.test.ts +107 -0
  68. package/src/cli/dev-controls.ts +354 -0
  69. package/src/cli/dev-schema-sync.ts +12 -4
  70. package/src/cli/dev.test.ts +50 -30
  71. package/src/cli/dev.ts +607 -66
  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/compiler/extract.ts +3 -3
  82. package/src/config/index.ts +3 -3
  83. package/src/console/server/claim.ts +3 -1
  84. package/src/console/ui/dist/assets/{index-ClX0ZCe5.js → index-D-9KFce5.js} +2 -2
  85. package/src/console/ui/dist/assets/panel-overview-CW0CQEc2.js +1 -0
  86. package/src/console/ui/dist/assets/panel-runs-BxuyDI3x.js +1 -0
  87. package/src/console/ui/dist/assets/{panel-signals-Dsxdu_AR.js → panel-signals-BXofbj7v.js} +1 -1
  88. package/src/console/ui/dist/assets/{panel-store-hy7O8HOs.js → panel-store-B8mH-arU.js} +1 -1
  89. package/src/console/ui/dist/assets/{panel-traces-CBiAe5go.js → panel-traces-DSA-ZWnf.js} +1 -1
  90. package/src/console/ui/dist/index.html +1 -1
  91. package/src/console/ui/overview/slo.ts +31 -3
  92. package/src/console/ui/overview/types.ts +6 -0
  93. package/src/console/ui/runs/errors.test.ts +53 -0
  94. package/src/console/ui/runs/errors.ts +88 -0
  95. package/src/console/ui/runs/index.ts +8 -0
  96. package/src/console/ui/runs/search.ts +16 -0
  97. package/src/console/ui/shell/panels/runs/RunsPanel.tsx +72 -1
  98. package/src/docker/ai-model-status.test.ts +101 -0
  99. package/src/docker/ai-model-status.ts +276 -0
  100. package/src/docker/compose-health.test.ts +115 -0
  101. package/src/docker/compose-health.ts +234 -0
  102. package/src/docker/compose.ts +24 -8
  103. package/src/docker/derive.ts +35 -9
  104. package/src/docker/docker.test.ts +304 -12
  105. package/src/docker/helpers.ts +56 -0
  106. package/src/docker/index.ts +52 -0
  107. package/src/docker/ollama-pull.ts +232 -0
  108. package/src/docker/recipes/cockroach.ts +24 -0
  109. package/src/docker/recipes/index.ts +32 -0
  110. package/src/docker/recipes/llama-cpp.ts +273 -0
  111. package/src/docker/recipes/ollama.ts +19 -23
  112. package/src/docker/recipes/pgdog.ts +4 -1
  113. package/src/docker/recipes/postgres.ts +5 -18
  114. package/src/docker/recipes/sglang.ts +55 -0
  115. package/src/docker/recipes/supabase.ts +21 -0
  116. package/src/docker/recipes/timescale.ts +18 -0
  117. package/src/docker/recipes/vllm.ts +44 -0
  118. package/src/docker/recipes/yugabyte.ts +23 -0
  119. package/src/docker/stack-id.test.ts +22 -0
  120. package/src/docker/stack-id.ts +38 -4
  121. package/src/docker/types.ts +15 -0
  122. package/src/drivers/ai-ollama.integration.test.ts +7 -4
  123. package/src/drivers/signal-engine.ts +10 -0
  124. package/src/drivers/signal-postgres.ts +24 -2
  125. package/src/drivers/signal-types.ts +10 -0
  126. package/src/elements/channel/fallback.test.ts +91 -0
  127. package/src/elements/channel/hard-bounce.test.ts +82 -0
  128. package/src/elements/channel/injection.test.ts +153 -0
  129. package/src/elements/channel/locale-ar.test.ts +102 -0
  130. package/src/elements/channel/otp-delivery.ts +5 -2
  131. package/src/elements/channel/receipts-ledger.test.ts +100 -0
  132. package/src/elements/channel/runtime.ts +24 -1
  133. package/src/elements/channel/test-helpers.ts +60 -0
  134. package/src/elements/channel/whatsapp-compliance.test.ts +124 -0
  135. package/src/elements/store/cache.ts +1 -1
  136. package/src/elements/store/emit-drizzle.ts +43 -9
  137. package/src/elements/store/runtime.ts +1 -1
  138. package/src/elements/store/schema-decl.test.ts +2 -0
  139. package/src/elements/store/schema-decl.ts +15 -0
  140. package/src/elements/store/seed.ts +1 -1
  141. package/src/index.ts +1 -0
  142. package/src/kernel/app.ts +118 -11
  143. package/src/kernel/boot-bind/signal.ts +6 -2
  144. package/src/kernel/boot-bind/store.ts +2 -1
  145. package/src/kernel/boot.test.ts +6 -5
  146. package/src/kernel/boot.ts +8 -3
  147. package/src/kernel/compensate.test.ts +131 -0
  148. package/src/kernel/correlation.test.ts +108 -0
  149. package/src/kernel/effect-timing.test.ts +82 -0
  150. package/src/kernel/flow.ts +38 -0
  151. package/src/kernel/fx-runs.test.ts +49 -0
  152. package/src/kernel/fx.test.ts +14 -0
  153. package/src/kernel/fx.ts +111 -1
  154. package/src/manifest/types.ts +8 -2
  155. package/src/plugins/auth-delivery.mailpit.integration.test.ts +1 -1
  156. package/src/plugins/auth-methods.security.test.ts +3 -3
  157. package/src/plugins/auth-methods.test.ts +1 -1
  158. package/src/plugins/index.ts +2 -2
  159. package/src/plugins/otp.test.ts +21 -19
  160. package/src/plugins/otp.ts +36 -34
  161. package/src/plugins/taqnyat.live.test.ts +3 -3
  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,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>
@@ -0,0 +1,136 @@
1
+ ---
2
+ title: "Postgres"
3
+ description: "Default store.sql image — POSTGRES_* env, PGDATA path, and when DATABASE_URL points at PgDog instead."
4
+ icon: "Database"
5
+ source: "docs/spec/unified-theory.md"
6
+ ---
7
+
8
+ Postgres is the default `store.sql` image in docker and prod. `oke docker` matches
9
+ any image whose reference contains `postgres` or `pgvector` (after more specific
10
+ recipes), then derives env, healthcheck, and connection URL.
11
+
12
+ <Callout title="The one rule">
13
+ The driver id stays `postgres` no matter which Postgres-wire image you pin — vendor choice lives
14
+ in `images["store.sql"]`, not in `drivers.store.sql`.
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.sql": "postgres:18-alpine", // any Postgres / pgvector image
27
+ },
28
+ ```
29
+
30
+ </Step>
31
+
32
+ <Step>
33
+ ### Bring the stack up
34
+
35
+ ```bash
36
+ oke dev --docker
37
+ ```
38
+
39
+ `oke docker` writes `POSTGRES_USER` / `POSTGRES_PASSWORD` / `POSTGRES_DB` into
40
+ `docker/.env.docker` as `${OKE_STORE_SQL_USER}` / `${OKE_STORE_SQL_PASSWORD}` /
41
+ `${OKE_STORE_SQL_DB}` refs — never literal secrets in compose YAML.
42
+
43
+ </Step>
44
+
45
+ <Step>
46
+ ### Connect
47
+
48
+ ```bash
49
+ # App reads this (also accepts OKE_STORE_SQL_URL)
50
+ echo "$DATABASE_URL"
51
+ # postgres://oke:…@127.0.0.1:5432/oke
52
+ ```
53
+
54
+ When PgDog is pinned, `DATABASE_URL` points at `:6432` instead — see
55
+ [PgDog](/docs/recipes/pgdog).
56
+
57
+ </Step>
58
+
59
+ </Steps>
60
+
61
+ ## Required env
62
+
63
+ | Variable | Who sets it | Meaning |
64
+ | ------------------------ | ---------------------- | ------------------------------------ |
65
+ | `OKE_STORE_SQL_USER` | `oke docker` → compose | Injected as `POSTGRES_USER` |
66
+ | `OKE_STORE_SQL_PASSWORD` | `oke docker` → compose | Injected as `POSTGRES_PASSWORD` |
67
+ | `OKE_STORE_SQL_DB` | `oke docker` → compose | Injected as `POSTGRES_DB` |
68
+ | `DATABASE_URL` | stack env for the app | What Bun.SQL / Drizzle actually open |
69
+ | `PGDATA` | stack default | `/var/lib/postgresql/data/pgdata` |
70
+ | `POSTGRES_INITDB_ARGS` | stack default | `--data-checksums` |
71
+
72
+ ## Data and backup
73
+
74
+ | Path | What lives there |
75
+ | --------------------------------------------- | ---------------------------------------------------- |
76
+ | `$PGDATA` (`/var/lib/postgresql/data/pgdata`) | Cluster data directory |
77
+ | Image `VOLUME` `/var/lib/postgresql/data` | Official image mount — Docker attaches a volume here |
78
+
79
+ The recipe does **not** declare a named compose volume. Persistence rides the image's
80
+ `VOLUME` (anonymous unless you add a named mount in `compose.override.yml`).
81
+
82
+ **Backup means:** `pg_dump` / `pg_dumpall`, or a Docker volume backup of
83
+ `/var/lib/postgresql/data`. Losing that volume loses the cluster.
84
+
85
+ ## Production note
86
+
87
+ For multi-replica apps, keep SQL on a shared Postgres — Clock CronStore and durable
88
+ journal need one backend. See [Clock](/docs/elements/clock#what-the-runtime-guarantees) ·
89
+ [Docker](/docs/deployment/docker#share-backing-services).
90
+
91
+ Scale out with [PgDog](/docs/recipes/pgdog) so `N × Bun.SQL pool` does not exhaust
92
+ `max_connections`. Do not also stack a managed provider's pooler on the same URL.
93
+
94
+ ## What the recipe configures
95
+
96
+ | Field | Value |
97
+ | -------------- | ---------------------------------------------------- |
98
+ | Container port | `5432` |
99
+ | Healthcheck | `pg_isready -U $POSTGRES_USER`, every 5s, 10 retries |
100
+ | Connection URL | `postgres://user:pass@host:5432/db` |
101
+
102
+ ## Troubleshooting
103
+
104
+ <Accordions>
105
+ <Accordion title="oke boot: postgres driver needs DATABASE_URL">
106
+
107
+ The `postgres` driver fails boot when neither `DATABASE_URL` nor `OKE_STORE_SQL_URL` is
108
+ set. Re-run `oke dev --docker` so the stack writes `docker/.env.docker`, or export
109
+ `DATABASE_URL` yourself when pointing at a managed host.
110
+
111
+ </Accordion>
112
+ <Accordion title="pg_isready fails / store-sql unhealthy">
113
+
114
+ Wrong `POSTGRES_USER` or the container is still initializing. Check
115
+ `docker compose … logs store-sql` for `database system is ready to accept connections`.
116
+
117
+ Credential refs must resolve in `.env.docker` — empty `${OKE_STORE_SQL_PASSWORD}` leaves
118
+ Postgres refusing auth.
119
+
120
+ </Accordion>
121
+ </Accordions>
122
+
123
+ ## Learn more
124
+
125
+ - [PgDog](/docs/recipes/pgdog) — transaction pooling in front of this recipe
126
+ - [Store · SQL](/docs/elements/store#sql) — schema push / generate / migrate
127
+ - [Environment variables](/docs/reference/environment-variables) — `DATABASE_URL` precedence
128
+ - [Neon](/docs/providers/neon) · [Supabase](/docs/providers/supabase) — managed alternatives
129
+
130
+ ## Next
131
+
132
+ <Cards>
133
+ <Card title="PgDog" description="Add pooling in front of Postgres." href="/docs/recipes/pgdog" />
134
+ <Card title="Neon" description="A managed alternative." href="/docs/providers/neon" />
135
+ <Card title="Redis" description="The default store.kv image." href="/docs/recipes/redis" />
136
+ </Cards>
@@ -0,0 +1,130 @@
1
+ ---
2
+ title: "Redis"
3
+ description: "Default store.kv image — OKE_STORE_KV_PASSWORD, maxmemory policy, ephemeral data, REDIS_URL."
4
+ icon: "Zap"
5
+ source: "docs/spec/unified-theory.md"
6
+ ---
7
+
8
+ Redis is the default `store.kv` image in docker and prod. `oke docker` matches image
9
+ references containing `redis` or `keydb` and derives a password-required server — no
10
+ unauthenticated instance.
11
+
12
+ <Callout title="The one rule">
13
+ The driver id stays `redis` for every Redis-wire image — Redis, Valkey, Dragonfly, or a managed
14
+ provider. Vendor choice lives in `images["store.kv"]`, never in `drivers.store.kv`.
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": "redis:8-alpine",
27
+ },
28
+ ```
29
+
30
+ </Step>
31
+
32
+ <Step>
33
+ ### Required password
34
+
35
+ ```bash title="docker/.env.docker (written by oke docker)"
36
+ OKE_STORE_KV_PASSWORD=…
37
+ REDIS_URL=redis://:…@127.0.0.1:6379
38
+ ```
39
+
40
+ The recipe runs:
41
+
42
+ `redis-server --requirepass "$OKE_STORE_KV_PASSWORD" --maxmemory … --maxmemory-policy …`
43
+
44
+ </Step>
45
+
46
+ <Step>
47
+ ### Optional memory caps
48
+
49
+ | Variable | Default in recipe | Meaning |
50
+ | ------------------------------- | ------------------------------------------------------- | -------- |
51
+ | `OKE_STORE_KV_MAXMEMORY` | `0` (unlimited) in command; stack often sets `256mb` | Max RSS |
52
+ | `OKE_STORE_KV_MAXMEMORY_POLICY` | `noeviction` in command; stack often sets `allkeys-lru` | Eviction |
53
+
54
+ </Step>
55
+
56
+ </Steps>
57
+
58
+ ## Required env
59
+
60
+ | Variable | Required? | Meaning |
61
+ | ------------------------------- | ------------- | ------------------------------------------------------------------ |
62
+ | `OKE_STORE_KV_PASSWORD` | **Yes** | `--requirepass` — empty password is not a valid production posture |
63
+ | `REDIS_URL` | **Yes** (app) | What the `redis` driver opens (`OKE_STORE_KV_URL` also works) |
64
+ | `OKE_STORE_KV_MAXMEMORY` | Optional | Cap memory |
65
+ | `OKE_STORE_KV_MAXMEMORY_POLICY` | Optional | Eviction when at cap |
66
+
67
+ ## Data and backup
68
+
69
+ The Redis recipe declares **no named volume**. Process memory is the source of truth;
70
+ a container recreate loses keys unless you add persistence yourself
71
+ (`compose.override.yml` + Redis AOF/RDB).
72
+
73
+ **Backup means (default recipe):** treat KV as a cache — rebuild from SQL / files, or
74
+ add an explicit volume + `SAVE`/`BGSAVE` policy before you rely on durability.
75
+
76
+ ## Production note
77
+
78
+ <Callout title="License note" type="warn">
79
+ Redis ≥8 is dual-licensed RSALv2 / SSPLv1 / AGPLv3. Those terms restrict offering Redis itself as
80
+ a managed service to third parties — running it for your own app is unaffected. Prefer
81
+ [Valkey](/docs/recipes/valkey) when the managed-service restriction matters.
82
+ </Callout>
83
+
84
+ Multi-replica Gate rate counters and Signal need a **shared** Redis URL — an in-process
85
+ `memory` driver is per instance. Same shared-store idea as
86
+ [Clock](/docs/elements/clock#what-the-runtime-guarantees) for cron exclusivity.
87
+
88
+ ## What the recipe configures
89
+
90
+ | Field | Value |
91
+ | -------------- | ---------------------------------------------------- |
92
+ | Container port | `6379` |
93
+ | Healthcheck | `redis-cli -a <password> ping`, every 5s, 10 retries |
94
+ | Connection URL | `redis://:pass@host:6379` |
95
+
96
+ ## Troubleshooting
97
+
98
+ <Accordions>
99
+ <Accordion title="oke boot: redis driver needs REDIS_URL">
100
+
101
+ Missing `REDIS_URL` fails boot loudly — never a silent fallback to `memory`. Re-run
102
+ `oke dev --docker` or export `REDIS_URL` when using a managed host. In docker mode the
103
+ message asks whether `oke dev -d` wrote `docker/.env.docker`.
104
+
105
+ </Accordion>
106
+ <Accordion title="NOAUTH Authentication required">
107
+
108
+ `REDIS_URL` is missing the password, or it does not match `OKE_STORE_KV_PASSWORD`.
109
+ Format must be `redis://:PASSWORD@host:6379` (colon before the password, empty username).
110
+
111
+ </Accordion>
112
+ </Accordions>
113
+
114
+ ## Learn more
115
+
116
+ - [Store · KV](/docs/elements/store#kv) — TTL physics, per-driver behavior, license table
117
+ - [Valkey](/docs/recipes/valkey) · [Dragonfly](/docs/recipes/dragonfly) — Redis-wire peers
118
+ - [Environment variables](/docs/reference/environment-variables) — `REDIS_URL` precedence
119
+
120
+ ## Next
121
+
122
+ <Cards>
123
+ <Card title="Valkey" description="Permissive-licensed alternative." href="/docs/recipes/valkey" />
124
+ <Card title="Redis Cloud" description="Managed Redis." href="/docs/providers/redis-cloud" />
125
+ <Card
126
+ title="Upstash"
127
+ description="Serverless Redis-protocol option."
128
+ href="/docs/providers/upstash"
129
+ />
130
+ </Cards>
@@ -0,0 +1,148 @@
1
+ ---
2
+ title: "RustFS"
3
+ description: "S3-compatible object store — S3_ACCESS_KEY_ID / S3_SECRET_ACCESS_KEY, /data volume, console on :9001."
4
+ icon: "HardDrive"
5
+ source: "docs/spec/unified-theory.md"
6
+ ---
7
+
8
+ RustFS is the default docker `store.files` image — an Apache-2.0 S3-compatible object
9
+ store (MinIO alternative). Driver id stays `s3`; Bun binds `Bun.S3Client` against the
10
+ endpoint.
11
+
12
+ <Callout title="The one rule">
13
+ Protocol name is `s3` — vendor choice is the image pin (`rustfs/rustfs:…`) plus `S3_ENDPOINT`.
14
+ Never invent a `rustfs` driver id.
15
+ </Callout>
16
+
17
+ ## Quick start
18
+
19
+ <Steps>
20
+
21
+ <Step>
22
+ ### Pin driver + image (templates already do)
23
+
24
+ ```typescript title="oke.config.ts"
25
+ drivers: {
26
+ store: {
27
+ files: { local: "fs", docker: "s3", test: "memory", prod: "s3" },
28
+ },
29
+ },
30
+ images: {
31
+ "store.files": "rustfs/rustfs:1.0.0-beta.11",
32
+ },
33
+ ```
34
+
35
+ </Step>
36
+
37
+ <Step>
38
+ ### Credentials and endpoint
39
+
40
+ ```bash title="docker/.env.docker (written by oke docker)"
41
+ S3_ACCESS_KEY_ID=…
42
+ S3_SECRET_ACCESS_KEY=…
43
+ S3_BUCKET=oke
44
+ S3_ENDPOINT=http://127.0.0.1:9000
45
+ S3_URL=http://…@127.0.0.1:9000/oke
46
+ S3_REGION=us-east-1
47
+ S3_CONSOLE_URL=http://127.0.0.1:9001
48
+ ```
49
+
50
+ Recipe maps:
51
+
52
+ - `RUSTFS_ACCESS_KEY=${S3_ACCESS_KEY_ID}`
53
+ - `RUSTFS_SECRET_KEY=${S3_SECRET_ACCESS_KEY}`
54
+ - `RUSTFS_CONSOLE_ENABLE=true`
55
+ - `RUSTFS_ADDRESS=:9000`
56
+ - command: `/data`
57
+
58
+ </Step>
59
+
60
+ <Step>
61
+ ### Put an object
62
+
63
+ Any flow using `fx.store` files (`put` / `get`) against the `s3` driver talks to this
64
+ endpoint. Open `S3_CONSOLE_URL` for a browser console on port **9001**.
65
+
66
+ </Step>
67
+
68
+ </Steps>
69
+
70
+ ## Required env
71
+
72
+ | Variable | Required? | Meaning |
73
+ | ---------------------- | ----------------- | ------------------------------------------ |
74
+ | `S3_ACCESS_KEY_ID` | **Yes** | Access key → `RUSTFS_ACCESS_KEY` |
75
+ | `S3_SECRET_ACCESS_KEY` | **Yes** | Secret key → `RUSTFS_SECRET_KEY` |
76
+ | `S3_BUCKET` | **Yes** (app) | Bucket name the driver opens |
77
+ | `S3_ENDPOINT` | **Yes** (non-AWS) | Origin `http://host:9000` |
78
+ | `S3_REGION` | Optional | Default `us-east-1` in stack env |
79
+ | `S3_SESSION_TOKEN` | Optional | Temporary creds |
80
+ | `S3_CONSOLE_URL` | Written by stack | UI on `:9001` — not required by the driver |
81
+ | `S3_URL` | Written by stack | Credentialed URL form |
82
+
83
+ ## Data and backup
84
+
85
+ | Volume | Path | What it stores |
86
+ | ------------------ | ------- | ------------------------------ |
87
+ | `store-files-data` | `/data` | Object bytes + RustFS metadata |
88
+
89
+ **Backup means:** snapshot that named volume (or `aws s3 sync` against the API).
90
+ Deleting the volume deletes every object.
91
+
92
+ Credentials live in `.env.docker`, not in `/data` — rotate keys without wiping objects.
93
+ Losing both volume and keys means full restore from backup only.
94
+
95
+ ## Production note
96
+
97
+ For cloud prod, point the same `s3` driver at real S3 / R2 / GCS interop by changing
98
+ `S3_ENDPOINT` + keys — keep the driver id. Self-hosted RustFS on a single node is a
99
+ durability SPOF: put the `/data` volume on reliable disks and back it up.
100
+
101
+ Do not publish `:9000` / `:9001` on the public internet without TLS and network policy —
102
+ the recipe enables the console for local ops.
103
+
104
+ ## What the recipe configures
105
+
106
+ | Field | Value |
107
+ | -------------- | ----------------------------------------------------------------------- |
108
+ | API port | `9000` |
109
+ | Extra port | `9001` → console |
110
+ | Healthcheck | `curl -f http://127.0.0.1:9000/health`, every 5s, 12 retries, 10s start |
111
+ | Connection URL | `http://user:pass@host:9000/bucket` |
112
+
113
+ ## Troubleshooting
114
+
115
+ <Accordions>
116
+ <Accordion title="oke boot: redis / meilisearch / S3 URL missing — S3_BUCKET">
117
+
118
+ Files on the `s3` driver need `S3_BUCKET` (or `OKE_STORE_FILES_DB`). Without it the
119
+ facet cannot open. Re-run `oke dev --docker` so stack env fills `S3_*`, or export them
120
+ for a managed bucket.
121
+
122
+ </Accordion>
123
+ <Accordion title="Access Denied / signature errors against :9000">
124
+
125
+ `S3_ACCESS_KEY_ID` / `S3_SECRET_ACCESS_KEY` do not match `RUSTFS_*` inside the
126
+ container, or `S3_ENDPOINT` points at the wrong host/port. Align `.env.docker` with the
127
+ running service; confirm health at `/health` before debugging client signatures.
128
+
129
+ </Accordion>
130
+ </Accordions>
131
+
132
+ ## Learn more
133
+
134
+ - [Store · Files](/docs/elements/store#files) — facet API and drivers
135
+ - [Environment variables](/docs/reference/environment-variables) — full `S3_*` map
136
+ - [Installation](/docs/get-started/installation) — docker env cheat sheet
137
+
138
+ ## Next
139
+
140
+ <Cards>
141
+ <Card
142
+ title="Mailpit"
143
+ description="SMTP catcher for the same stack."
144
+ href="/docs/recipes/mailpit"
145
+ />
146
+ <Card title="OpenBao" description="Vault for secrets." href="/docs/recipes/openbao" />
147
+ <Card title="Store" description="Files facet details." href="/docs/elements/store" />
148
+ </Cards>