@k9o/kibo 1.2.0 → 1.3.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/dist/index.js CHANGED
@@ -22,9 +22,15 @@ function requireProjectSlug(config) {
22
22
  }
23
23
  return config.projectSlug;
24
24
  }
25
+ var MAX_GET_PAGES = 20;
25
26
  function makeVaultNamespace(config) {
27
+ const listSecrets = async (input) => request(config, {
28
+ method: "GET",
29
+ path: projectPath(requireProjectSlug(config), "vault/secrets"),
30
+ query: { prefix: input?.prefix, cursor: input?.cursor, limit: input?.limit }
31
+ });
26
32
  return {
27
- /** ZK-DEK opaque store/fetch (F.5). */
33
+ /** Opaque store/fetch of a customer-wrapped DEK envelope. */
28
34
  zkDeks: {
29
35
  /**
30
36
  * Store an opaque customer-wrapped DEK envelope.
@@ -51,55 +57,47 @@ function makeVaultNamespace(config) {
51
57
  }
52
58
  },
53
59
  /**
54
- * typed secrets surface (`services/gateway/src/routes/vault-secrets.ts`) — the full family:
55
- * list/get/create/revealChallenge/reveal/update/rotate/move/delete. Every operation on a NAMED
56
- * secret (everything except `list`) requires `region` — live-verified against staging, not
57
- * documented in the route's OpenAPI schema. `list` is the one exception (verified it does NOT
58
- * need `region`).
60
+ * Typed secrets surface (`/@<projectSlug>/vault/secrets`): list / get / create / revealChallenge / reveal /
61
+ * update / rotate / move / delete.
62
+ *
63
+ * `region`: the gateway sets the residency region of every vault call itself and discards a caller-supplied
64
+ * `region` query parameter, so it is optional on every method. When given, it is still sent (older gateways
65
+ * required it).
59
66
  *
60
- * Response shapes for `update`/`rotate`/`move` are typed to what the gateway ACTUALLY returns
61
- * (live-verified), not the OpenAPI's documented (and inaccurate for these three) `SecretSummary` —
62
- * see the 🔴 notes on `UpdateSecretResult`/`RotateSecretResult`/`MoveSecretResult` in `types.ts`.
67
+ * Response shapes for `update`/`rotate`/`move` are typed to what the gateway returns, not the OpenAPI's
68
+ * `SecretSummary` (see `UpdateSecretResult`/`RotateSecretResult`/`MoveSecretResult` in `types.ts`).
63
69
  */
64
70
  secrets: {
65
- /** List secrets (metadata only — never lists ciphertext). `GET /@<projectSlug>/vault/secrets`.
66
- * The one secrets operation that does NOT need `region` (live-verified). */
67
- async list(input) {
68
- return request(config, {
69
- method: "GET",
70
- path: projectPath(requireProjectSlug(config), "vault/secrets"),
71
- query: { prefix: input?.prefix, cursor: input?.cursor, limit: input?.limit }
72
- });
73
- },
71
+ /** List secrets (metadata only, never ciphertext). `GET /@<projectSlug>/vault/secrets`. */
72
+ list: listSecrets,
74
73
  /**
75
- * Read a secret's metadata (never its value — use `revealChallenge`+`reveal` for that).
76
- * `GET /@<projectSlug>/vault/secrets/<name>?region=<region>`.
74
+ * Read one secret's metadata (never its value; use `revealChallenge` + `reveal` for that).
77
75
  *
78
- * 🔴 KNOWN LIVE BUG (staging, 2026-09-05): this call currently fails with
79
- * `K9O_REVEAL_CHALLENGE_REQUIRED` on every attempt — the gateway/kibo does not honor the
80
- * documented "no `X-K9O-Reveal-Challenge` header ⇒ return metadata" behavior; it demands a
81
- * challenge unconditionally. Implemented per the documented contract (this is a backend defect,
82
- * not a client workaround); `list({prefix: exactName})` is the current working alternative for a
83
- * single secret's metadata until the gateway/kibo side is fixed.
76
+ * Vault answers a `GET /@<projectSlug>/vault/secrets/<name>` only as a reveal: with no
77
+ * `X-K9O-Reveal-Challenge` header it refuses with 401 `K9O_REVEAL_CHALLENGE_REQUIRED`, whatever the caller's
78
+ * rights. There is no plain metadata read on that route, so `get` reads the metadata the route that exists
79
+ * for it returns: it lists by the exact name as prefix and picks the exact match. It throws `K9O_NOT_FOUND`
80
+ * (status 404) when no secret has that name.
84
81
  */
85
- async get(name, opts) {
86
- return request(config, {
87
- method: "GET",
88
- path: projectPath(requireProjectSlug(config), `vault/secrets/${encodeURIComponent(name)}`),
89
- query: { region: opts.region }
82
+ async get(name, _opts) {
83
+ let cursor;
84
+ for (let page = 0; page < MAX_GET_PAGES; page++) {
85
+ const env = await listSecrets({ prefix: name, ...cursor !== void 0 ? { cursor } : {}, limit: 100 });
86
+ const hit = env.data.secrets.find((s) => s.name === name);
87
+ if (hit !== void 0) return { ...env, data: hit };
88
+ if (env.data.nextCursor === null || env.data.nextCursor === void 0) break;
89
+ cursor = env.data.nextCursor;
90
+ }
91
+ throw new KError({
92
+ code: "K9O_NOT_FOUND",
93
+ message: `No secret named '${name}' in this project.`,
94
+ context: { name },
95
+ status: 404
90
96
  });
91
97
  },
92
98
  /**
93
99
  * Create a secret under the caller's own auth.
94
- * `POST /@<projectSlug>/vault/secrets/<name>?region=<region>` → the `sec_<base62>` summary.
95
- *
96
- * 🔴 `region` is REQUIRED on the query string, live-verified against staging — the vault-forward
97
- * proxy fail-closes with `K9O_INVALID_FIELD_VALUE` ("this request requires an authoritative
98
- * region") without it. Distinct from any `region` carried inside `input.value`'s envelope (e.g.
99
- * the BYOS aws_sigv4 STS-signing region): this one scopes the vault WRITE itself, mirroring the
100
- * gateway's `gatewayRegion()` residency guard (region-guard.ts) that closed a 2026-08 wrong-region
101
- * incident for the audit path — the request-shape unit tests never caught this because they don't
102
- * exercise a real gateway.
100
+ * `POST /@<projectSlug>/vault/secrets/<name>` -> the `sec_<base62>` summary.
103
101
  */
104
102
  async create(input) {
105
103
  const { name, region, value, ...rest } = input;
@@ -107,26 +105,24 @@ function makeVaultNamespace(config) {
107
105
  method: "POST",
108
106
  path: projectPath(requireProjectSlug(config), `vault/secrets/${encodeURIComponent(name)}`),
109
107
  query: { region },
110
- // Live-verified against staging: the wire `value` MUST be base64 — a raw plaintext string
111
- // (matching the route's own "the plaintext to seal" OpenAPI description literally) is
112
- // rejected with K9O_INVALID_FIELD_VALUE "Request body could not be parsed." `input.value`
113
- // stays the caller-facing plaintext; base64 is purely a wire-transport detail handled here.
108
+ // The wire `value` is base64 (a raw string is rejected as unparseable). `input.value` stays the
109
+ // caller-facing plaintext; base64 is purely a wire detail handled here.
114
110
  body: { ...rest, value: encodeBase64Utf8(value) }
115
111
  });
116
112
  },
117
- /** Mint a single-use reveal challenge — present it as `X-K9O-Reveal-Challenge` on the
118
- * immediately-following `reveal()` call. `POST .../<name>?op=reveal-challenge&region=<region>`. */
113
+ /** Mint a single-use reveal challenge; pass it to the immediately-following `reveal()`.
114
+ * `POST .../<name>?op=reveal-challenge`. The optional `reason` is accepted by the gateway and not forwarded. */
119
115
  async revealChallenge(name, opts) {
120
116
  return request(config, {
121
117
  method: "POST",
122
118
  path: projectPath(requireProjectSlug(config), `vault/secrets/${encodeURIComponent(name)}`),
123
- query: { op: "reveal-challenge", region: opts.region }
119
+ query: { op: "reveal-challenge", region: opts?.region },
120
+ ...opts?.reason !== void 0 ? { body: { reason: opts.reason } } : {}
124
121
  });
125
122
  },
126
123
  /** Reveal a secret's plaintext value using a single-use challenge from `revealChallenge()`.
127
- * `GET .../<name>?region=<region>` with `X-K9O-Reveal-Challenge`. Decodes the wire's base64
128
- * `value` back to plaintext (live-verified: the reveal response's `value` is base64, symmetric
129
- * with `create`'s encode). */
124
+ * `GET .../<name>` with `X-K9O-Reveal-Challenge`. Decodes the wire's base64 `value` back to plaintext
125
+ * (symmetric with `create`'s encode). */
130
126
  async reveal(name, opts) {
131
127
  const envelope = await request(config, {
132
128
  method: "GET",
@@ -136,9 +132,8 @@ function makeVaultNamespace(config) {
136
132
  });
137
133
  return { ...envelope, data: { ...envelope.data, value: decodeBase64Utf8(envelope.data.value) } };
138
134
  },
139
- /** Update a secret's value (bumps version) — owner-only. `PUT .../<name>?region=<region>`.
140
- * 🔴 Returns only `{id, name, version}` live (see `UpdateSecretResult`), not the full summary
141
- * the OpenAPI schema documents — call `get()` after if you need the rest. */
135
+ /** Update a secret's value (bumps version), owner-only. `PUT .../<name>`.
136
+ * Returns only `{id, name, version}` (see `UpdateSecretResult`); call `get()` after for the rest. */
142
137
  async update(name, input) {
143
138
  return request(config, {
144
139
  method: "PUT",
@@ -147,17 +142,16 @@ function makeVaultNamespace(config) {
147
142
  body: { value: encodeBase64Utf8(input.value), ...input.metadata !== void 0 ? { metadata: input.metadata } : {} }
148
143
  });
149
144
  },
150
- /** Rotate a secret's wrapping key (re-wraps under a fresh `kid`; content unchanged) — owner-only.
151
- * `POST .../<name>?op=rotate&region=<region>`. */
145
+ /** Rotate a secret's wrapping key (re-wraps under a fresh `kid`; content unchanged), owner-only.
146
+ * `POST .../<name>?op=rotate`. */
152
147
  async rotate(name, opts) {
153
148
  return request(config, {
154
149
  method: "POST",
155
150
  path: projectPath(requireProjectSlug(config), `vault/secrets/${encodeURIComponent(name)}`),
156
- query: { op: "rotate", region: opts.region }
151
+ query: { op: "rotate", region: opts?.region }
157
152
  });
158
153
  },
159
- /** Move (rename) a secret's hierarchical path — owner-only.
160
- * `POST .../<name>?op=move&region=<region>`. */
154
+ /** Move (rename) a secret's hierarchical path, owner-only. `POST .../<name>?op=move`. */
161
155
  async move(name, opts) {
162
156
  return request(config, {
163
157
  method: "POST",
@@ -166,12 +160,12 @@ function makeVaultNamespace(config) {
166
160
  body: { newPath: opts.newPath }
167
161
  });
168
162
  },
169
- /** Retire a secret — owner-only. `DELETE .../<name>?region=<region>` → `204 No Content`. */
163
+ /** Retire a secret, owner-only. `DELETE .../<name>`. */
170
164
  async delete(name, opts) {
171
165
  return request(config, {
172
166
  method: "DELETE",
173
167
  path: projectPath(requireProjectSlug(config), `vault/secrets/${encodeURIComponent(name)}`),
174
- query: { region: opts.region }
168
+ query: { region: opts?.region }
175
169
  });
176
170
  }
177
171
  }
@@ -186,24 +180,89 @@ function createKiboClient(config) {
186
180
  }
187
181
 
188
182
  // src/crypto.ts
183
+ import { KError as KError3 } from "@k9o/core/errors";
184
+
185
+ // src/wasm-loader.ts
189
186
  import { KError as KError2 } from "@k9o/core/errors";
187
+ var NOT_FOUND_CODES = /* @__PURE__ */ new Set(["ENOENT", "MODULE_NOT_FOUND", "ERR_MODULE_NOT_FOUND"]);
188
+ function describe(e) {
189
+ return e instanceof Error ? `${e.name}: ${e.message}` : String(e);
190
+ }
191
+ function isMissing(e, path) {
192
+ if (typeof e !== "object" || e === null) return false;
193
+ const err = e;
194
+ if (typeof err.code !== "string" || !NOT_FOUND_CODES.has(err.code)) return false;
195
+ if (typeof err.path === "string") return err.path === path;
196
+ return typeof err.message === "string" && err.message.includes(path);
197
+ }
198
+ function withCause(err, cause) {
199
+ err.cause = cause;
200
+ return err;
201
+ }
202
+ async function loadZkGlue(deps) {
203
+ for (const path of deps.candidates) {
204
+ let mod;
205
+ try {
206
+ mod = await deps.importGlue(path);
207
+ } catch (e) {
208
+ if (isMissing(e, path)) continue;
209
+ throw withCause(
210
+ new KError2({
211
+ code: "KIBO_WASM_ERROR",
212
+ message: `the Kibo zero-knowledge core (wasm) is present at ${path} but failed to load: ${describe(e)}`,
213
+ context: { path, cause: describe(e) },
214
+ recommendation: "Reinstall @k9o/kibo; if it persists, load the core yourself and pass it to setZkWasm."
215
+ }),
216
+ e
217
+ );
218
+ }
219
+ const m = mod;
220
+ if (typeof m?.zk_encrypt !== "function" || typeof m?.zk_decrypt !== "function") {
221
+ throw new KError2({
222
+ code: "KIBO_WASM_ERROR",
223
+ message: `the Kibo zero-knowledge core (wasm) loaded from ${path} does not export zk_encrypt and zk_decrypt`,
224
+ context: { path },
225
+ recommendation: "Reinstall @k9o/kibo; the bundled core does not match this version."
226
+ });
227
+ }
228
+ return m;
229
+ }
230
+ throw new KError2({
231
+ code: "KIBO_WASM_ERROR",
232
+ message: `the Kibo zero-knowledge core (wasm) is not bundled in this build of @k9o/kibo: no file at ${deps.candidates.join(" or ")}; encrypt and decrypt are unavailable`,
233
+ context: { candidates: deps.candidates },
234
+ recommendation: "Install a release of @k9o/kibo that ships the core, or load it yourself and pass it to setZkWasm."
235
+ });
236
+ }
237
+ function evalCommonJs(source, path, req, dirname) {
238
+ const module = { exports: {} };
239
+ const wrapper = new Function("exports", "require", "module", "__filename", "__dirname", source);
240
+ wrapper(module.exports, req, module, path, dirname);
241
+ return module.exports;
242
+ }
243
+ function nodeDeps(moduleUrl) {
244
+ const builtin = (n) => ["node", n].join(":");
245
+ const here = (rel) => decodeURIComponent(new URL(rel, moduleUrl).pathname.replace(/^\/([A-Za-z]:)/, "$1"));
246
+ return {
247
+ candidates: [here("../wasm/kibo_kms_rest_ffi.cjs"), here("../wasm/kibo_kms_rest_ffi.js")],
248
+ async importGlue(path) {
249
+ const fs = await import(builtin("fs"));
250
+ const nodeModule = await import(builtin("module"));
251
+ const nodePath = await import(builtin("path"));
252
+ const source = fs.readFileSync(path, "utf8");
253
+ return evalCommonJs(source, path, nodeModule.createRequire(path), nodePath.dirname(path));
254
+ }
255
+ };
256
+ }
257
+
258
+ // src/crypto.ts
190
259
  var cachedWasm;
191
260
  function setZkWasm(mod) {
192
261
  cachedWasm = mod;
193
262
  }
194
263
  async function loadWasm() {
195
264
  if (cachedWasm) return cachedWasm;
196
- const pkg = ["..", "wasm", "kibo_kms_rest_ffi.js"].join("/");
197
- try {
198
- cachedWasm = await import(pkg);
199
- } catch {
200
- throw new KError2({
201
- code: "KIBO_WASM_ERROR",
202
- message: "the Kibo zero-knowledge core (wasm) is not bundled in this build of @k9o/kibo; encrypt and decrypt are unavailable",
203
- context: {},
204
- recommendation: "Install a release of @k9o/kibo that ships the core, or load it yourself."
205
- });
206
- }
265
+ cachedWasm = await loadZkGlue(nodeDeps(import.meta.url));
207
266
  return cachedWasm;
208
267
  }
209
268
  async function encrypt(input) {
@@ -229,14 +288,14 @@ async function decrypt(input) {
229
288
  function toBytes(v) {
230
289
  if (v instanceof Uint8Array) return v;
231
290
  if (Array.isArray(v)) return Uint8Array.from(v);
232
- throw new KError2({
291
+ throw new KError3({
233
292
  code: "KIBO_WASM_DECODE_ERROR",
234
293
  message: "ZK core returned a non-byte value where bytes were expected",
235
294
  context: {}
236
295
  });
237
296
  }
238
297
  function mapZkError(e) {
239
- if (e instanceof KError2) return e;
298
+ if (e instanceof KError3) return e;
240
299
  const raw = e instanceof Error ? e.message : String(e);
241
300
  const head = /^([A-Z][A-Z0-9_]*):\s*([\s\S]*)$/.exec(raw);
242
301
  const code = head?.[1] ?? "KIBO_WASM_ERROR";
@@ -247,13 +306,12 @@ function mapZkError(e) {
247
306
  context = { dimension: pv[1].trim(), requested: pv[2].trim(), declared: pv[3].trim() };
248
307
  message = message.slice(0, pv.index).trim();
249
308
  }
250
- return new KError2({ code, message, context });
309
+ return new KError3({ code, message, context });
251
310
  }
252
311
  export {
253
312
  createKiboClient,
254
313
  decrypt,
255
314
  encrypt,
256
- makeVaultNamespace,
257
315
  setZkWasm
258
316
  };
259
317
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client-factory.ts","../src/vault.ts","../src/crypto.ts"],"sourcesContent":["/**\n * `createKiboClient(config)` — per-instance Kibo (ZK vault) SDK factory.\n *\n * Composes into `@k9o/core` as `client.kibo` (re-export, available when a\n * project is bound — the same composition pattern as `@k9o/gate`). Reuses the\n * core HTTP layer (`@k9o/core/http`) so the bearer/header/retry contract is\n * identical to the rest of the SDK pool.\n */\n\nimport { type HttpClientConfig } from \"@k9o/core/http\";\nimport { makeVaultNamespace } from \"./vault.ts\";\n\n/** Shape of the per-instance client returned by `createKiboClient`. */\nexport type KiboClient = {\n /** Vault surface — `client.kibo.vault.zkDeks.{store,fetch}` (F.5). */\n vault: ReturnType<typeof makeVaultNamespace>;\n};\n\n/** Construct a Kibo client bound to the supplied (core) HTTP config. */\nexport function createKiboClient(config: HttpClientConfig): KiboClient {\n return {\n vault: makeVaultNamespace(config),\n };\n}\n","/**\n * `client.kibo.vault.*` — the Kibo vault surface.\n *\n * `vault.zkDeks.{store,fetch}` — member-plane,\n * project-scoped storage of the opaque customer-wrapped DEK envelope. Zero\n * crypto SDK-side: this is a plain authenticated REST round-trip carrying an\n * opaque blob (the wrapping happens in the ZK core, piece C). The encryption\n * surface (`kibo.encrypt`/`decrypt`) is provided by the ZK core binding when it is\n * bundled.\n */\n\nimport { request, type HttpClientConfig } from \"@k9o/core/http\";\nimport { projectPath } from \"@k9o/core/url-grammar\";\nimport { KError } from \"@k9o/core/errors\";\nimport type { Envelope } from \"@k9o/core/envelope\";\nimport type {\n StoreZkDekInput, ZkDekHandle, ZkDekRecord,\n CreateSecretInput, SecretSummary, ListSecretsInput, ListSecretsResult,\n RevealChallenge, RevealSecretResult, UpdateSecretInput, UpdateSecretResult,\n RotateSecretResult, MoveSecretResult,\n} from \"./types.ts\";\n\n/** UTF-8-safe base64 encode, usable across browser/Node/Bun (unlike `Buffer`) — the vault-secrets wire\n * `value` field is base64 (live-verified; the OpenAPI \"plaintext to seal\" description does not mean\n * raw-on-the-wire). */\nfunction encodeBase64Utf8(plaintext: string): string {\n return btoa(unescape(encodeURIComponent(plaintext)));\n}\n\n/** Inverse of {@link encodeBase64Utf8} — decodes a revealed wire `value` back to plaintext. */\nfunction decodeBase64Utf8(b64: string): string {\n return decodeURIComponent(escape(atob(b64)));\n}\n\n/** Both `vault.zkDeks` and `vault.secrets` require a project context (member-plane, /). */\nfunction requireProjectSlug(config: HttpClientConfig): string {\n if (config.projectSlug === undefined || config.projectSlug.length === 0) {\n throw new KError({\n code: \"K9O_MISSING_PROJECT_CONTEXT\",\n message: \"client.kibo.vault requires a bound projectSlug (member-plane resource)\",\n context: {},\n recommendation: \"Pass projectSlug when constructing the client.\",\n });\n }\n return config.projectSlug;\n}\n\nexport function makeVaultNamespace(config: HttpClientConfig) {\n return {\n /** ZK-DEK opaque store/fetch (F.5). */\n zkDeks: {\n /**\n * Store an opaque customer-wrapped DEK envelope.\n * `POST /@<projectSlug>/vault/zk-deks` → the `zkdek_<base62>` handle.\n * The SDK ships the envelope verbatim; it is never parsed here.\n */\n async store(input: StoreZkDekInput): Promise<Envelope<ZkDekHandle>> {\n return request<ZkDekHandle>(config, {\n method: \"POST\",\n path: projectPath(requireProjectSlug(config), \"vault/zk-deks\"),\n body: input,\n });\n },\n\n /**\n * Fetch a stored `WrappedDek` by its handle.\n * `GET /@<projectSlug>/vault/zk-deks/<id>` → the opaque envelope + the\n * non-secret `provider`/`keyVersionRef` the caller needs to unwrap it.\n */\n async fetch(id: string): Promise<Envelope<ZkDekRecord>> {\n return request<ZkDekRecord>(config, {\n method: \"GET\",\n path: projectPath(requireProjectSlug(config), `vault/zk-deks/${encodeURIComponent(id)}`),\n });\n },\n },\n\n /**\n * typed secrets surface (`services/gateway/src/routes/vault-secrets.ts`) — the full family:\n * list/get/create/revealChallenge/reveal/update/rotate/move/delete. Every operation on a NAMED\n * secret (everything except `list`) requires `region` — live-verified against staging, not\n * documented in the route's OpenAPI schema. `list` is the one exception (verified it does NOT\n * need `region`).\n *\n * Response shapes for `update`/`rotate`/`move` are typed to what the gateway ACTUALLY returns\n * (live-verified), not the OpenAPI's documented (and inaccurate for these three) `SecretSummary` —\n * see the 🔴 notes on `UpdateSecretResult`/`RotateSecretResult`/`MoveSecretResult` in `types.ts`.\n */\n secrets: {\n /** List secrets (metadata only — never lists ciphertext). `GET /@<projectSlug>/vault/secrets`.\n * The one secrets operation that does NOT need `region` (live-verified). */\n async list(input?: ListSecretsInput): Promise<Envelope<ListSecretsResult>> {\n return request<ListSecretsResult>(config, {\n method: \"GET\",\n path: projectPath(requireProjectSlug(config), \"vault/secrets\"),\n query: { prefix: input?.prefix, cursor: input?.cursor, limit: input?.limit },\n });\n },\n\n /**\n * Read a secret's metadata (never its value — use `revealChallenge`+`reveal` for that).\n * `GET /@<projectSlug>/vault/secrets/<name>?region=<region>`.\n *\n * 🔴 KNOWN LIVE BUG (staging, 2026-09-05): this call currently fails with\n * `K9O_REVEAL_CHALLENGE_REQUIRED` on every attempt — the gateway/kibo does not honor the\n * documented \"no `X-K9O-Reveal-Challenge` header ⇒ return metadata\" behavior; it demands a\n * challenge unconditionally. Implemented per the documented contract (this is a backend defect,\n * not a client workaround); `list({prefix: exactName})` is the current working alternative for a\n * single secret's metadata until the gateway/kibo side is fixed.\n */\n async get(name: string, opts: { region: string }): Promise<Envelope<SecretSummary>> {\n return request<SecretSummary>(config, {\n method: \"GET\",\n path: projectPath(requireProjectSlug(config), `vault/secrets/${encodeURIComponent(name)}`),\n query: { region: opts.region },\n });\n },\n\n /**\n * Create a secret under the caller's own auth.\n * `POST /@<projectSlug>/vault/secrets/<name>?region=<region>` → the `sec_<base62>` summary.\n *\n * 🔴 `region` is REQUIRED on the query string, live-verified against staging — the vault-forward\n * proxy fail-closes with `K9O_INVALID_FIELD_VALUE` (\"this request requires an authoritative\n * region\") without it. Distinct from any `region` carried inside `input.value`'s envelope (e.g.\n * the BYOS aws_sigv4 STS-signing region): this one scopes the vault WRITE itself, mirroring the\n * gateway's `gatewayRegion()` residency guard (region-guard.ts) that closed a 2026-08 wrong-region\n * incident for the audit path — the request-shape unit tests never caught this because they don't\n * exercise a real gateway.\n */\n async create(input: CreateSecretInput): Promise<Envelope<SecretSummary>> {\n const { name, region, value, ...rest } = input;\n return request<SecretSummary>(config, {\n method: \"POST\",\n path: projectPath(requireProjectSlug(config), `vault/secrets/${encodeURIComponent(name)}`),\n query: { region },\n // Live-verified against staging: the wire `value` MUST be base64 — a raw plaintext string\n // (matching the route's own \"the plaintext to seal\" OpenAPI description literally) is\n // rejected with K9O_INVALID_FIELD_VALUE \"Request body could not be parsed.\" `input.value`\n // stays the caller-facing plaintext; base64 is purely a wire-transport detail handled here.\n body: { ...rest, value: encodeBase64Utf8(value) },\n });\n },\n\n /** Mint a single-use reveal challenge — present it as `X-K9O-Reveal-Challenge` on the\n * immediately-following `reveal()` call. `POST .../<name>?op=reveal-challenge&region=<region>`. */\n async revealChallenge(name: string, opts: { region: string }): Promise<Envelope<RevealChallenge>> {\n return request<RevealChallenge>(config, {\n method: \"POST\",\n path: projectPath(requireProjectSlug(config), `vault/secrets/${encodeURIComponent(name)}`),\n query: { op: \"reveal-challenge\", region: opts.region },\n });\n },\n\n /** Reveal a secret's plaintext value using a single-use challenge from `revealChallenge()`.\n * `GET .../<name>?region=<region>` with `X-K9O-Reveal-Challenge`. Decodes the wire's base64\n * `value` back to plaintext (live-verified: the reveal response's `value` is base64, symmetric\n * with `create`'s encode). */\n async reveal(name: string, opts: { region: string; challenge: string }): Promise<Envelope<RevealSecretResult>> {\n const envelope = await request<RevealSecretResult>(config, {\n method: \"GET\",\n path: projectPath(requireProjectSlug(config), `vault/secrets/${encodeURIComponent(name)}`),\n query: { region: opts.region },\n headers: { \"X-K9O-Reveal-Challenge\": opts.challenge },\n });\n return { ...envelope, data: { ...envelope.data, value: decodeBase64Utf8(envelope.data.value) } };\n },\n\n /** Update a secret's value (bumps version) — owner-only. `PUT .../<name>?region=<region>`.\n * 🔴 Returns only `{id, name, version}` live (see `UpdateSecretResult`), not the full summary\n * the OpenAPI schema documents — call `get()` after if you need the rest. */\n async update(name: string, input: UpdateSecretInput): Promise<Envelope<UpdateSecretResult>> {\n return request<UpdateSecretResult>(config, {\n method: \"PUT\",\n path: projectPath(requireProjectSlug(config), `vault/secrets/${encodeURIComponent(name)}`),\n query: { region: input.region },\n body: { value: encodeBase64Utf8(input.value), ...(input.metadata !== undefined ? { metadata: input.metadata } : {}) },\n });\n },\n\n /** Rotate a secret's wrapping key (re-wraps under a fresh `kid`; content unchanged) — owner-only.\n * `POST .../<name>?op=rotate&region=<region>`. */\n async rotate(name: string, opts: { region: string }): Promise<Envelope<RotateSecretResult>> {\n return request<RotateSecretResult>(config, {\n method: \"POST\",\n path: projectPath(requireProjectSlug(config), `vault/secrets/${encodeURIComponent(name)}`),\n query: { op: \"rotate\", region: opts.region },\n });\n },\n\n /** Move (rename) a secret's hierarchical path — owner-only.\n * `POST .../<name>?op=move&region=<region>`. */\n async move(name: string, opts: { region: string; newPath: string }): Promise<Envelope<MoveSecretResult>> {\n return request<MoveSecretResult>(config, {\n method: \"POST\",\n path: projectPath(requireProjectSlug(config), `vault/secrets/${encodeURIComponent(name)}`),\n query: { op: \"move\", region: opts.region },\n body: { newPath: opts.newPath },\n });\n },\n\n /** Retire a secret — owner-only. `DELETE .../<name>?region=<region>` → `204 No Content`. */\n async delete(name: string, opts: { region: string }): Promise<Envelope<void>> {\n return request<void>(config, {\n method: \"DELETE\",\n path: projectPath(requireProjectSlug(config), `vault/secrets/${encodeURIComponent(name)}`),\n query: { region: opts.region },\n });\n },\n },\n };\n}\n","/**\n * ZK encrypt/decrypt — the thin TS wrapper over the bound crypto core\n * (`kibo-kms-rest-ffi`, piece C) compiled to wasm. ZERO crypto here: every\n * wrap/unwrap, envelope framing, posture-floor enforcement, and KMS round-trip\n * lives in the audited Rust core. This layer only marshals the typed input\n * across the wasm boundary and maps the core's error string → `KError`.\n *\n * The core exposes `zk_encrypt`/`zk_decrypt` (async) taking/returning plain JS\n * objects via serde-wasm-bindgen, and rejecting with the `ZkFfiError` display\n * string (`\"<K9O_CODE>: <message>\"`, optionally with a posture-violation tail).\n *\n * Composes into `@k9o/core` as `client.kibo.encrypt`/`decrypt`; the F.5\n * `vault.zkDeks.{store,fetch}` surface (envelope persistence) is separate +\n * decoupled (the core never sees the store).\n */\n\nimport { KError } from \"@k9o/core/errors\";\nimport type {\n KiboEncryptInput,\n KiboEncryptOutput,\n KiboDecryptInput,\n KiboDecryptOutput,\n} from \"./crypto-types.ts\";\n\n/** The minimal surface the bound ZK-core wasm exposes. */\nexport interface ZkWasm {\n zk_encrypt(input: unknown): Promise<unknown>;\n zk_decrypt(input: unknown): Promise<unknown>;\n}\n\nlet cachedWasm: ZkWasm | undefined;\n\n/**\n * Inject a pre-loaded core wasm module. Hosts that bundle the wasm themselves\n * (browser/RN, or a test) call this; otherwise `encrypt`/`decrypt` lazy-load\n * the vendored per-provider artifact.\n */\nexport function setZkWasm(mod: ZkWasm): void {\n cachedWasm = mod;\n}\n\n/**\n * Lazily load the bound core wasm. ONE all-provider artifact is generated from\n * `kibo-kms-rest-ffi` (the core compiles every v2 KMS provider in; the\n * `provider` input field selects at call time), vendored under `wasm/` by the\n * publish pipeline and gitignored in-repo. The specifier is assembled at\n * runtime so the bundler/typechecker must not hard-resolve it — the contract\n * is the `ZkWasm` interface above.\n */\nasync function loadWasm(): Promise<ZkWasm> {\n if (cachedWasm) return cachedWasm;\n const pkg = [\"..\", \"wasm\", \"kibo_kms_rest_ffi.js\"].join(\"/\");\n try {\n cachedWasm = (await import(pkg)) as unknown as ZkWasm;\n } catch {\n // A package published without its native core (no bundle at publish time). Say so; never surface a bare\n // module-resolution error, and do not cache the failure.\n throw new KError({\n code: \"KIBO_WASM_ERROR\",\n message: \"the Kibo zero-knowledge core (wasm) is not bundled in this build of @k9o/kibo; encrypt and decrypt are unavailable\",\n context: {},\n recommendation: \"Install a release of @k9o/kibo that ships the core, or load it yourself.\",\n });\n }\n return cachedWasm;\n}\n\n/**\n * Encrypt `input.plaintext` via the customer KMS, client-side. Returns the\n * opaque ZKFE envelope — store it via `vault.zkDeks.store` (F.5) or keep it\n * yourself; it is meaningless to the platform.\n */\nexport async function encrypt(input: KiboEncryptInput): Promise<KiboEncryptOutput> {\n const wasm = await loadWasm();\n let out: unknown;\n try {\n out = await wasm.zk_encrypt(input);\n } catch (e) {\n throw mapZkError(e);\n }\n return { envelope: toBytes((out as { envelope?: unknown }).envelope) };\n}\n\n/**\n * Decrypt a ZKFE `input.envelope`. `strategy`, `attestation_tier`, and\n * `encryption_context` MUST match the values used at encrypt — they are AAD-bound\n * and never recovered from the envelope (anti-substitution discipline).\n */\nexport async function decrypt(input: KiboDecryptInput): Promise<KiboDecryptOutput> {\n const wasm = await loadWasm();\n let out: unknown;\n try {\n out = await wasm.zk_decrypt(input);\n } catch (e) {\n throw mapZkError(e);\n }\n return { plaintext: toBytes((out as { plaintext?: unknown }).plaintext) };\n}\n\n/** Normalise wasm byte output (serde-wasm-bindgen emits `Vec<u8>` as a number\n * array; some hosts return a `Uint8Array`) to `Uint8Array`. */\nfunction toBytes(v: unknown): Uint8Array {\n if (v instanceof Uint8Array) return v;\n if (Array.isArray(v)) return Uint8Array.from(v as number[]);\n throw new KError({\n code: \"KIBO_WASM_DECODE_ERROR\",\n message: \"ZK core returned a non-byte value where bytes were expected\",\n context: {},\n });\n}\n\n/**\n * Map the core's rejected error → `KError`. The wasm path flattens `ZkFfiError`\n * to its display string: `\"<CODE>: <message>\"` where `<CODE>` is a `K9O_*` wire\n * code (or `UPSTREAM_KMS_ERROR` for the customer's own KMS failures), optionally\n * followed by `\"(dimension: …, requested: …, declared: …)\"` for a posture-floor\n * violation. Codes are passed through (never hardcoded), per the KError discipline.\n */\nfunction mapZkError(e: unknown): KError {\n if (e instanceof KError) return e;\n const raw = e instanceof Error ? e.message : String(e);\n const head = /^([A-Z][A-Z0-9_]*):\\s*([\\s\\S]*)$/.exec(raw);\n const code = head?.[1] ?? \"KIBO_WASM_ERROR\";\n let message = head?.[2] ?? raw;\n\n let context: Record<string, unknown> = {};\n const pv = /\\(dimension:\\s*([^,]+),\\s*requested:\\s*([^,]+),\\s*declared:\\s*([^)]+)\\)\\s*$/.exec(message);\n if (pv) {\n context = { dimension: pv[1]!.trim(), requested: pv[2]!.trim(), declared: pv[3]!.trim() };\n message = message.slice(0, pv.index).trim();\n }\n return new KError({ code, message, context });\n}\n"],"mappings":"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","names":["KError"]}
1
+ {"version":3,"sources":["../src/client-factory.ts","../src/vault.ts","../src/crypto.ts","../src/wasm-loader.ts"],"sourcesContent":["/**\n * `createKiboClient(config)` — per-instance Kibo (ZK vault) SDK factory.\n *\n * Composes into `@k9o/core` as `client.kibo` (re-export, available when a\n * project is bound — the same composition pattern as `@k9o/gate`). Reuses the\n * core HTTP layer (`@k9o/core/http`) so the bearer/header/retry contract is\n * identical to the rest of the SDK pool.\n */\n\nimport { type HttpClientConfig } from \"@k9o/core/http\";\nimport { makeVaultNamespace } from \"./vault.ts\";\n\n/** Shape of the per-instance client returned by `createKiboClient`. */\nexport type KiboClient = {\n /** Vault surface: `vault.secrets.*` and `vault.zkDeks.{store,fetch}`. */\n vault: ReturnType<typeof makeVaultNamespace>;\n};\n\n/** Construct a Kibo client bound to the supplied (core) HTTP config. */\nexport function createKiboClient(config: HttpClientConfig): KiboClient {\n return {\n vault: makeVaultNamespace(config),\n };\n}\n","/**\n * `client.kibo.vault.*` - the Kibo vault surface.\n *\n * `vault.zkDeks.{store,fetch}` store and fetch the opaque customer-wrapped DEK envelope, project-scoped. There is\n * no cryptography in this client: it is a plain authenticated REST round-trip carrying an opaque blob (the\n * wrapping happens in the standalone `encrypt`/`decrypt`, which never call the platform).\n * `vault.secrets.*` is the typed secrets family.\n */\n\nimport { request, type HttpClientConfig } from \"@k9o/core/http\";\nimport { projectPath } from \"@k9o/core/url-grammar\";\nimport { KError } from \"@k9o/core/errors\";\nimport type { Envelope } from \"@k9o/core/envelope\";\nimport type {\n StoreZkDekInput, ZkDekHandle, ZkDekRecord,\n CreateSecretInput, SecretSummary, ListSecretsInput, ListSecretsResult,\n RevealChallenge, RevealSecretResult, UpdateSecretInput, UpdateSecretResult,\n RotateSecretResult, MoveSecretResult,\n} from \"./types.ts\";\n\n/** UTF-8-safe base64 encode, usable across browser/Node/Bun (unlike `Buffer`). The wire `value` field is base64. */\nfunction encodeBase64Utf8(plaintext: string): string {\n return btoa(unescape(encodeURIComponent(plaintext)));\n}\n\n/** Inverse of {@link encodeBase64Utf8} — decodes a revealed wire `value` back to plaintext. */\nfunction decodeBase64Utf8(b64: string): string {\n return decodeURIComponent(escape(atob(b64)));\n}\n\n/** Both `vault.zkDeks` and `vault.secrets` require a project context (member-plane). */\nfunction requireProjectSlug(config: HttpClientConfig): string {\n if (config.projectSlug === undefined || config.projectSlug.length === 0) {\n throw new KError({\n code: \"K9O_MISSING_PROJECT_CONTEXT\",\n message: \"client.kibo.vault requires a bound projectSlug (member-plane resource)\",\n context: {},\n recommendation: \"Pass projectSlug when constructing the client.\",\n });\n }\n return config.projectSlug;\n}\n\n/** Bound on the pages `secrets.get` scans for an exact name. */\nconst MAX_GET_PAGES = 20;\n\n/** @internal */\nexport function makeVaultNamespace(config: HttpClientConfig) {\n const listSecrets = async (input?: ListSecretsInput): Promise<Envelope<ListSecretsResult>> =>\n request<ListSecretsResult>(config, {\n method: \"GET\",\n path: projectPath(requireProjectSlug(config), \"vault/secrets\"),\n query: { prefix: input?.prefix, cursor: input?.cursor, limit: input?.limit },\n });\n\n return {\n /** Opaque store/fetch of a customer-wrapped DEK envelope. */\n zkDeks: {\n /**\n * Store an opaque customer-wrapped DEK envelope.\n * `POST /@<projectSlug>/vault/zk-deks` → the `zkdek_<base62>` handle.\n * The SDK ships the envelope verbatim; it is never parsed here.\n */\n async store(input: StoreZkDekInput): Promise<Envelope<ZkDekHandle>> {\n return request<ZkDekHandle>(config, {\n method: \"POST\",\n path: projectPath(requireProjectSlug(config), \"vault/zk-deks\"),\n body: input,\n });\n },\n\n /**\n * Fetch a stored `WrappedDek` by its handle.\n * `GET /@<projectSlug>/vault/zk-deks/<id>` → the opaque envelope + the\n * non-secret `provider`/`keyVersionRef` the caller needs to unwrap it.\n */\n async fetch(id: string): Promise<Envelope<ZkDekRecord>> {\n return request<ZkDekRecord>(config, {\n method: \"GET\",\n path: projectPath(requireProjectSlug(config), `vault/zk-deks/${encodeURIComponent(id)}`),\n });\n },\n },\n\n /**\n * Typed secrets surface (`/@<projectSlug>/vault/secrets`): list / get / create / revealChallenge / reveal /\n * update / rotate / move / delete.\n *\n * `region`: the gateway sets the residency region of every vault call itself and discards a caller-supplied\n * `region` query parameter, so it is optional on every method. When given, it is still sent (older gateways\n * required it).\n *\n * Response shapes for `update`/`rotate`/`move` are typed to what the gateway returns, not the OpenAPI's\n * `SecretSummary` (see `UpdateSecretResult`/`RotateSecretResult`/`MoveSecretResult` in `types.ts`).\n */\n secrets: {\n /** List secrets (metadata only, never ciphertext). `GET /@<projectSlug>/vault/secrets`. */\n list: listSecrets,\n\n /**\n * Read one secret's metadata (never its value; use `revealChallenge` + `reveal` for that).\n *\n * Vault answers a `GET /@<projectSlug>/vault/secrets/<name>` only as a reveal: with no\n * `X-K9O-Reveal-Challenge` header it refuses with 401 `K9O_REVEAL_CHALLENGE_REQUIRED`, whatever the caller's\n * rights. There is no plain metadata read on that route, so `get` reads the metadata the route that exists\n * for it returns: it lists by the exact name as prefix and picks the exact match. It throws `K9O_NOT_FOUND`\n * (status 404) when no secret has that name.\n */\n async get(name: string, _opts?: { region?: string }): Promise<Envelope<SecretSummary>> {\n let cursor: string | undefined;\n for (let page = 0; page < MAX_GET_PAGES; page++) {\n const env = await listSecrets({ prefix: name, ...(cursor !== undefined ? { cursor } : {}), limit: 100 });\n const hit = env.data.secrets.find((s) => s.name === name);\n if (hit !== undefined) return { ...env, data: hit };\n if (env.data.nextCursor === null || env.data.nextCursor === undefined) break;\n cursor = env.data.nextCursor;\n }\n throw new KError({\n code: \"K9O_NOT_FOUND\",\n message: `No secret named '${name}' in this project.`,\n context: { name },\n status: 404,\n });\n },\n\n /**\n * Create a secret under the caller's own auth.\n * `POST /@<projectSlug>/vault/secrets/<name>` -> the `sec_<base62>` summary.\n */\n async create(input: CreateSecretInput): Promise<Envelope<SecretSummary>> {\n const { name, region, value, ...rest } = input;\n return request<SecretSummary>(config, {\n method: \"POST\",\n path: projectPath(requireProjectSlug(config), `vault/secrets/${encodeURIComponent(name)}`),\n query: { region },\n // The wire `value` is base64 (a raw string is rejected as unparseable). `input.value` stays the\n // caller-facing plaintext; base64 is purely a wire detail handled here.\n body: { ...rest, value: encodeBase64Utf8(value) },\n });\n },\n\n /** Mint a single-use reveal challenge; pass it to the immediately-following `reveal()`.\n * `POST .../<name>?op=reveal-challenge`. The optional `reason` is accepted by the gateway and not forwarded. */\n async revealChallenge(name: string, opts?: { region?: string; reason?: string }): Promise<Envelope<RevealChallenge>> {\n return request<RevealChallenge>(config, {\n method: \"POST\",\n path: projectPath(requireProjectSlug(config), `vault/secrets/${encodeURIComponent(name)}`),\n query: { op: \"reveal-challenge\", region: opts?.region },\n ...(opts?.reason !== undefined ? { body: { reason: opts.reason } } : {}),\n });\n },\n\n /** Reveal a secret's plaintext value using a single-use challenge from `revealChallenge()`.\n * `GET .../<name>` with `X-K9O-Reveal-Challenge`. Decodes the wire's base64 `value` back to plaintext\n * (symmetric with `create`'s encode). */\n async reveal(name: string, opts: { region?: string; challenge: string }): Promise<Envelope<RevealSecretResult>> {\n const envelope = await request<RevealSecretResult>(config, {\n method: \"GET\",\n path: projectPath(requireProjectSlug(config), `vault/secrets/${encodeURIComponent(name)}`),\n query: { region: opts.region },\n headers: { \"X-K9O-Reveal-Challenge\": opts.challenge },\n });\n return { ...envelope, data: { ...envelope.data, value: decodeBase64Utf8(envelope.data.value) } };\n },\n\n /** Update a secret's value (bumps version), owner-only. `PUT .../<name>`.\n * Returns only `{id, name, version}` (see `UpdateSecretResult`); call `get()` after for the rest. */\n async update(name: string, input: UpdateSecretInput): Promise<Envelope<UpdateSecretResult>> {\n return request<UpdateSecretResult>(config, {\n method: \"PUT\",\n path: projectPath(requireProjectSlug(config), `vault/secrets/${encodeURIComponent(name)}`),\n query: { region: input.region },\n body: { value: encodeBase64Utf8(input.value), ...(input.metadata !== undefined ? { metadata: input.metadata } : {}) },\n });\n },\n\n /** Rotate a secret's wrapping key (re-wraps under a fresh `kid`; content unchanged), owner-only.\n * `POST .../<name>?op=rotate`. */\n async rotate(name: string, opts?: { region?: string }): Promise<Envelope<RotateSecretResult>> {\n return request<RotateSecretResult>(config, {\n method: \"POST\",\n path: projectPath(requireProjectSlug(config), `vault/secrets/${encodeURIComponent(name)}`),\n query: { op: \"rotate\", region: opts?.region },\n });\n },\n\n /** Move (rename) a secret's hierarchical path, owner-only. `POST .../<name>?op=move`. */\n async move(name: string, opts: { region?: string; newPath: string }): Promise<Envelope<MoveSecretResult>> {\n return request<MoveSecretResult>(config, {\n method: \"POST\",\n path: projectPath(requireProjectSlug(config), `vault/secrets/${encodeURIComponent(name)}`),\n query: { op: \"move\", region: opts.region },\n body: { newPath: opts.newPath },\n });\n },\n\n /** Retire a secret, owner-only. `DELETE .../<name>`. */\n async delete(name: string, opts?: { region?: string }): Promise<Envelope<void>> {\n return request<void>(config, {\n method: \"DELETE\",\n path: projectPath(requireProjectSlug(config), `vault/secrets/${encodeURIComponent(name)}`),\n query: { region: opts?.region },\n });\n },\n },\n };\n}\n","/**\n * ZK encrypt/decrypt — the thin TS wrapper over the bound crypto core\n * (`kibo-kms-rest-ffi`) compiled to wasm. ZERO crypto here: every\n * wrap/unwrap, envelope framing, posture-floor enforcement, and KMS round-trip\n * lives in the audited Rust core. This layer only marshals the typed input\n * across the wasm boundary and maps the core's error string → `KError`.\n *\n * The core exposes `zk_encrypt`/`zk_decrypt` (async) taking/returning plain JS\n * objects via serde-wasm-bindgen, and rejecting with the `ZkFfiError` display\n * string (`\"<K9O_CODE>: <message>\"`, optionally with a posture-violation tail).\n *\n * These are standalone exports (`import { encrypt, decrypt } from \"@k9o/kibo\"`), not methods of the client: they run\n * locally against the customer's own KMS and make no platform call. The `vault.zkDeks.{store,fetch}` surface\n * (envelope persistence) is separate and decoupled (the core never sees the store).\n */\n\nimport { KError } from \"@k9o/core/errors\";\nimport { loadZkGlue, nodeDeps } from \"./wasm-loader.ts\";\nimport type {\n KiboEncryptInput,\n KiboEncryptOutput,\n KiboDecryptInput,\n KiboDecryptOutput,\n} from \"./crypto-types.ts\";\n\n/** The minimal surface the bound ZK-core wasm exposes. */\nexport interface ZkWasm {\n zk_encrypt(input: unknown): Promise<unknown>;\n zk_decrypt(input: unknown): Promise<unknown>;\n}\n\nlet cachedWasm: ZkWasm | undefined;\n\n/**\n * Inject a pre-loaded core wasm module. Hosts that bundle the wasm themselves\n * (browser/RN, or a test) call this; otherwise `encrypt`/`decrypt` lazy-load\n * the vendored per-provider artifact.\n */\nexport function setZkWasm(mod: ZkWasm): void {\n cachedWasm = mod;\n}\n\n/**\n * Lazily load the bound core wasm. ONE all-provider artifact is generated from `kibo-kms-rest-ffi` (the core compiles\n * every KMS provider in; the `provider` input field selects at call time) and vendored under `wasm/` by the publish\n * pipeline. See `wasm-loader.ts` for how the CommonJS glue is loaded from this ESM package and for the failure modes.\n */\nasync function loadWasm(): Promise<ZkWasm> {\n if (cachedWasm) return cachedWasm;\n // A failure is not cached: the next call tries again.\n cachedWasm = await loadZkGlue(nodeDeps(import.meta.url));\n return cachedWasm;\n}\n\n/**\n * Encrypt `input.plaintext` via the customer KMS, client-side. Returns the\n * opaque ZKFE envelope — store it via `vault.zkDeks.store` or keep it\n * yourself; it is meaningless to the platform.\n */\nexport async function encrypt(input: KiboEncryptInput): Promise<KiboEncryptOutput> {\n const wasm = await loadWasm();\n let out: unknown;\n try {\n out = await wasm.zk_encrypt(input);\n } catch (e) {\n throw mapZkError(e);\n }\n return { envelope: toBytes((out as { envelope?: unknown }).envelope) };\n}\n\n/**\n * Decrypt a ZKFE `input.envelope`. `strategy`, `attestation_tier`, and\n * `encryption_context` MUST match the values used at encrypt — they are AAD-bound\n * and never recovered from the envelope (anti-substitution discipline).\n */\nexport async function decrypt(input: KiboDecryptInput): Promise<KiboDecryptOutput> {\n const wasm = await loadWasm();\n let out: unknown;\n try {\n out = await wasm.zk_decrypt(input);\n } catch (e) {\n throw mapZkError(e);\n }\n return { plaintext: toBytes((out as { plaintext?: unknown }).plaintext) };\n}\n\n/** Normalise wasm byte output (serde-wasm-bindgen emits `Vec<u8>` as a number\n * array; some hosts return a `Uint8Array`) to `Uint8Array`. */\nfunction toBytes(v: unknown): Uint8Array {\n if (v instanceof Uint8Array) return v;\n if (Array.isArray(v)) return Uint8Array.from(v as number[]);\n throw new KError({\n code: \"KIBO_WASM_DECODE_ERROR\",\n message: \"ZK core returned a non-byte value where bytes were expected\",\n context: {},\n });\n}\n\n/**\n * Map the core's rejected error → `KError`. The wasm path flattens `ZkFfiError`\n * to its display string: `\"<CODE>: <message>\"` where `<CODE>` is a `K9O_*` wire\n * code (or `UPSTREAM_KMS_ERROR` for the customer's own KMS failures), optionally\n * followed by `\"(dimension: …, requested: …, declared: …)\"` for a posture-floor\n * violation. Codes are passed through (never hardcoded), per the KError discipline.\n */\nfunction mapZkError(e: unknown): KError {\n if (e instanceof KError) return e;\n const raw = e instanceof Error ? e.message : String(e);\n const head = /^([A-Z][A-Z0-9_]*):\\s*([\\s\\S]*)$/.exec(raw);\n const code = head?.[1] ?? \"KIBO_WASM_ERROR\";\n let message = head?.[2] ?? raw;\n\n let context: Record<string, unknown> = {};\n const pv = /\\(dimension:\\s*([^,]+),\\s*requested:\\s*([^,]+),\\s*declared:\\s*([^)]+)\\)\\s*$/.exec(message);\n if (pv) {\n context = { dimension: pv[1]!.trim(), requested: pv[2]!.trim(), declared: pv[3]!.trim() };\n message = message.slice(0, pv.index).trim();\n }\n return new KError({ code, message, context });\n}\n","/**\n * Loads the nodejs-target glue that wasm-bindgen emits for the bound core.\n *\n * That glue is CommonJS (`exports.zk_encrypt = ...`, `require(\"fs\")`, a synchronous read of the sibling `_bg.wasm`).\n * This package is ESM (`\"type\": \"module\"`), so Node parses any `.js` under it as ESM and the glue dies with\n * `exports is not defined`; `import()` and `require()` of that file both hit it. The default loader therefore reads the\n * glue source and evaluates it with a CommonJS wrapper itself, which does not depend on the file extension or on the\n * nearest `package.json`.\n *\n * Failures are reported as what they are: no glue file at all, or a glue file that is there and failed (with the\n * underlying error attached as `cause`).\n */\n\nimport { KError } from \"@k9o/core/errors\";\nimport type { ZkWasm } from \"./crypto.ts\";\n\n/** What the loader needs from the host; injectable so the failure paths are testable without a wasm artefact. */\nexport interface WasmLoaderDeps {\n /** Absolute paths to try, in order. */\n candidates: string[];\n /** Load the glue at `path`. Must reject with an error whose `code` is `ENOENT`/`MODULE_NOT_FOUND`/`ERR_MODULE_NOT_FOUND`\n * and whose `path` (or message) names `path` when that file is missing. */\n importGlue(path: string): Promise<unknown>;\n}\n\nconst NOT_FOUND_CODES = new Set([\"ENOENT\", \"MODULE_NOT_FOUND\", \"ERR_MODULE_NOT_FOUND\"]);\n\nfunction describe(e: unknown): string {\n return e instanceof Error ? `${e.name}: ${e.message}` : String(e);\n}\n\n/** True when `e` says the file `path` itself is missing (not a file the glue went on to read). */\nfunction isMissing(e: unknown, path: string): boolean {\n if (typeof e !== \"object\" || e === null) return false;\n const err = e as { code?: unknown; path?: unknown; message?: unknown };\n if (typeof err.code !== \"string\" || !NOT_FOUND_CODES.has(err.code)) return false;\n if (typeof err.path === \"string\") return err.path === path;\n return typeof err.message === \"string\" && err.message.includes(path);\n}\n\nfunction withCause(err: KError, cause: unknown): KError {\n (err as { cause?: unknown }).cause = cause;\n return err;\n}\n\nexport async function loadZkGlue(deps: WasmLoaderDeps): Promise<ZkWasm> {\n for (const path of deps.candidates) {\n let mod: unknown;\n try {\n mod = await deps.importGlue(path);\n } catch (e) {\n if (isMissing(e, path)) continue;\n throw withCause(\n new KError({\n code: \"KIBO_WASM_ERROR\",\n message: `the Kibo zero-knowledge core (wasm) is present at ${path} but failed to load: ${describe(e)}`,\n context: { path, cause: describe(e) },\n recommendation: \"Reinstall @k9o/kibo; if it persists, load the core yourself and pass it to setZkWasm.\",\n }),\n e,\n );\n }\n const m = mod as Partial<ZkWasm> | null | undefined;\n if (typeof m?.zk_encrypt !== \"function\" || typeof m?.zk_decrypt !== \"function\") {\n throw new KError({\n code: \"KIBO_WASM_ERROR\",\n message: `the Kibo zero-knowledge core (wasm) loaded from ${path} does not export zk_encrypt and zk_decrypt`,\n context: { path },\n recommendation: \"Reinstall @k9o/kibo; the bundled core does not match this version.\",\n });\n }\n return m as ZkWasm;\n }\n throw new KError({\n code: \"KIBO_WASM_ERROR\",\n message: `the Kibo zero-knowledge core (wasm) is not bundled in this build of @k9o/kibo: no file at ${deps.candidates.join(\" or \")}; encrypt and decrypt are unavailable`,\n context: { candidates: deps.candidates },\n recommendation: \"Install a release of @k9o/kibo that ships the core, or load it yourself and pass it to setZkWasm.\",\n });\n}\n\n/** Evaluate CommonJS source as a module living at `path`, with a `require` resolving from `path`. */\nexport function evalCommonJs(source: string, path: string, req: (id: string) => unknown, dirname: string): unknown {\n const module = { exports: {} as Record<string, unknown> };\n // eslint-disable-next-line no-new-func\n const wrapper = new Function(\"exports\", \"require\", \"module\", \"__filename\", \"__dirname\", source);\n wrapper(module.exports, req, module, path, dirname);\n return module.exports;\n}\n\n/** Node host: glue next to this package's `wasm/` directory, evaluated as CommonJS. */\nexport function nodeDeps(moduleUrl: string): WasmLoaderDeps {\n // The node builtins are named at run time so a browser/RN bundler never tries to resolve them: this path only runs on Node.\n const builtin = (n: string) => [\"node\", n].join(\":\");\n const here = (rel: string) => decodeURIComponent(new URL(rel, moduleUrl).pathname.replace(/^\\/([A-Za-z]:)/, \"$1\"));\n return {\n candidates: [here(\"../wasm/kibo_kms_rest_ffi.cjs\"), here(\"../wasm/kibo_kms_rest_ffi.js\")],\n async importGlue(path: string) {\n const fs = (await import(builtin(\"fs\"))) as typeof import(\"node:fs\");\n const nodeModule = (await import(builtin(\"module\"))) as typeof import(\"node:module\");\n const nodePath = (await import(builtin(\"path\"))) as typeof import(\"node:path\");\n const source = fs.readFileSync(path, \"utf8\"); // throws ENOENT with err.path === path when the glue is missing\n return evalCommonJs(source, path, nodeModule.createRequire(path), nodePath.dirname(path));\n },\n };\n}\n"],"mappings":"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","names":["KError","KError","KError"]}
package/dist/types.d.ts CHANGED
@@ -1,11 +1,9 @@
1
1
  /**
2
2
  * `@k9o/kibo` — Zero-Knowledge BYOK/HYOK vault SDK types.
3
3
  *
4
- * Surface: the ZK-DEK store/fetch. The wrapped
5
- * envelope is produced client-side by the audited ZK core (the
6
- * `kibo-kms-rest` binding); this SDK ships it **verbatim and opaque** and never
7
- * reads its interior — the strict-ZK invariant + envelope framing live in the
8
- * core, never here.
4
+ * Surface: the ZK-DEK store/fetch and the secrets family. The wrapped
5
+ * envelope is produced client-side by the ZK core (compiled to wasm); this SDK ships it
6
+ * **verbatim and opaque** and never reads its interior.
9
7
  */
10
8
  /** Customer KMS/HSM provider — non-secret routing metadata (mirrors the ZK FFI provider selector). */
11
9
  type KmsProvider = "aws-kms" | "gcp-kms" | "azure-kv" | "hashicorp-vault";
@@ -37,7 +35,7 @@ type ZkDekRecord = {
37
35
  keyVersionRef: string;
38
36
  createdAt: string;
39
37
  };
40
- /** secret visibility — write ops (create/update) stay owner-only regardless of this tag. */
38
+ /** Secret visibility. Write operations (create/update) stay owner-only regardless of this tag. */
41
39
  type SecretVisibility = {
42
40
  kind: "private";
43
41
  } | {
@@ -45,19 +43,14 @@ type SecretVisibility = {
45
43
  principals?: string[];
46
44
  roles?: string[];
47
45
  };
48
- /** Input to create a vault secret — `POST /@<projectSlug>/vault/secrets/<name>?region=<region>`. */
46
+ /** Input to create a vault secret: `POST /@<projectSlug>/vault/secrets/<name>`. */
49
47
  type CreateSecretInput = {
50
48
  /** Hierarchical path the secret is addressed under, e.g. `byos/s3-prod` (also the `:name` URL segment). */
51
49
  name: string;
52
- /** The plaintext to seal, as a regular (non-base64) string — `vault.secrets.create` base64-encodes
53
- * it for the wire itself (live-verified against staging: the route's OpenAPI description says
54
- * "the plaintext to seal" but a raw non-base64 `value` is rejected as unparseable). */
50
+ /** The plaintext to seal, as a regular (non-base64) string; `vault.secrets.create` base64-encodes it for the wire. */
55
51
  value: string;
56
- /** The authoritative region for this vault write (sent as `?region=`), REQUIRED — the vault-forward
57
- * proxy fail-closes without it (live-verified against staging, not documented in the route's
58
- * OpenAPI schema as of this writing). Distinct from any region carried inside `value`'s own
59
- * envelope. */
60
- region: string;
52
+ /** Optional. The gateway sets the residency region of the write itself and discards this; it is sent only when given. */
53
+ region?: string;
61
54
  visibility?: SecretVisibility;
62
55
  metadata?: Record<string, string>;
63
56
  };
@@ -79,8 +72,7 @@ type SecretSummary = {
79
72
  retiredAt: string | null;
80
73
  metadata: Record<string, string>;
81
74
  };
82
- /** `client.kibo.vault.secrets.list()` input — none of these need `region` (live-verified: unlike every
83
- * other secrets operation, list is NOT region-gated). */
75
+ /** `client.kibo.vault.secrets.list()` input. */
84
76
  type ListSecretsInput = {
85
77
  /** Filter to a path prefix. */
86
78
  prefix?: string;
@@ -91,7 +83,7 @@ type ListSecretsResult = {
91
83
  secrets: readonly SecretSummary[];
92
84
  nextCursor: string | null;
93
85
  };
94
- /** releasing-node observed attestation tier (K4) — present on every reveal. */
86
+ /** The releasing node's observed attestation tier; present on every reveal. */
95
87
  type AttestationTier = {
96
88
  /** `attestation:destination:region` trust-tier URN. */
97
89
  urn: string;
@@ -117,18 +109,15 @@ type RevealSecretResult = {
117
109
  };
118
110
  /** `update()` input. */
119
111
  type UpdateSecretInput = {
120
- /** The authoritative region for this write (sent as `?region=`), REQUIRED — same live-verified gate
121
- * as `create`. */
122
- region: string;
112
+ /** Optional. The gateway sets the residency region of the write itself and discards this; it is sent only when given. */
113
+ region?: string;
123
114
  /** New plaintext — bumps `version`. The SDK base64-encodes it for the wire. */
124
115
  value: string;
125
116
  metadata?: Record<string, string>;
126
117
  };
127
118
  /**
128
- * `update()` result — 🔴 live-verified NARROWER than the documented `SecretSummary`: the gateway
129
- * returns only `{id, name, version}` on a real PUT, not the full summary the OpenAPI schema claims.
130
- * Typed to what's actually observed rather than the aspirational doc shape; call `get()` afterward if
131
- * the full summary is needed.
119
+ * `update()` result: the gateway returns only `{id, name, version}`, narrower than the OpenAPI `SecretSummary`.
120
+ * Call `get()` afterward if the full summary is needed.
132
121
  */
133
122
  type UpdateSecretResult = {
134
123
  id: string;
@@ -136,9 +125,8 @@ type UpdateSecretResult = {
136
125
  version: number;
137
126
  };
138
127
  /**
139
- * `rotate()` result — 🔴 live-verified shape, not documented anywhere in the OpenAPI schema (the route
140
- * only declares `t.Union([SecretSummary, RevealChallengeResponse])`, which this matches neither).
141
- * `ledgerSequenceNumber` is undocumented; kept as-observed.
128
+ * `rotate()` result: not described by the OpenAPI schema (which declares a summary or a reveal challenge for the
129
+ * route); typed to what the gateway returns. `ledgerSequenceNumber` is undocumented.
142
130
  */
143
131
  type RotateSecretResult = {
144
132
  id: string;
@@ -148,9 +136,8 @@ type RotateSecretResult = {
148
136
  ledgerSequenceNumber: number;
149
137
  };
150
138
  /**
151
- * `move()` result — 🔴 live-verified shape, also not the documented `SecretSummary`: no `version`, and
152
- * an undocumented `event` field (`"SECRET_PATH_CHANGED"` observed; typed as `string` since only one
153
- * sample was seen and a closed enum can't be asserted from one observation).
139
+ * `move()` result: not the OpenAPI `SecretSummary`; it has no `version` and carries an undocumented `event`
140
+ * field (`"SECRET_PATH_CHANGED"` observed; typed as `string`).
154
141
  */
155
142
  type MoveSecretResult = {
156
143
  id: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@k9o/kibo",
3
- "version": "1.2.0",
4
- "description": "Kibo — Zero-Knowledge BYOK/HYOK vault SDK (client-side; composes into @k9o/core as client.kibo). ZK-DEK store/fetch (F.5) + encrypt/decrypt (thin wrappers over the audited bound core compiled to wasm).",
3
+ "version": "1.3.0",
4
+ "description": "Kibo - Zero-Knowledge BYOK/HYOK vault SDK. The vault client (secrets, wrapped data keys; composes into @k9o/core as client.kibo) plus standalone encrypt/decrypt against your own KMS, in WASM.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -37,7 +37,7 @@
37
37
  "directory": "packages/kibo"
38
38
  },
39
39
  "dependencies": {
40
- "@k9o/core": "1.2.0"
40
+ "@k9o/core": "1.3.0"
41
41
  },
42
42
  "devDependencies": {
43
43
  "bun-types": "^1.1.0",
@@ -12,7 +12,7 @@ import { makeVaultNamespace } from "./vault.ts";
12
12
 
13
13
  /** Shape of the per-instance client returned by `createKiboClient`. */
14
14
  export type KiboClient = {
15
- /** Vault surface — `client.kibo.vault.zkDeks.{store,fetch}` (F.5). */
15
+ /** Vault surface: `vault.secrets.*` and `vault.zkDeks.{store,fetch}`. */
16
16
  vault: ReturnType<typeof makeVaultNamespace>;
17
17
  };
18
18
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * TS contract for the ZK encrypt/decrypt FFI surface — mirrors the Rust
3
- * `kibo-kms-rest-ffi::types` (the bound core, piece C) field-for-field.
3
+ * `kibo-kms-rest-ffi::types` (the bound core) field-for-field.
4
4
  *
5
5
  * These types cross the wasm boundary as plain JS objects (serde-wasm-bindgen),
6
6
  * so the field names + enum wire-tokens must match the Rust serde exactly:
@@ -13,7 +13,7 @@
13
13
  * after use and never logged or returned. The platform never sees plaintext.
14
14
  */
15
15
 
16
- // The customer KMS provider — the same routing selector as the F.5 store
16
+ // The customer KMS provider — the same routing selector as the ZK-DEK store
17
17
  // surface. Reuse the canonical definition (no duplication).
18
18
  import type { KmsProvider } from "./types.ts";
19
19
  export type { KmsProvider };
@@ -41,7 +41,7 @@ export interface KiboAllowedOracleModes {
41
41
  direct: boolean;
42
42
  }
43
43
 
44
- /** Project-declared crypto-posture floor. Omit fields for the
44
+ /** Project-declared crypto-posture floor . Omit fields for the
45
45
  * most-permissive default (`platform_managed` floor + both oracle modes). */
46
46
  export interface KiboPosturePolicy {
47
47
  min_custody_posture?: KiboMinPosture;