okengine 0.10.2 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (343) hide show
  1. package/package.json +76 -10
  2. package/site/content/docs/ai/mcp.mdx +0 -1
  3. package/site/content/docs/deployment/docker-swarm.mdx +48 -111
  4. package/site/content/docs/deployment/docker.mdx +43 -36
  5. package/site/content/docs/deployment/index.mdx +9 -9
  6. package/site/content/docs/deployment/kubernetes.mdx +4 -4
  7. package/site/content/docs/elements/ai.mdx +7 -13
  8. package/site/content/docs/elements/channel.mdx +9 -13
  9. package/site/content/docs/elements/clock.mdx +8 -14
  10. package/site/content/docs/elements/flow.mdx +46 -54
  11. package/site/content/docs/elements/gate.mdx +6 -11
  12. package/site/content/docs/elements/signal.mdx +2 -8
  13. package/site/content/docs/elements/store.mdx +79 -93
  14. package/site/content/docs/elements/vault.mdx +89 -76
  15. package/site/content/docs/get-started/basic-usage.mdx +8 -14
  16. package/site/content/docs/get-started/installation.mdx +36 -36
  17. package/site/content/docs/get-started/introduction.mdx +11 -0
  18. package/site/content/docs/get-started/why.mdx +3 -4
  19. package/site/content/docs/index.mdx +0 -1
  20. package/site/content/docs/meta.json +0 -1
  21. package/site/content/docs/plugins/anonymous.mdx +1 -1
  22. package/site/content/docs/plugins/magic-link.mdx +11 -11
  23. package/site/content/docs/plugins/otp.mdx +2 -2
  24. package/site/content/docs/plugins/passkey.mdx +1 -1
  25. package/site/content/docs/plugins/two-factor.mdx +1 -1
  26. package/site/content/docs/plugins/username.mdx +1 -1
  27. package/site/content/docs/recipes/caddy.mdx +1 -1
  28. package/site/content/docs/recipes/cockroachdb.mdx +2 -2
  29. package/site/content/docs/recipes/dragonfly.mdx +1 -1
  30. package/site/content/docs/recipes/index.mdx +1 -2
  31. package/site/content/docs/recipes/llama-cpp.mdx +21 -23
  32. package/site/content/docs/recipes/mailpit.mdx +11 -11
  33. package/site/content/docs/recipes/meilisearch.mdx +10 -10
  34. package/site/content/docs/recipes/meta.json +0 -1
  35. package/site/content/docs/recipes/ollama.mdx +1 -1
  36. package/site/content/docs/recipes/pgdog.mdx +12 -9
  37. package/site/content/docs/recipes/postgres.mdx +3 -3
  38. package/site/content/docs/recipes/redis.mdx +2 -2
  39. package/site/content/docs/recipes/rustfs.mdx +10 -7
  40. package/site/content/docs/recipes/sglang.mdx +1 -1
  41. package/site/content/docs/recipes/supabase-docker.mdx +1 -1
  42. package/site/content/docs/recipes/timescale.mdx +2 -2
  43. package/site/content/docs/recipes/valkey.mdx +1 -1
  44. package/site/content/docs/recipes/vllm.mdx +1 -1
  45. package/site/content/docs/recipes/yugabytedb.mdx +2 -2
  46. package/site/content/docs/reference/cli.md +61 -38
  47. package/site/content/docs/reference/configuration.mdx +145 -70
  48. package/site/content/docs/reference/environment-variables.mdx +36 -28
  49. package/site/content/docs/reference/errors.mdx +28 -29
  50. package/site/content/docs/reference/fx.mdx +14 -7
  51. package/site/content/docs/reference/i18n.mdx +17 -6
  52. package/site/content/docs/reference/index.mdx +5 -0
  53. package/site/content/docs/reference/meta.json +1 -0
  54. package/site/content/docs/reference/migrating-environments.mdx +158 -0
  55. package/site/content/docs/reference/plugins.mdx +2 -2
  56. package/src/auth/gate-auth.test.ts +12 -10
  57. package/src/cli/ai-setup/ai-setup.test.ts +30 -16
  58. package/src/cli/ai-setup/apply.ts +111 -15
  59. package/src/cli/ai-setup/catalog.ts +3 -3
  60. package/src/cli/ai-setup/index.ts +1 -1
  61. package/src/cli/ai-setup/prompts.ts +1 -1
  62. package/src/cli/ai-setup/recommend.test.ts +1 -1
  63. package/src/cli/ask-seed.test.ts +3 -3
  64. package/src/cli/build.ts +8 -9
  65. package/src/cli/db-auto-push.test.ts +6 -3
  66. package/src/cli/db-auto-push.ts +7 -5
  67. package/src/cli/db-seed.ts +7 -8
  68. package/src/cli/db.test.ts +70 -71
  69. package/src/cli/db.ts +86 -12
  70. package/src/cli/dev-controller.test.ts +35 -0
  71. package/src/cli/dev-controller.ts +298 -0
  72. package/src/cli/dev-controls.ts +11 -65
  73. package/src/cli/dev-db-push.test.ts +47 -15
  74. package/src/cli/dev-schema-sync.test.ts +11 -11
  75. package/src/cli/dev-schema-sync.ts +10 -10
  76. package/src/cli/dev-session-lock.test.ts +106 -0
  77. package/src/cli/dev-session-lock.ts +209 -0
  78. package/src/cli/dev.test.ts +246 -114
  79. package/src/cli/dev.ts +153 -205
  80. package/src/cli/doc-staleness.test.ts +3 -6
  81. package/src/cli/docker-clean.test.ts +33 -1
  82. package/src/cli/docker-clean.ts +28 -33
  83. package/src/cli/docker-cli.test.ts +12 -11
  84. package/src/cli/docker.ts +95 -20
  85. package/src/cli/doctor.test.ts +40 -0
  86. package/src/cli/doctor.ts +142 -9
  87. package/src/cli/drizzle-env.test.ts +11 -13
  88. package/src/cli/drizzle-env.ts +18 -11
  89. package/src/cli/ensure-drizzle-config.ts +11 -14
  90. package/src/cli/hero-meta.test.ts +15 -15
  91. package/src/cli/hero-meta.ts +10 -9
  92. package/src/cli/index.ts +36 -6
  93. package/src/cli/load-config.images.test.ts +44 -10
  94. package/src/cli/load-config.source.test.ts +50 -0
  95. package/src/cli/load-config.ts +35 -29
  96. package/src/cli/meilisearch-local.ts +1 -2
  97. package/src/cli/mode.ts +7 -58
  98. package/src/cli/registry.ts +175 -19
  99. package/src/cli/resolve-dev-sql-env.test.ts +8 -16
  100. package/src/cli/resolve-dev-sql-env.ts +13 -15
  101. package/src/cli/safe-defaults.test.ts +29 -11
  102. package/src/cli/schema.ts +65 -16
  103. package/src/cli/test.ts +58 -0
  104. package/src/cli/tui/App.tsx +252 -0
  105. package/src/cli/tui/DevLive.tsx +116 -0
  106. package/src/cli/tui/components/ConfirmDialog.tsx +48 -0
  107. package/src/cli/tui/components/HeaderBar.tsx +67 -0
  108. package/src/cli/tui/components/LogStream.tsx +47 -0
  109. package/src/cli/tui/components/PanelCard.tsx +39 -0
  110. package/src/cli/tui/components/SelectRow.tsx +37 -0
  111. package/src/cli/tui/components/SlashPalette.tsx +172 -0
  112. package/src/cli/tui/components/Spinner.tsx +32 -0
  113. package/src/cli/tui/components/StatusBar.tsx +51 -0
  114. package/src/cli/tui/components/StatusDot.tsx +37 -0
  115. package/src/cli/tui/components/TabBar.tsx +49 -0
  116. package/src/cli/tui/components/Wordmark.tsx +43 -0
  117. package/src/cli/tui/format.ts +38 -0
  118. package/src/cli/tui/keys.test.ts +18 -0
  119. package/src/cli/tui/keys.ts +26 -0
  120. package/src/cli/tui/launch.test.ts +18 -0
  121. package/src/cli/tui/launch.ts +57 -0
  122. package/src/cli/tui/panels/Dashboard.tsx +201 -0
  123. package/src/cli/tui/panels/Database.tsx +198 -0
  124. package/src/cli/tui/panels/Dev.tsx +257 -0
  125. package/src/cli/tui/panels/Docker.tsx +357 -0
  126. package/src/cli/tui/panels/Navigator.test.ts +22 -0
  127. package/src/cli/tui/panels/Navigator.tsx +213 -0
  128. package/src/cli/tui/slash-catalog.test.ts +31 -0
  129. package/src/cli/tui/slash-catalog.ts +184 -0
  130. package/src/cli/tui/slash-run.ts +54 -0
  131. package/src/cli/tui/state/ports.ts +95 -0
  132. package/src/cli/tui/theme.ts +24 -0
  133. package/src/cli/vault-cmd.test.ts +402 -0
  134. package/src/cli/vault-cmd.ts +727 -69
  135. package/src/cli/vault-secure-input.test.ts +56 -0
  136. package/src/cli/vault-secure-input.ts +124 -0
  137. package/src/compiler/effects-infer.ts +7 -0
  138. package/src/compiler/extract.ts +12 -0
  139. package/src/compiler/fixtures/skyport/oke.config.ts +7 -5
  140. package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +1 -1
  141. package/src/compiler/fixtures/skyport.expected.json +1 -1
  142. package/src/compiler/generate-adopt.ts +27 -1
  143. package/src/compiler-entry.ts +7 -0
  144. package/src/config/define-config.test.ts +111 -47
  145. package/src/config/driver-defaults.test.ts +115 -0
  146. package/src/config/driver-defaults.ts +156 -0
  147. package/src/config/index.ts +246 -131
  148. package/src/config/resolve-driver.test.ts +14 -19
  149. package/src/console/server/app.ts +4 -2
  150. package/src/console/server/console.test.ts +58 -1
  151. package/src/console/server/flows.ts +22 -2
  152. package/src/console/server/index.ts +3 -0
  153. package/src/console/server/operator-db.test.ts +55 -13
  154. package/src/console/server/operator-db.ts +305 -263
  155. package/src/console/server/serve.ts +16 -7
  156. package/src/console/server/state.ts +31 -9
  157. package/src/console/server/vault.test.ts +120 -5
  158. package/src/console/server/vault.ts +208 -12
  159. package/src/console/ui/dist/assets/{index-D-9KFce5.js → index--Y0Uaz-m.js} +2 -2
  160. package/src/console/ui/dist/assets/panel-vault-BkZdJPX8.js +1 -0
  161. package/src/console/ui/dist/assets/style-C3L349iZ.css +3 -0
  162. package/src/console/ui/dist/index.html +2 -2
  163. package/src/console/ui/shell/client.ts +16 -0
  164. package/src/console/ui/shell/panels/vault/VaultPanel.tsx +54 -1
  165. package/src/console/ui/vault/VaultA11yView.tsx +33 -1
  166. package/src/console/ui/vault/backend.test.ts +75 -0
  167. package/src/console/ui/vault/backend.ts +117 -0
  168. package/src/console/ui/vault/fixture.ts +21 -2
  169. package/src/console/ui/vault/index.ts +11 -1
  170. package/src/console/ui/vault/types.ts +26 -0
  171. package/src/docker/compose.ts +164 -61
  172. package/src/docker/derive.ts +106 -12
  173. package/src/docker/docker.test.ts +156 -110
  174. package/src/docker/helpers.ts +98 -0
  175. package/src/docker/images-config.test.ts +163 -0
  176. package/src/docker/index.ts +30 -5
  177. package/src/docker/recipes/index.ts +0 -3
  178. package/src/docker/recipes/llama-cpp.ts +4 -4
  179. package/src/docker/recipes/pgdog.ts +9 -2
  180. package/src/docker/resources.test.ts +71 -0
  181. package/src/docker/resources.ts +176 -0
  182. package/src/docker/stack-id.test.ts +11 -4
  183. package/src/docker/stack-id.ts +42 -32
  184. package/src/docker/stack.integration.test.ts +4 -15
  185. package/src/docker/types.ts +35 -3
  186. package/src/drivers/ai-ollama.integration.test.ts +2 -2
  187. package/src/drivers/conformance.test.ts +11 -12
  188. package/src/drivers/drizzle-dialect.test.ts +0 -4
  189. package/src/drivers/drizzle-dialect.ts +7 -13
  190. package/src/drivers/index.ts +24 -7
  191. package/src/drivers/journal-postgres.test.ts +3 -1
  192. package/src/drivers/journal-postgres.ts +4 -2
  193. package/src/drivers/meilisearch.ts +1 -1
  194. package/src/drivers/pglite.ts +28 -5
  195. package/src/drivers/types.ts +9 -11
  196. package/src/drivers/vault-aws-secrets-manager.test.ts +159 -0
  197. package/src/drivers/vault-aws-secrets-manager.ts +333 -0
  198. package/src/drivers/vault-builtin.test.ts +110 -0
  199. package/src/drivers/vault-builtin.ts +184 -0
  200. package/src/drivers/vault-driver-removal.test.ts +13 -0
  201. package/src/drivers/vault-managed.test.ts +95 -0
  202. package/src/drivers/vault-managed.ts +101 -25
  203. package/src/drivers/vault-types.ts +25 -5
  204. package/src/elements/ai/runtime.ts +1 -1
  205. package/src/elements/clock/chaos.test.ts +4 -1
  206. package/src/elements/clock/durable.ts +57 -4
  207. package/src/elements/clock.test.ts +13 -14
  208. package/src/elements/gate/constants.ts +18 -0
  209. package/src/elements/gate.test.ts +1 -1
  210. package/src/elements/gate.ts +2 -2
  211. package/src/elements/store/domain-ddl.test.ts +5 -12
  212. package/src/elements/store/emit-drizzle.ts +44 -19
  213. package/src/elements/store/index-boot.test.ts +15 -116
  214. package/src/elements/store/load-plugin-tables.ts +1 -1
  215. package/src/elements/store/resource-list-docs.fixture.ts +12 -6
  216. package/src/elements/store/resource-list-docs.test.ts +64 -44
  217. package/src/elements/store/resource.test.ts +17 -10
  218. package/src/elements/store/runtime.ts +9 -12
  219. package/src/elements/store/schema-decl.test.ts +16 -32
  220. package/src/elements/store/seed.test.ts +1 -2
  221. package/src/elements/store/seed.ts +7 -8
  222. package/src/elements/store/sql-condition.test.ts +2 -2
  223. package/src/elements/store/sql-session.test.ts +8 -10
  224. package/src/elements/store/sql-session.ts +10 -2
  225. package/src/elements/store.test.ts +13 -13
  226. package/src/elements/vault/audit.ts +233 -0
  227. package/src/elements/vault/boot-chain.test.ts +78 -0
  228. package/src/elements/vault/boot-chain.ts +43 -39
  229. package/src/elements/vault/builtin-adapter.test.ts +479 -0
  230. package/src/elements/vault/builtin-adapter.ts +1184 -0
  231. package/src/elements/vault/crypto.test.ts +237 -0
  232. package/src/elements/vault/crypto.ts +431 -0
  233. package/src/elements/vault/declare.ts +164 -3
  234. package/src/elements/vault/errors.ts +56 -0
  235. package/src/elements/vault/kms-unseal.test.ts +76 -0
  236. package/src/elements/vault/kms-unseal.ts +181 -0
  237. package/src/elements/vault/path.ts +90 -0
  238. package/src/elements/vault/resilience.test.ts +102 -0
  239. package/src/elements/vault/resilience.ts +107 -0
  240. package/src/elements/vault/runtime.ts +36 -4
  241. package/src/elements/vault/security-checklist.test.ts +130 -0
  242. package/src/elements/vault/storage.ts +448 -0
  243. package/src/elements/vault/types.ts +288 -0
  244. package/src/elements/vault/unseal.ts +150 -0
  245. package/src/elements/vault.test.ts +72 -4
  246. package/src/elements/vault.ts +91 -2
  247. package/src/full.ts +248 -0
  248. package/src/http.ts +41 -0
  249. package/src/i18n/interpolate-simple.ts +37 -0
  250. package/src/i18n/messages.ts +51 -4
  251. package/src/i18n-entry.ts +7 -0
  252. package/src/index.ts +35 -145
  253. package/src/journal-entry.ts +20 -0
  254. package/src/kernel/adopt-barrel-fresh.test.ts +25 -10
  255. package/src/kernel/app.ts +40 -42
  256. package/src/kernel/auto-registry.test.ts +2 -2
  257. package/src/kernel/boot-bind/ai.test.ts +5 -5
  258. package/src/kernel/boot-bind/ai.ts +2 -1
  259. package/src/kernel/boot-bind/channel.test.ts +6 -6
  260. package/src/kernel/boot-bind/channel.ts +5 -4
  261. package/src/kernel/boot-bind/clock.ts +5 -5
  262. package/src/kernel/boot-bind/gate.ts +6 -7
  263. package/src/kernel/boot-bind/honor-config.test.ts +42 -42
  264. package/src/kernel/boot-bind/journal.ts +5 -5
  265. package/src/kernel/boot-bind/signal.ts +3 -1
  266. package/src/kernel/boot-bind/store.test.ts +55 -37
  267. package/src/kernel/boot-bind/store.ts +24 -34
  268. package/src/kernel/boot-bind/vault.ts +19 -4
  269. package/src/kernel/boot.test.ts +12 -4
  270. package/src/kernel/boot.ts +31 -19
  271. package/src/kernel/budget-entry.ts +1 -1
  272. package/src/kernel/compensate.test.ts +288 -1
  273. package/src/kernel/compensate.ts +126 -0
  274. package/src/kernel/concurrency.test.ts +10 -9
  275. package/src/kernel/effects-stamping.test.ts +27 -2
  276. package/src/kernel/element-registries.ts +5 -3
  277. package/src/kernel/flow.ts +4 -2
  278. package/src/kernel/fx.test.ts +119 -3
  279. package/src/kernel/fx.ts +215 -40
  280. package/src/kernel/index.ts +14 -0
  281. package/src/kernel/journal.ts +130 -7
  282. package/src/kernel/pipeline.test.ts +20 -6
  283. package/src/kernel/redacted.test.ts +35 -0
  284. package/src/kernel/redacted.ts +24 -2
  285. package/src/kernel/router/create-default.ts +16 -0
  286. package/src/kernel/router/create-edge.ts +18 -0
  287. package/src/kernel/router/create.ts +23 -0
  288. package/src/kernel/router/index.ts +23 -0
  289. package/src/kernel/router/linear.ts +88 -0
  290. package/src/kernel/router/regexp.ts +188 -0
  291. package/src/kernel/router/smart.ts +120 -0
  292. package/src/kernel/router/trie.ts +108 -0
  293. package/src/kernel/router/types.ts +56 -0
  294. package/src/kernel/router.ts +18 -532
  295. package/src/kernel-entry.ts +43 -0
  296. package/src/manifest/fixtures/skyport.excerpt.json +1 -1
  297. package/src/manifest/fixtures/skyport.manifest.json +1 -1
  298. package/src/plugins/config-source.test.ts +7 -7
  299. package/src/release/build-lib.ts +58 -0
  300. package/src/release/exports.test.ts +4 -2
  301. package/src/release/exports.ts +30 -4
  302. package/src/release/measure.ts +101 -22
  303. package/src/runs/duckdb.ts +7 -2
  304. package/src/shared/optional-peer.ts +41 -0
  305. package/src/term.test.ts +3 -44
  306. package/src/test/create-test-app.ts +12 -0
  307. package/src/test/provisions.integration.test.ts +1 -1
  308. package/src/test/reset-element-registries.ts +2 -0
  309. package/src/upgrade/codemods.test.ts +7 -20
  310. package/src/upgrade/codemods.ts +93 -60
  311. package/site/content/docs/console/access.mdx +0 -29
  312. package/site/content/docs/console/ai.mdx +0 -35
  313. package/site/content/docs/console/architecture.mdx +0 -35
  314. package/site/content/docs/console/channels.mdx +0 -37
  315. package/site/content/docs/console/clock.mdx +0 -51
  316. package/site/content/docs/console/flows.mdx +0 -31
  317. package/site/content/docs/console/gates.mdx +0 -73
  318. package/site/content/docs/console/index.mdx +0 -54
  319. package/site/content/docs/console/manifest-diff.mdx +0 -34
  320. package/site/content/docs/console/meta.json +0 -24
  321. package/site/content/docs/console/overview.mdx +0 -40
  322. package/site/content/docs/console/plugins.mdx +0 -41
  323. package/site/content/docs/console/privacy.mdx +0 -32
  324. package/site/content/docs/console/runs.mdx +0 -50
  325. package/site/content/docs/console/signals.mdx +0 -40
  326. package/site/content/docs/console/store.mdx +0 -32
  327. package/site/content/docs/console/tenancy.mdx +0 -32
  328. package/site/content/docs/console/traces.mdx +0 -38
  329. package/site/content/docs/console/vault.mdx +0 -41
  330. package/site/content/docs/recipes/openbao.mdx +0 -141
  331. package/src/cli/ask-dev-mode.ts +0 -40
  332. package/src/cli/dev-mode.test.ts +0 -45
  333. package/src/cli/dev-mode.ts +0 -78
  334. package/src/cli/openbao-bootstrap.test.ts +0 -150
  335. package/src/cli/openbao-bootstrap.ts +0 -321
  336. package/src/cli/openbao-restart.integration.test.ts +0 -145
  337. package/src/console/ui/dist/assets/panel-vault-Za1GyJfM.js +0 -1
  338. package/src/console/ui/dist/assets/style-C8MxEWPd.css +0 -3
  339. package/src/docker/recipes/openbao.ts +0 -47
  340. package/src/drivers/libsql.ts +0 -179
  341. package/src/drivers/sqlite.ts +0 -44
  342. package/src/drivers/vault-openbao.test.ts +0 -97
  343. package/src/drivers/vault-openbao.ts +0 -143
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Retry wrapper for Vault SQL — connection blips, not business errors.
3
+ *
4
+ * Audit writes must use the raw {@link SqlExec}: retrying an append can
5
+ * duplicate rows and break the hash chain.
6
+ */
7
+
8
+ import type { SqlExec } from "./storage.ts";
9
+
10
+ /** Tunables for {@link withResilience}. */
11
+ export interface RetryConfig {
12
+ /** Attempts after the first failure. */
13
+ readonly maxRetries: number;
14
+ /** Initial backoff in milliseconds. */
15
+ readonly baseDelayMs: number;
16
+ /** Cap on exponential backoff. */
17
+ readonly maxDelayMs: number;
18
+ /** Substrings matched against `error.code` or `error.message`. */
19
+ readonly retryableErrors: readonly string[];
20
+ }
21
+
22
+ /** Default Postgres / network blip codes. */
23
+ export const DEFAULT_RETRY: RetryConfig = {
24
+ maxRetries: 3,
25
+ baseDelayMs: 500,
26
+ maxDelayMs: 5000,
27
+ retryableErrors: ["ECONNRESET", "ETIMEDOUT", "08006", "08003", "57P01"],
28
+ };
29
+
30
+ /**
31
+ * Whether `error` looks like a transient connection failure.
32
+ *
33
+ * @param error - Caught value
34
+ * @param config - Retry tunables
35
+ */
36
+ export function isRetryableError(error: unknown, config: RetryConfig = DEFAULT_RETRY): boolean {
37
+ if (!error || typeof error !== "object") return false;
38
+ const record = error as { code?: string | number; message?: string };
39
+ const code =
40
+ typeof record.code === "string" || typeof record.code === "number" ? String(record.code) : "";
41
+ const message =
42
+ typeof record.message === "string"
43
+ ? record.message
44
+ : error instanceof Error
45
+ ? error.message
46
+ : "";
47
+ return config.retryableErrors.some(
48
+ (retryable) => code.includes(retryable) || message.includes(retryable),
49
+ );
50
+ }
51
+
52
+ /**
53
+ * Sleep without journaling (CLI / adapter path, not a durable Flow step).
54
+ *
55
+ * @param ms - Delay
56
+ */
57
+ export async function sleep(ms: number): Promise<void> {
58
+ await new Promise<void>((resolve) => {
59
+ setTimeout(resolve, ms);
60
+ });
61
+ }
62
+
63
+ /**
64
+ * Run `operation` with exponential backoff on retryable failures.
65
+ *
66
+ * @param operation - Thunk to retry
67
+ * @param config - Retry tunables
68
+ */
69
+ export async function withResilience<T>(
70
+ operation: () => Promise<T>,
71
+ config: RetryConfig = DEFAULT_RETRY,
72
+ ): Promise<T> {
73
+ let lastError: unknown;
74
+ for (let attempt = 0; attempt <= config.maxRetries; attempt += 1) {
75
+ try {
76
+ return await operation();
77
+ } catch (error) {
78
+ lastError = error;
79
+ if (attempt === config.maxRetries) break;
80
+ if (!isRetryableError(error, config)) throw error;
81
+ const delay = Math.min(config.baseDelayMs * 2 ** attempt, config.maxDelayMs);
82
+ console.warn(`[vault] Query failed (attempt ${attempt + 1}), retrying in ${delay}ms...`);
83
+ await sleep(delay);
84
+ }
85
+ }
86
+ throw lastError;
87
+ }
88
+
89
+ /**
90
+ * Wrap a {@link SqlExec} so every `query` / `execute` retries on blips.
91
+ *
92
+ * Do **not** pass the result to {@link createSqlAuditWriter} — audit must
93
+ * fail loudly or be best-effort without duplicate appends.
94
+ *
95
+ * @param db - Underlying SQL surface
96
+ * @param config - Retry tunables
97
+ */
98
+ export function createResilientSqlExec(db: SqlExec, config: RetryConfig = DEFAULT_RETRY): SqlExec {
99
+ return {
100
+ query<T>(sql: string, params?: unknown[]): Promise<T[]> {
101
+ return withResilience(() => db.query<T>(sql, params), config);
102
+ },
103
+ execute(sql: string, params?: unknown[]): Promise<void> {
104
+ return withResilience(() => db.execute(sql, params), config);
105
+ },
106
+ };
107
+ }
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import type { VaultBag, VaultDriver } from "../../drivers/vault-types.ts";
6
- import { fromDockerRole, isFromDocker } from "./declare.ts";
6
+ import { fromDockerRole, isFromDocker, readEnv } from "./declare.ts";
7
7
  import type { VaultSecretDecl } from "./declare.ts";
8
8
  import { fingerprintSecretSync } from "./fingerprint.ts";
9
9
  import { createSecretRedactor, SECRET_MASK } from "./redact.ts";
@@ -69,9 +69,15 @@ export interface VaultResolutionStep {
69
69
  export interface CreateVaultRuntimeOptions {
70
70
  /** Declared secret / config contracts. */
71
71
  readonly secrets?: readonly VaultSecretDecl[];
72
+ /**
73
+ * Environment variables declared through `vault.env.required`. Missing
74
+ * ones are reported as {@link VaultGap}s in the same boot pass as secrets,
75
+ * so one run lists every hole in the configuration.
76
+ */
77
+ readonly requiredEnv?: readonly string[];
72
78
  /**
73
79
  * Resolution chain — first hit wins.
74
- * Callers pass the env-appropriate layers (env / openbao / memory / ).
80
+ * Callers pass the env-appropriate layers (env / vault / memory / managed).
75
81
  */
76
82
  readonly chain?: readonly VaultChainLayer[];
77
83
  /**
@@ -88,7 +94,7 @@ export interface VaultRuntime {
88
94
  /** Declared contracts by name. */
89
95
  readonly contracts: ReadonlyMap<string, VaultSecretDecl>;
90
96
  /**
91
- * Driver ids of each chain layer in order (`env` · `openbao` · `memory` · `managed`).
97
+ * Driver ids of each chain layer in order (`env` · `vault` · `memory` · `managed`).
92
98
  * Available before {@link boot}.
93
99
  */
94
100
  readonly chainDriverIds: readonly string[];
@@ -100,7 +106,7 @@ export interface VaultRuntime {
100
106
  /** Whether {@link boot} has succeeded. */
101
107
  readonly booted: boolean;
102
108
  /**
103
- * Read a secret (capability is enforced by `fx.vault`).
109
+ * Read a secret (capability is enforced by `fx.vault.get`).
104
110
  * Records {@link lastReadAt}.
105
111
  *
106
112
  * @param name - Secret name
@@ -238,6 +244,7 @@ export function createVaultRuntime(options: CreateVaultRuntimeOptions = {}): Vau
238
244
  });
239
245
  }
240
246
  }
247
+ gaps.push(...requiredEnvGaps(options.requiredEnv ?? [], new Set(contracts.keys())));
241
248
  if (gaps.length > 0) {
242
249
  throw new VaultBootError(gaps);
243
250
  }
@@ -378,6 +385,31 @@ export function createVaultRuntime(options: CreateVaultRuntimeOptions = {}): Vau
378
385
  };
379
386
  }
380
387
 
388
+ /**
389
+ * Gaps for every `vault.env.required` name absent from the process
390
+ * environment. Names that are also declared contracts are skipped — those
391
+ * resolve through the boot chain (`.env.local`, driver, dev fallback), so
392
+ * `process.env` alone would report a false gap.
393
+ *
394
+ * @param names - Required environment variable names
395
+ * @param declared - Contract names already validated by the boot chain
396
+ */
397
+ export function requiredEnvGaps(
398
+ names: readonly string[],
399
+ declared: ReadonlySet<string> = new Set(),
400
+ ): VaultGap[] {
401
+ const gaps: VaultGap[] = [];
402
+ const seen = new Set<string>();
403
+ for (const name of names) {
404
+ if (declared.has(name) || seen.has(name)) continue;
405
+ seen.add(name);
406
+ if (readEnv(name) === undefined) {
407
+ gaps.push({ name, description: "required environment variable (vault.env.required)" });
408
+ }
409
+ }
410
+ return gaps;
411
+ }
412
+
381
413
  /**
382
414
  * Infer the Console source id for a chain layer.
383
415
  *
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Phase 10 security checklist — threat-model assertions for the built-in vault.
3
+ */
4
+
5
+ import { describe, expect, test } from "bun:test";
6
+ import { connectPglite } from "../../drivers/pglite.ts";
7
+ import type { SqlConnection } from "../../drivers/types.ts";
8
+ import {
9
+ createBuiltinVaultAdapter,
10
+ sqlConnectionAsExec,
11
+ type BuiltinVaultAdapter,
12
+ } from "./builtin-adapter.ts";
13
+ import {
14
+ ALGORITHM,
15
+ buildAad,
16
+ decryptSecret,
17
+ encryptSecret,
18
+ generateDek,
19
+ importAesKey,
20
+ unwrapDek,
21
+ wrapDek,
22
+ } from "./crypto.ts";
23
+ import { VaultError } from "./errors.ts";
24
+ import { canonicalizePath } from "./path.ts";
25
+ import type { SqlExec } from "./storage.ts";
26
+ import { createMemoryUnsealer } from "./unseal.ts";
27
+
28
+ /** Fresh initialized + unsealed adapter. */
29
+ async function harness(): Promise<{
30
+ adapter: BuiltinVaultAdapter;
31
+ db: SqlExec;
32
+ conn: SqlConnection;
33
+ close(): Promise<void>;
34
+ }> {
35
+ const conn = await connectPglite({ url: `memory://vault-sec-${crypto.randomUUID()}` });
36
+ const db = sqlConnectionAsExec(conn);
37
+ const adapter = createBuiltinVaultAdapter({ db });
38
+ const init = await adapter.initialize!();
39
+ await adapter.unseal!(init.masterKey);
40
+ return {
41
+ adapter,
42
+ db,
43
+ conn,
44
+ async close() {
45
+ await conn.close();
46
+ },
47
+ };
48
+ }
49
+
50
+ describe("vault security checklist", () => {
51
+ test("DB rows never contain the cleartext secret", async () => {
52
+ const h = await harness();
53
+ try {
54
+ const secret = `sk_live_${crypto.randomUUID()}`;
55
+ await h.adapter.set("prod/api/stripe", secret);
56
+ const rows = await h.conn.query(
57
+ "select encode(encrypted_value, 'escape') as blob from oke_vault_secrets",
58
+ );
59
+ for (const row of rows) {
60
+ const blob = String((row as { blob?: unknown }).blob ?? "");
61
+ expect(blob.includes(secret)).toBe(false);
62
+ }
63
+ } finally {
64
+ await h.close();
65
+ }
66
+ });
67
+
68
+ test("sealed vault fails closed on get and set", async () => {
69
+ const h = await harness();
70
+ try {
71
+ await h.adapter.set("a/b", "v1");
72
+ await h.adapter.seal!();
73
+ await expect(h.adapter.get("a/b")).rejects.toBeInstanceOf(VaultError);
74
+ await expect(h.adapter.set("a/b", "v2")).rejects.toBeInstanceOf(VaultError);
75
+ } finally {
76
+ await h.close();
77
+ }
78
+ });
79
+
80
+ test("canonical path is what enters AAD — non-canonical input is rejected before store", () => {
81
+ expect(() => canonicalizePath("../etc/passwd")).toThrow(VaultError);
82
+ expect(() => canonicalizePath("a\\b")).toThrow(VaultError);
83
+ expect(() => canonicalizePath("a\0b")).toThrow(VaultError);
84
+ expect(canonicalizePath("/prod//api/stripe/")).toBe("prod/api/stripe");
85
+ });
86
+
87
+ test("AAD path mismatch fails decrypt even with the correct DEK", async () => {
88
+ const dekBytes = generateDek();
89
+ const dek = await importAesKey(dekBytes);
90
+ const aad = buildAad("prod/api/stripe", 1, ALGORITHM, 1);
91
+ const sealed = await encryptSecret(dek, "plain", aad);
92
+ const wrongAad = buildAad("prod/api/other", 1, ALGORITHM, 1);
93
+ await expect(decryptSecret(dek, sealed, wrongAad)).rejects.toBeInstanceOf(VaultError);
94
+ });
95
+
96
+ test("VaultError from unwrap paths never exposes a cause chain", async () => {
97
+ const unsealer = createMemoryUnsealer(crypto.getRandomValues(new Uint8Array(32)));
98
+ const kek = await unsealer.unwrapKek();
99
+ const dekBytes = generateDek();
100
+ const aad = buildAad("p", 1, ALGORITHM, 1);
101
+ const wrapped = await wrapDek(kek, dekBytes, aad);
102
+ const other = createMemoryUnsealer(crypto.getRandomValues(new Uint8Array(32)));
103
+ const otherKek = await other.unwrapKek();
104
+ try {
105
+ await unwrapDek(otherKek, wrapped, aad);
106
+ expect.unreachable();
107
+ } catch (err) {
108
+ expect(err).toBeInstanceOf(VaultError);
109
+ expect((err as Error & { cause?: unknown }).cause).toBeUndefined();
110
+ expect(String(err)).not.toContain(Buffer.from(dekBytes).toString("base64"));
111
+ }
112
+ unsealer.seal();
113
+ other.seal();
114
+ });
115
+
116
+ test("audit verify detects a tampered row_hash", async () => {
117
+ const h = await harness();
118
+ try {
119
+ await h.adapter.set("x/y", "one");
120
+ await h.conn.exec(
121
+ "update oke_vault_audit set row_hash = $1 where seq = (select max(seq) from oke_vault_audit)",
122
+ ["0".repeat(64)],
123
+ );
124
+ const result = await h.adapter.verifyAudit();
125
+ expect(result.ok).toBe(false);
126
+ } finally {
127
+ await h.close();
128
+ }
129
+ });
130
+ });