okengine 0.10.3 → 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.
- package/package.json +76 -10
- package/site/content/docs/deployment/docker-swarm.mdx +48 -111
- package/site/content/docs/deployment/docker.mdx +43 -36
- package/site/content/docs/deployment/index.mdx +9 -9
- package/site/content/docs/deployment/kubernetes.mdx +4 -4
- package/site/content/docs/elements/ai.mdx +7 -7
- package/site/content/docs/elements/channel.mdx +9 -7
- package/site/content/docs/elements/clock.mdx +8 -8
- package/site/content/docs/elements/flow.mdx +46 -47
- package/site/content/docs/elements/gate.mdx +6 -5
- package/site/content/docs/elements/signal.mdx +2 -2
- package/site/content/docs/elements/store.mdx +71 -78
- package/site/content/docs/elements/vault.mdx +89 -70
- package/site/content/docs/get-started/basic-usage.mdx +8 -8
- package/site/content/docs/get-started/installation.mdx +36 -36
- package/site/content/docs/get-started/introduction.mdx +11 -0
- package/site/content/docs/get-started/why.mdx +2 -2
- package/site/content/docs/plugins/anonymous.mdx +1 -1
- package/site/content/docs/plugins/magic-link.mdx +11 -11
- package/site/content/docs/plugins/otp.mdx +2 -2
- package/site/content/docs/plugins/passkey.mdx +1 -1
- package/site/content/docs/plugins/two-factor.mdx +1 -1
- package/site/content/docs/plugins/username.mdx +1 -1
- package/site/content/docs/recipes/caddy.mdx +1 -1
- package/site/content/docs/recipes/cockroachdb.mdx +2 -2
- package/site/content/docs/recipes/dragonfly.mdx +1 -1
- package/site/content/docs/recipes/index.mdx +1 -2
- package/site/content/docs/recipes/llama-cpp.mdx +21 -23
- package/site/content/docs/recipes/mailpit.mdx +11 -11
- package/site/content/docs/recipes/meilisearch.mdx +10 -10
- package/site/content/docs/recipes/meta.json +0 -1
- package/site/content/docs/recipes/ollama.mdx +1 -1
- package/site/content/docs/recipes/pgdog.mdx +12 -9
- package/site/content/docs/recipes/postgres.mdx +3 -3
- package/site/content/docs/recipes/redis.mdx +2 -2
- package/site/content/docs/recipes/rustfs.mdx +10 -7
- package/site/content/docs/recipes/sglang.mdx +1 -1
- package/site/content/docs/recipes/supabase-docker.mdx +1 -1
- package/site/content/docs/recipes/timescale.mdx +2 -2
- package/site/content/docs/recipes/valkey.mdx +1 -1
- package/site/content/docs/recipes/vllm.mdx +1 -1
- package/site/content/docs/recipes/yugabytedb.mdx +2 -2
- package/site/content/docs/reference/cli.md +61 -38
- package/site/content/docs/reference/configuration.mdx +145 -70
- package/site/content/docs/reference/environment-variables.mdx +36 -28
- package/site/content/docs/reference/errors.mdx +28 -29
- package/site/content/docs/reference/fx.mdx +11 -4
- package/site/content/docs/reference/i18n.mdx +17 -6
- package/site/content/docs/reference/index.mdx +5 -0
- package/site/content/docs/reference/meta.json +1 -0
- package/site/content/docs/reference/migrating-environments.mdx +158 -0
- package/site/content/docs/reference/plugins.mdx +2 -2
- package/src/auth/gate-auth.test.ts +12 -10
- package/src/cli/ai-setup/ai-setup.test.ts +27 -15
- package/src/cli/ai-setup/apply.ts +111 -15
- package/src/cli/ai-setup/catalog.ts +3 -3
- package/src/cli/ai-setup/index.ts +1 -1
- package/src/cli/ai-setup/prompts.ts +1 -1
- package/src/cli/ai-setup/recommend.test.ts +1 -1
- package/src/cli/ask-seed.test.ts +3 -3
- package/src/cli/build.ts +1 -1
- package/src/cli/db-auto-push.test.ts +6 -3
- package/src/cli/db-auto-push.ts +7 -5
- package/src/cli/db-seed.ts +7 -8
- package/src/cli/db.test.ts +70 -71
- package/src/cli/db.ts +86 -12
- package/src/cli/dev-controller.test.ts +35 -0
- package/src/cli/dev-controller.ts +298 -0
- package/src/cli/dev-controls.ts +11 -65
- package/src/cli/dev-db-push.test.ts +47 -15
- package/src/cli/dev-schema-sync.test.ts +11 -11
- package/src/cli/dev-schema-sync.ts +10 -10
- package/src/cli/dev-session-lock.test.ts +106 -0
- package/src/cli/dev-session-lock.ts +209 -0
- package/src/cli/dev.test.ts +166 -121
- package/src/cli/dev.ts +138 -192
- package/src/cli/doc-staleness.test.ts +3 -6
- package/src/cli/docker-clean.test.ts +33 -1
- package/src/cli/docker-clean.ts +28 -33
- package/src/cli/docker-cli.test.ts +12 -11
- package/src/cli/docker.ts +95 -20
- package/src/cli/doctor.test.ts +40 -0
- package/src/cli/doctor.ts +55 -15
- package/src/cli/drizzle-env.test.ts +11 -13
- package/src/cli/drizzle-env.ts +18 -11
- package/src/cli/ensure-drizzle-config.ts +11 -14
- package/src/cli/hero-meta.test.ts +15 -15
- package/src/cli/hero-meta.ts +10 -9
- package/src/cli/index.ts +36 -6
- package/src/cli/load-config.images.test.ts +16 -12
- package/src/cli/load-config.source.test.ts +50 -0
- package/src/cli/load-config.ts +34 -28
- package/src/cli/meilisearch-local.ts +1 -2
- package/src/cli/mode.ts +7 -58
- package/src/cli/registry.ts +175 -19
- package/src/cli/resolve-dev-sql-env.test.ts +8 -16
- package/src/cli/resolve-dev-sql-env.ts +13 -15
- package/src/cli/safe-defaults.test.ts +28 -10
- package/src/cli/schema.ts +65 -16
- package/src/cli/test.ts +58 -0
- package/src/cli/tui/App.tsx +252 -0
- package/src/cli/tui/DevLive.tsx +116 -0
- package/src/cli/tui/components/ConfirmDialog.tsx +48 -0
- package/src/cli/tui/components/HeaderBar.tsx +67 -0
- package/src/cli/tui/components/LogStream.tsx +47 -0
- package/src/cli/tui/components/PanelCard.tsx +39 -0
- package/src/cli/tui/components/SelectRow.tsx +37 -0
- package/src/cli/tui/components/SlashPalette.tsx +172 -0
- package/src/cli/tui/components/Spinner.tsx +32 -0
- package/src/cli/tui/components/StatusBar.tsx +51 -0
- package/src/cli/tui/components/StatusDot.tsx +37 -0
- package/src/cli/tui/components/TabBar.tsx +49 -0
- package/src/cli/tui/components/Wordmark.tsx +43 -0
- package/src/cli/tui/format.ts +38 -0
- package/src/cli/tui/keys.test.ts +18 -0
- package/src/cli/tui/keys.ts +26 -0
- package/src/cli/tui/launch.test.ts +18 -0
- package/src/cli/tui/launch.ts +57 -0
- package/src/cli/tui/panels/Dashboard.tsx +201 -0
- package/src/cli/tui/panels/Database.tsx +198 -0
- package/src/cli/tui/panels/Dev.tsx +257 -0
- package/src/cli/tui/panels/Docker.tsx +357 -0
- package/src/cli/tui/panels/Navigator.test.ts +22 -0
- package/src/cli/tui/panels/Navigator.tsx +213 -0
- package/src/cli/tui/slash-catalog.test.ts +31 -0
- package/src/cli/tui/slash-catalog.ts +184 -0
- package/src/cli/tui/slash-run.ts +54 -0
- package/src/cli/tui/state/ports.ts +95 -0
- package/src/cli/tui/theme.ts +24 -0
- package/src/cli/vault-cmd.test.ts +402 -0
- package/src/cli/vault-cmd.ts +727 -69
- package/src/cli/vault-secure-input.test.ts +56 -0
- package/src/cli/vault-secure-input.ts +124 -0
- package/src/compiler/effects-infer.ts +7 -0
- package/src/compiler/fixtures/skyport/oke.config.ts +3 -3
- package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +1 -1
- package/src/compiler/fixtures/skyport.expected.json +1 -1
- package/src/compiler-entry.ts +7 -0
- package/src/config/define-config.test.ts +111 -47
- package/src/config/driver-defaults.test.ts +16 -23
- package/src/config/driver-defaults.ts +33 -63
- package/src/config/index.ts +169 -144
- package/src/config/resolve-driver.test.ts +14 -19
- package/src/console/server/app.ts +4 -2
- package/src/console/server/console.test.ts +58 -1
- package/src/console/server/flows.ts +22 -2
- package/src/console/server/index.ts +3 -0
- package/src/console/server/operator-db.test.ts +55 -13
- package/src/console/server/operator-db.ts +305 -263
- package/src/console/server/serve.ts +16 -7
- package/src/console/server/state.ts +31 -9
- package/src/console/server/vault.test.ts +120 -5
- package/src/console/server/vault.ts +208 -12
- package/src/console/ui/dist/assets/{index-D-9KFce5.js → index--Y0Uaz-m.js} +2 -2
- package/src/console/ui/dist/assets/panel-vault-BkZdJPX8.js +1 -0
- package/src/console/ui/dist/assets/style-C3L349iZ.css +3 -0
- package/src/console/ui/dist/index.html +2 -2
- package/src/console/ui/shell/client.ts +16 -0
- package/src/console/ui/shell/panels/vault/VaultPanel.tsx +54 -1
- package/src/console/ui/vault/VaultA11yView.tsx +33 -1
- package/src/console/ui/vault/backend.test.ts +75 -0
- package/src/console/ui/vault/backend.ts +117 -0
- package/src/console/ui/vault/fixture.ts +21 -2
- package/src/console/ui/vault/index.ts +11 -1
- package/src/console/ui/vault/types.ts +26 -0
- package/src/docker/compose.ts +164 -61
- package/src/docker/derive.ts +106 -12
- package/src/docker/docker.test.ts +156 -110
- package/src/docker/helpers.ts +98 -0
- package/src/docker/images-config.test.ts +3 -8
- package/src/docker/index.ts +30 -5
- package/src/docker/recipes/index.ts +0 -3
- package/src/docker/recipes/llama-cpp.ts +4 -4
- package/src/docker/recipes/pgdog.ts +9 -2
- package/src/docker/resources.test.ts +71 -0
- package/src/docker/resources.ts +176 -0
- package/src/docker/stack-id.test.ts +11 -4
- package/src/docker/stack-id.ts +42 -32
- package/src/docker/stack.integration.test.ts +4 -15
- package/src/docker/types.ts +35 -3
- package/src/drivers/ai-ollama.integration.test.ts +2 -2
- package/src/drivers/conformance.test.ts +11 -12
- package/src/drivers/drizzle-dialect.test.ts +0 -4
- package/src/drivers/drizzle-dialect.ts +7 -13
- package/src/drivers/index.ts +24 -7
- package/src/drivers/journal-postgres.test.ts +3 -1
- package/src/drivers/journal-postgres.ts +4 -2
- package/src/drivers/meilisearch.ts +1 -1
- package/src/drivers/pglite.ts +28 -5
- package/src/drivers/types.ts +9 -11
- package/src/drivers/vault-aws-secrets-manager.test.ts +159 -0
- package/src/drivers/vault-aws-secrets-manager.ts +333 -0
- package/src/drivers/vault-builtin.test.ts +110 -0
- package/src/drivers/vault-builtin.ts +184 -0
- package/src/drivers/vault-driver-removal.test.ts +13 -0
- package/src/drivers/vault-managed.test.ts +95 -0
- package/src/drivers/vault-managed.ts +101 -25
- package/src/drivers/vault-types.ts +25 -5
- package/src/elements/ai/runtime.ts +1 -1
- package/src/elements/clock/chaos.test.ts +4 -1
- package/src/elements/clock/durable.ts +57 -4
- package/src/elements/clock.test.ts +13 -14
- package/src/elements/gate/constants.ts +18 -0
- package/src/elements/gate.test.ts +1 -1
- package/src/elements/gate.ts +2 -2
- package/src/elements/store/domain-ddl.test.ts +5 -12
- package/src/elements/store/emit-drizzle.ts +44 -19
- package/src/elements/store/index-boot.test.ts +15 -116
- package/src/elements/store/load-plugin-tables.ts +1 -1
- package/src/elements/store/resource-list-docs.fixture.ts +12 -6
- package/src/elements/store/resource-list-docs.test.ts +64 -44
- package/src/elements/store/resource.test.ts +17 -10
- package/src/elements/store/runtime.ts +9 -12
- package/src/elements/store/schema-decl.test.ts +16 -32
- package/src/elements/store/seed.test.ts +1 -2
- package/src/elements/store/seed.ts +7 -8
- package/src/elements/store/sql-condition.test.ts +2 -2
- package/src/elements/store/sql-session.test.ts +8 -10
- package/src/elements/store/sql-session.ts +10 -2
- package/src/elements/store.test.ts +13 -13
- package/src/elements/vault/audit.ts +233 -0
- package/src/elements/vault/boot-chain.test.ts +78 -0
- package/src/elements/vault/boot-chain.ts +43 -39
- package/src/elements/vault/builtin-adapter.test.ts +479 -0
- package/src/elements/vault/builtin-adapter.ts +1184 -0
- package/src/elements/vault/crypto.test.ts +237 -0
- package/src/elements/vault/crypto.ts +431 -0
- package/src/elements/vault/declare.ts +164 -3
- package/src/elements/vault/errors.ts +56 -0
- package/src/elements/vault/kms-unseal.test.ts +76 -0
- package/src/elements/vault/kms-unseal.ts +181 -0
- package/src/elements/vault/path.ts +90 -0
- package/src/elements/vault/resilience.test.ts +102 -0
- package/src/elements/vault/resilience.ts +107 -0
- package/src/elements/vault/runtime.ts +36 -4
- package/src/elements/vault/security-checklist.test.ts +130 -0
- package/src/elements/vault/storage.ts +448 -0
- package/src/elements/vault/types.ts +288 -0
- package/src/elements/vault/unseal.ts +150 -0
- package/src/elements/vault.test.ts +72 -4
- package/src/elements/vault.ts +91 -2
- package/src/full.ts +248 -0
- package/src/http.ts +41 -0
- package/src/i18n/interpolate-simple.ts +37 -0
- package/src/i18n/messages.ts +51 -4
- package/src/i18n-entry.ts +7 -0
- package/src/index.ts +35 -145
- package/src/journal-entry.ts +20 -0
- package/src/kernel/adopt-barrel-fresh.test.ts +25 -10
- package/src/kernel/app.ts +40 -42
- package/src/kernel/auto-registry.test.ts +2 -2
- package/src/kernel/boot-bind/ai.test.ts +5 -5
- package/src/kernel/boot-bind/ai.ts +2 -1
- package/src/kernel/boot-bind/channel.test.ts +6 -6
- package/src/kernel/boot-bind/channel.ts +3 -10
- package/src/kernel/boot-bind/clock.ts +2 -2
- package/src/kernel/boot-bind/gate.ts +5 -9
- package/src/kernel/boot-bind/honor-config.test.ts +42 -42
- package/src/kernel/boot-bind/journal.ts +2 -2
- package/src/kernel/boot-bind/store.test.ts +43 -44
- package/src/kernel/boot-bind/store.ts +16 -41
- package/src/kernel/boot-bind/vault.ts +16 -1
- package/src/kernel/boot.test.ts +12 -4
- package/src/kernel/boot.ts +31 -19
- package/src/kernel/budget-entry.ts +1 -1
- package/src/kernel/compensate.test.ts +288 -1
- package/src/kernel/compensate.ts +126 -0
- package/src/kernel/concurrency.test.ts +10 -9
- package/src/kernel/effects-stamping.test.ts +27 -2
- package/src/kernel/element-registries.ts +5 -3
- package/src/kernel/flow.ts +4 -2
- package/src/kernel/fx.test.ts +119 -3
- package/src/kernel/fx.ts +215 -40
- package/src/kernel/index.ts +14 -0
- package/src/kernel/journal.ts +130 -7
- package/src/kernel/pipeline.test.ts +20 -6
- package/src/kernel/redacted.test.ts +35 -0
- package/src/kernel/redacted.ts +24 -2
- package/src/kernel/router/create-default.ts +16 -0
- package/src/kernel/router/create-edge.ts +18 -0
- package/src/kernel/router/create.ts +23 -0
- package/src/kernel/router/index.ts +23 -0
- package/src/kernel/router/linear.ts +88 -0
- package/src/kernel/router/regexp.ts +188 -0
- package/src/kernel/router/smart.ts +120 -0
- package/src/kernel/router/trie.ts +108 -0
- package/src/kernel/router/types.ts +56 -0
- package/src/kernel/router.ts +18 -532
- package/src/kernel-entry.ts +43 -0
- package/src/manifest/fixtures/skyport.excerpt.json +1 -1
- package/src/manifest/fixtures/skyport.manifest.json +1 -1
- package/src/plugins/config-source.test.ts +7 -7
- package/src/release/build-lib.ts +58 -0
- package/src/release/exports.test.ts +4 -2
- package/src/release/exports.ts +30 -4
- package/src/release/measure.ts +101 -22
- package/src/runs/duckdb.ts +7 -2
- package/src/shared/optional-peer.ts +41 -0
- package/src/term.test.ts +3 -44
- package/src/test/create-test-app.ts +12 -0
- package/src/test/provisions.integration.test.ts +1 -1
- package/src/test/reset-element-registries.ts +2 -0
- package/src/upgrade/codemods.test.ts +7 -20
- package/src/upgrade/codemods.ts +93 -60
- package/site/content/docs/recipes/openbao.mdx +0 -141
- package/src/cli/ask-dev-mode.ts +0 -40
- package/src/cli/dev-mode.test.ts +0 -45
- package/src/cli/dev-mode.ts +0 -78
- package/src/cli/openbao-bootstrap.test.ts +0 -150
- package/src/cli/openbao-bootstrap.ts +0 -321
- package/src/cli/openbao-restart.integration.test.ts +0 -145
- package/src/console/ui/dist/assets/panel-vault-Za1GyJfM.js +0 -1
- package/src/console/ui/dist/assets/style-C8MxEWPd.css +0 -3
- package/src/docker/recipes/openbao.ts +0 -47
- package/src/drivers/libsql.ts +0 -179
- package/src/drivers/sqlite.ts +0 -44
- package/src/drivers/vault-openbao.test.ts +0 -97
- package/src/drivers/vault-openbao.ts +0 -143
package/src/upgrade/codemods.ts
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
* `oke upgrade` runs every applicable transform and prints a diff.
|
|
5
5
|
* Adding a breaking change without a codemod is a process defect; the
|
|
6
6
|
* registry is the checklist.
|
|
7
|
+
*
|
|
8
|
+
* Environment-model migrations (`local`/`docker` → `dev`, sqlite removal)
|
|
9
|
+
* are documented only — see site docs `reference/migrating-environments`.
|
|
10
|
+
* No soft-compat rewriter for those.
|
|
7
11
|
*/
|
|
8
12
|
|
|
9
13
|
import { readdir } from "node:fs/promises";
|
|
@@ -38,49 +42,7 @@ export interface Codemod {
|
|
|
38
42
|
}
|
|
39
43
|
|
|
40
44
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* Vault `dev:` fallbacks are intentionally excluded — callers pass only
|
|
44
|
-
* `oke.config.*` paths into this rewriter.
|
|
45
|
-
*
|
|
46
|
-
* @param source - File contents
|
|
47
|
-
*/
|
|
48
|
-
export function rewriteConfigEnvKeys(source: string): string {
|
|
49
|
-
return source
|
|
50
|
-
.replace(/(^|[^\w.])dev(\s*:)/gm, "$1local$2")
|
|
51
|
-
.replace(/(^|[^\w.])stack(\s*:)/gm, "$1docker$2");
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Rewrite removed vault stack markers to docker equivalents.
|
|
56
|
-
*
|
|
57
|
-
* @param source - File contents
|
|
58
|
-
*/
|
|
59
|
-
export function rewriteFromStackMarkers(source: string): string {
|
|
60
|
-
return source
|
|
61
|
-
.replace(/\bfromStack\b/g, "fromDocker")
|
|
62
|
-
.replace(/\bFROM_STACK_PREFIX\b/g, "FROM_DOCKER_PREFIX")
|
|
63
|
-
.replace(/\bisFromStack\b/g, "isFromDocker")
|
|
64
|
-
.replace(/\bfromStackRole\b/g, "fromDockerRole")
|
|
65
|
-
.replace(/__oke_from_stack__/g, "__oke_from_docker__");
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Collect `oke.config.*` files under a project root.
|
|
70
|
-
*
|
|
71
|
-
* @param cwd - Project root
|
|
72
|
-
*/
|
|
73
|
-
async function collectConfigCandidates(cwd: string): Promise<string[]> {
|
|
74
|
-
const out: string[] = [];
|
|
75
|
-
for (const name of ["oke.config.ts", "oke.config.mts", "oke.config.js"]) {
|
|
76
|
-
const path = resolve(cwd, name);
|
|
77
|
-
if (await Bun.file(path).exists()) out.push(path);
|
|
78
|
-
}
|
|
79
|
-
return out;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Collect TypeScript sources that may reference `fromStack`.
|
|
45
|
+
* Collect TypeScript sources under `src` / `app`.
|
|
84
46
|
*
|
|
85
47
|
* @param cwd - Project root
|
|
86
48
|
*/
|
|
@@ -116,30 +78,101 @@ async function walkTs(dir: string, out: string[]): Promise<void> {
|
|
|
116
78
|
}
|
|
117
79
|
|
|
118
80
|
/**
|
|
119
|
-
*
|
|
81
|
+
* Rewrite deep `okengine` imports that left the thin root to subpaths.
|
|
82
|
+
*
|
|
83
|
+
* @param source - File contents
|
|
120
84
|
*/
|
|
85
|
+
export function rewriteThinRootImports(source: string): string {
|
|
86
|
+
const movers: readonly { readonly names: readonly string[]; readonly from: string }[] = [
|
|
87
|
+
{
|
|
88
|
+
names: [
|
|
89
|
+
"createRunsRuntime",
|
|
90
|
+
"collectWideEvent",
|
|
91
|
+
"createRunTelemetry",
|
|
92
|
+
"explainOutliers",
|
|
93
|
+
"filesRunsDriver",
|
|
94
|
+
"memoryRunsDriver",
|
|
95
|
+
"postgresRunsDriver",
|
|
96
|
+
"clickhouseRunsDriver",
|
|
97
|
+
"openDuckDB",
|
|
98
|
+
"duckQuery",
|
|
99
|
+
],
|
|
100
|
+
from: "okengine/runs",
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
names: [
|
|
104
|
+
"createJournal",
|
|
105
|
+
"createMemoryJournalStore",
|
|
106
|
+
"createFileJournalStore",
|
|
107
|
+
"hasJournalLease",
|
|
108
|
+
"JOURNAL_DEFAULT_LEASE_MS",
|
|
109
|
+
],
|
|
110
|
+
from: "okengine/journal",
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
names: ["compileAot", "compileDynamic", "compileRoute", "sucrose", "extractManifest"],
|
|
114
|
+
from: "okengine/compiler",
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
names: ["formatMessage", "clearMessageFormatCache", "resolveFailureMessage"],
|
|
118
|
+
from: "okengine/i18n",
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
names: [
|
|
122
|
+
"bootApplication",
|
|
123
|
+
"mintCapabilities",
|
|
124
|
+
"createFx",
|
|
125
|
+
"createRouter",
|
|
126
|
+
"CONSOLE_PORT",
|
|
127
|
+
"MCP_PORT",
|
|
128
|
+
],
|
|
129
|
+
from: "okengine/full",
|
|
130
|
+
},
|
|
131
|
+
];
|
|
132
|
+
|
|
133
|
+
let text = source;
|
|
134
|
+
for (const mover of movers) {
|
|
135
|
+
const re = new RegExp(`import\\s*\\{([^}]*)\\}\\s*from\\s*(["'])okengine\\2`, "g");
|
|
136
|
+
text = text.replace(re, (full, body: string, quote: string) => {
|
|
137
|
+
const parts = body
|
|
138
|
+
.split(",")
|
|
139
|
+
.map((p) => p.trim())
|
|
140
|
+
.filter(Boolean);
|
|
141
|
+
const keep: string[] = [];
|
|
142
|
+
const move: string[] = [];
|
|
143
|
+
for (const part of parts) {
|
|
144
|
+
const base =
|
|
145
|
+
part
|
|
146
|
+
.replace(/^type\s+/, "")
|
|
147
|
+
.split(/\s+as\s+/)[0]
|
|
148
|
+
?.trim() ?? part;
|
|
149
|
+
if (mover.names.includes(base)) move.push(part);
|
|
150
|
+
else keep.push(part);
|
|
151
|
+
}
|
|
152
|
+
if (move.length === 0) return full;
|
|
153
|
+
const lines: string[] = [];
|
|
154
|
+
if (keep.length > 0) {
|
|
155
|
+
lines.push(`import { ${keep.join(", ")} } from ${quote}okengine${quote}`);
|
|
156
|
+
}
|
|
157
|
+
lines.push(`import { ${move.join(", ")} } from ${quote}${mover.from}${quote}`);
|
|
158
|
+
return lines.join(";\n");
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return text;
|
|
162
|
+
}
|
|
163
|
+
|
|
121
164
|
export const CODEMODS: readonly Codemod[] = [
|
|
122
165
|
{
|
|
123
|
-
id: "0.
|
|
124
|
-
from: "0.2
|
|
125
|
-
to: "0.
|
|
126
|
-
description:
|
|
166
|
+
id: "0.10.3-thin-root-subpaths",
|
|
167
|
+
from: "0.10.2",
|
|
168
|
+
to: "0.10.3",
|
|
169
|
+
description:
|
|
170
|
+
"Move deep okengine imports (runs/journal/compiler/i18n/boot helpers) to okengine/* subpaths after thin root",
|
|
127
171
|
async apply(cwd) {
|
|
128
172
|
const changes: CodemodChange[] = [];
|
|
129
|
-
for (const path of await collectConfigCandidates(cwd)) {
|
|
130
|
-
const before = await Bun.file(path).text();
|
|
131
|
-
const after = rewriteConfigEnvKeys(before);
|
|
132
|
-
if (after !== before) {
|
|
133
|
-
changes.push({
|
|
134
|
-
path: relative(cwd, path) || path,
|
|
135
|
-
before,
|
|
136
|
-
after,
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
173
|
for (const path of await collectVaultCandidates(cwd)) {
|
|
141
174
|
const before = await Bun.file(path).text();
|
|
142
|
-
const after =
|
|
175
|
+
const after = rewriteThinRootImports(before);
|
|
143
176
|
if (after !== before) {
|
|
144
177
|
changes.push({
|
|
145
178
|
path: relative(cwd, path) || path,
|
|
@@ -1,141 +0,0 @@
|
|
|
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>
|
package/src/cli/ask-dev-mode.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* One-time interactive `oke dev` mode choice via `@clack/prompts`.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { isCancel, select } from "@clack/prompts";
|
|
6
|
-
import type { DevMode } from "./dev-mode.ts";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Injectable ask step — production uses {@link askDevMode}.
|
|
10
|
-
*
|
|
11
|
-
* @returns Chosen mode, or `null` if the user cancelled
|
|
12
|
-
*/
|
|
13
|
-
export type AskDevModeFn = () => Promise<DevMode | null>;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Prompt once: local vs docker.
|
|
17
|
-
*
|
|
18
|
-
* @returns Chosen mode, or `null` on cancel
|
|
19
|
-
*/
|
|
20
|
-
export async function askDevMode(): Promise<DevMode | null> {
|
|
21
|
-
const value = await select({
|
|
22
|
-
message: "Run against",
|
|
23
|
-
options: [
|
|
24
|
-
{
|
|
25
|
-
value: "local",
|
|
26
|
-
label: "local",
|
|
27
|
-
hint: "sqlite file, instant, no Docker needed",
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
value: "docker",
|
|
31
|
-
label: "docker",
|
|
32
|
-
hint: "real Postgres/Redis via compose, closer to production",
|
|
33
|
-
},
|
|
34
|
-
],
|
|
35
|
-
initialValue: "local",
|
|
36
|
-
});
|
|
37
|
-
if (isCancel(value)) return null;
|
|
38
|
-
if (value === "local" || value === "docker") return value;
|
|
39
|
-
return null;
|
|
40
|
-
}
|
package/src/cli/dev-mode.test.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `.oke/mode` persistence and prompt gating.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { afterEach, describe, expect, test } from "bun:test";
|
|
6
|
-
import { mkdtemp, rm } from "node:fs/promises";
|
|
7
|
-
import { tmpdir } from "node:os";
|
|
8
|
-
import { join } from "node:path";
|
|
9
|
-
import { parseDevMode, readDevMode, shouldAskDevMode, writeDevMode } from "./dev-mode.ts";
|
|
10
|
-
|
|
11
|
-
const dirs: string[] = [];
|
|
12
|
-
|
|
13
|
-
afterEach(async () => {
|
|
14
|
-
await Promise.all(dirs.splice(0).map((d) => rm(d, { recursive: true, force: true })));
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
async function tempDir(): Promise<string> {
|
|
18
|
-
const dir = await mkdtemp(join(tmpdir(), "oke-mode-"));
|
|
19
|
-
dirs.push(dir);
|
|
20
|
-
return dir;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
describe("dev-mode", () => {
|
|
24
|
-
test("parseDevMode accepts local|docker only", () => {
|
|
25
|
-
expect(parseDevMode("local")).toBe("local");
|
|
26
|
-
expect(parseDevMode(" docker \n")).toBe("docker");
|
|
27
|
-
expect(parseDevMode("stack")).toBeNull();
|
|
28
|
-
expect(parseDevMode(undefined)).toBeNull();
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
test("read/write round-trip", async () => {
|
|
32
|
-
const dir = await tempDir();
|
|
33
|
-
expect(await readDevMode(dir)).toBeNull();
|
|
34
|
-
await writeDevMode(dir, "docker");
|
|
35
|
-
expect(await readDevMode(dir)).toBe("docker");
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
test("shouldAskDevMode: TTY + unset → ask; non-TTY → never", () => {
|
|
39
|
-
expect(shouldAskDevMode({ saved: null, explicit: false, stdinIsTTY: true })).toBe(true);
|
|
40
|
-
expect(shouldAskDevMode({ saved: null, explicit: false, stdinIsTTY: false })).toBe(false);
|
|
41
|
-
expect(shouldAskDevMode({ saved: null, explicit: false, stdinIsTTY: undefined })).toBe(false);
|
|
42
|
-
expect(shouldAskDevMode({ saved: "local", explicit: false, stdinIsTTY: true })).toBe(false);
|
|
43
|
-
expect(shouldAskDevMode({ saved: null, explicit: true, stdinIsTTY: true })).toBe(false);
|
|
44
|
-
});
|
|
45
|
-
});
|
package/src/cli/dev-mode.ts
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Persisted `oke dev` run mode — `local` vs `docker`.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { mkdir } from "node:fs/promises";
|
|
6
|
-
import { join, resolve } from "node:path";
|
|
7
|
-
|
|
8
|
-
/** Saved / selectable `oke dev` infrastructure mode. */
|
|
9
|
-
export type DevMode = "local" | "docker";
|
|
10
|
-
|
|
11
|
-
/** Relative path under the project root. */
|
|
12
|
-
export const DEV_MODE_RELATIVE_PATH = ".oke/mode";
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Absolute path to the mode preference file.
|
|
16
|
-
*
|
|
17
|
-
* @param cwd - Project root
|
|
18
|
-
*/
|
|
19
|
-
export function devModePath(cwd: string): string {
|
|
20
|
-
return resolve(cwd, DEV_MODE_RELATIVE_PATH);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Parse a raw mode string.
|
|
25
|
-
*
|
|
26
|
-
* @param raw - File contents or CLI arg
|
|
27
|
-
*/
|
|
28
|
-
export function parseDevMode(raw: string | undefined | null): DevMode | null {
|
|
29
|
-
if (raw === undefined || raw === null) return null;
|
|
30
|
-
const value = raw.trim().toLowerCase();
|
|
31
|
-
if (value === "local" || value === "docker") return value;
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Read the saved mode from `.oke/mode`, or `null` when unset / invalid.
|
|
37
|
-
*
|
|
38
|
-
* @param cwd - Project root
|
|
39
|
-
*/
|
|
40
|
-
export async function readDevMode(cwd: string): Promise<DevMode | null> {
|
|
41
|
-
const path = devModePath(cwd);
|
|
42
|
-
const file = Bun.file(path);
|
|
43
|
-
if (!(await file.exists())) return null;
|
|
44
|
-
try {
|
|
45
|
-
return parseDevMode(await file.text());
|
|
46
|
-
} catch {
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Persist the preferred mode for future `oke dev` invocations.
|
|
53
|
-
*
|
|
54
|
-
* @param cwd - Project root
|
|
55
|
-
* @param mode - Mode to save
|
|
56
|
-
*/
|
|
57
|
-
export async function writeDevMode(cwd: string, mode: DevMode): Promise<void> {
|
|
58
|
-
const path = devModePath(cwd);
|
|
59
|
-
await mkdir(join(cwd, ".oke"), { recursive: true });
|
|
60
|
-
await Bun.write(path, `${mode}\n`);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Whether `oke dev` should open the one-time mode prompt.
|
|
65
|
-
*
|
|
66
|
-
* Mirrors create-oke: never prompt when stdin is not a TTY.
|
|
67
|
-
*
|
|
68
|
-
* @param options - Preference + TTY + explicit flag
|
|
69
|
-
*/
|
|
70
|
-
export function shouldAskDevMode(options: {
|
|
71
|
-
readonly saved: DevMode | null;
|
|
72
|
-
readonly explicit: boolean;
|
|
73
|
-
readonly stdinIsTTY: boolean | undefined;
|
|
74
|
-
}): boolean {
|
|
75
|
-
if (options.explicit) return false;
|
|
76
|
-
if (options.saved !== null) return false;
|
|
77
|
-
return Boolean(options.stdinIsTTY);
|
|
78
|
-
}
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OpenBao bootstrap — material durability gates (`.oke/` ignore + real
|
|
3
|
-
* `0600`, init-then-write-fail must not report success, initialized-without-
|
|
4
|
-
* key fails loud).
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { afterEach, describe, expect, test } from "bun:test";
|
|
8
|
-
import { mkdtemp, rm } from "node:fs/promises";
|
|
9
|
-
import { statSync } from "node:fs";
|
|
10
|
-
import { tmpdir } from "node:os";
|
|
11
|
-
import { join } from "node:path";
|
|
12
|
-
import {
|
|
13
|
-
assertMode,
|
|
14
|
-
ensureOpenBao,
|
|
15
|
-
OpenBaoBootstrapError,
|
|
16
|
-
OPENBAO_STATE_DIR_REL,
|
|
17
|
-
} from "./openbao-bootstrap.ts";
|
|
18
|
-
|
|
19
|
-
let dirs: string[] = [];
|
|
20
|
-
|
|
21
|
-
async function tempDir(): Promise<string> {
|
|
22
|
-
const dir = await mkdtemp(join(tmpdir(), "oke-openbao-boot-"));
|
|
23
|
-
dirs.push(dir);
|
|
24
|
-
return dir;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
afterEach(async () => {
|
|
28
|
-
for (const dir of dirs) await rm(dir, { recursive: true, force: true });
|
|
29
|
-
dirs = [];
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
/** Scripted fake OpenBao (seal-status → init → unseal → policy → token). */
|
|
33
|
-
function fakeOpenBao(calls: { url: string; method: string; body?: string }[]) {
|
|
34
|
-
const routes: Record<string, () => Response> = {
|
|
35
|
-
"GET /v1/sys/seal-status": () =>
|
|
36
|
-
Response.json({ sealed: false, initialized: false, t: 1, n: 1, progress: 0 }),
|
|
37
|
-
"GET /v1/sys/health": () =>
|
|
38
|
-
Response.json({ initialized: true, sealed: false, standby: false }, { status: 200 }),
|
|
39
|
-
"POST /v1/sys/init": () =>
|
|
40
|
-
Response.json({ keys: ["unseal-key-1"], keys_base64: ["dW5zZWFs"], root_token: "root-tok" }),
|
|
41
|
-
"POST /v1/sys/unseal": () => Response.json({ sealed: false, t: 1, n: 1, progress: 0 }),
|
|
42
|
-
"POST /v1/sys/mounts/secret": () => Response.json({}),
|
|
43
|
-
"POST /v1/sys/policy/oke-app": () => new Response(null, { status: 204 }),
|
|
44
|
-
"POST /v1/auth/token/create": () =>
|
|
45
|
-
Response.json({ auth: { client_token: "app-tok", policies: ["oke-app"] } }),
|
|
46
|
-
};
|
|
47
|
-
const fetchFn = (async (input: string | URL | Request, init?: RequestInit) => {
|
|
48
|
-
const url = String(input);
|
|
49
|
-
const method = init?.method ?? "GET";
|
|
50
|
-
calls.push({ url, method, body: typeof init?.body === "string" ? init.body : undefined });
|
|
51
|
-
const path = new URL(url).pathname;
|
|
52
|
-
const handler = routes[`${method} ${path}`];
|
|
53
|
-
if (!handler) return new Response("not found", { status: 404 });
|
|
54
|
-
return handler();
|
|
55
|
-
}) as typeof globalThis.fetch;
|
|
56
|
-
return fetchFn;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
describe("openbao bootstrap material gates", () => {
|
|
60
|
-
test("`.oke/` is gitignored (check-ignore, not inference)", () => {
|
|
61
|
-
const root = join(import.meta.dir, "../..");
|
|
62
|
-
for (const p of [
|
|
63
|
-
`${OPENBAO_STATE_DIR_REL}/unseal.key`,
|
|
64
|
-
`${OPENBAO_STATE_DIR_REL}/root.token`,
|
|
65
|
-
]) {
|
|
66
|
-
const proc = Bun.spawnSync(["git", "check-ignore", "-v", p], {
|
|
67
|
-
cwd: root,
|
|
68
|
-
stdout: "pipe",
|
|
69
|
-
stderr: "pipe",
|
|
70
|
-
});
|
|
71
|
-
expect(proc.exitCode).toBe(0);
|
|
72
|
-
expect(proc.stdout.toString()).toContain(".gitignore");
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
test("init writes 0600 files + 0700 dir (real mode) before success", async () => {
|
|
77
|
-
const cwd = await tempDir();
|
|
78
|
-
const calls: { url: string; method: string; body?: string }[] = [];
|
|
79
|
-
const result = await ensureOpenBao({
|
|
80
|
-
cwd,
|
|
81
|
-
url: "http://127.0.0.1:8200",
|
|
82
|
-
names: ["STRIPE_KEY", "DATABASE_URL"],
|
|
83
|
-
fetch: fakeOpenBao(calls),
|
|
84
|
-
});
|
|
85
|
-
expect(result.initializedNow).toBe(true);
|
|
86
|
-
expect(result.appToken).toBe("app-tok");
|
|
87
|
-
|
|
88
|
-
const stateDir = join(cwd, OPENBAO_STATE_DIR_REL);
|
|
89
|
-
expect(statSync(stateDir).mode & 0o777).toBe(0o700);
|
|
90
|
-
expect(statSync(join(stateDir, "unseal.key")).mode & 0o777).toBe(0o600);
|
|
91
|
-
expect(statSync(join(stateDir, "root.token")).mode & 0o777).toBe(0o600);
|
|
92
|
-
expect(statSync(join(stateDir, "app.token")).mode & 0o777).toBe(0o600);
|
|
93
|
-
assertMode(join(stateDir, "unseal.key"), 0o600);
|
|
94
|
-
|
|
95
|
-
// Policy is scoped to the two declared paths only.
|
|
96
|
-
const policyCall = calls.find((c) => c.url.endsWith("/v1/sys/policy/oke-app"));
|
|
97
|
-
expect(policyCall?.body).toContain("secret/data/STRIPE_KEY");
|
|
98
|
-
expect(policyCall?.body).toContain("secret/data/DATABASE_URL");
|
|
99
|
-
expect(policyCall?.body).not.toContain("secret/data/*");
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test("post-init material write failure does NOT report success", async () => {
|
|
103
|
-
const cwd = await tempDir();
|
|
104
|
-
// Block the state dir with a *file* so mkdir/write fails after init.
|
|
105
|
-
const blocker = join(cwd, ".oke");
|
|
106
|
-
await Bun.write(blocker, "not-a-dir");
|
|
107
|
-
|
|
108
|
-
const calls: { url: string; method: string; body?: string }[] = [];
|
|
109
|
-
let failed: unknown;
|
|
110
|
-
try {
|
|
111
|
-
await ensureOpenBao({
|
|
112
|
-
cwd,
|
|
113
|
-
url: "http://127.0.0.1:8200",
|
|
114
|
-
names: ["STRIPE_KEY"],
|
|
115
|
-
fetch: fakeOpenBao(calls),
|
|
116
|
-
});
|
|
117
|
-
} catch (err) {
|
|
118
|
-
failed = err;
|
|
119
|
-
}
|
|
120
|
-
expect(failed).toBeDefined();
|
|
121
|
-
// init happened (vault now initialized server-side) but we never returned
|
|
122
|
-
// success without durable material.
|
|
123
|
-
expect(calls.some((c) => c.url.endsWith("/v1/sys/init"))).toBe(true);
|
|
124
|
-
expect(calls.some((c) => c.url.endsWith("/v1/auth/token/create"))).toBe(false);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
test("initialized but host unseal.key missing → loud permanent-loss error", async () => {
|
|
128
|
-
const cwd = await tempDir();
|
|
129
|
-
const calls: { url: string; method: string; body?: string }[] = [];
|
|
130
|
-
const fetchFn = fakeOpenBao(calls);
|
|
131
|
-
// Seal-status says initialized (data exists) but the host has no material.
|
|
132
|
-
const initialized = (async (input: string | URL | Request, init?: RequestInit) => {
|
|
133
|
-
const url = String(input);
|
|
134
|
-
if (url.endsWith("/v1/sys/seal-status")) {
|
|
135
|
-
return Response.json({ sealed: true, initialized: true });
|
|
136
|
-
}
|
|
137
|
-
return fetchFn(input as string, init);
|
|
138
|
-
}) as typeof globalThis.fetch;
|
|
139
|
-
|
|
140
|
-
let failed: unknown;
|
|
141
|
-
try {
|
|
142
|
-
await ensureOpenBao({ cwd, url: "http://127.0.0.1:8200", fetch: initialized });
|
|
143
|
-
} catch (err) {
|
|
144
|
-
failed = err;
|
|
145
|
-
}
|
|
146
|
-
expect(failed).toBeInstanceOf(OpenBaoBootstrapError);
|
|
147
|
-
expect(String(failed)).toContain("unrecoverable");
|
|
148
|
-
expect(String(failed)).toContain("compose.vault.yml down -v");
|
|
149
|
-
});
|
|
150
|
-
});
|