@kitsy/cnos 1.1.0 → 1.1.1

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/README.md CHANGED
@@ -2,4 +2,18 @@
2
2
 
3
3
  Developer-friendly CNOS runtime assembly. It bundles the core engine plus the official built-in plugins, exposes the main `createCnos(...)` entry point for app code, and re-exports the built-ins under `@kitsy/cnos/plugins/*`.
4
4
 
5
+ Current runtime surface includes:
6
+ - `createCnos()`
7
+ - `read`, `require`, `readOr`
8
+ - `value`, `secret`, `meta`
9
+ - `inspect`
10
+ - `toObject`, `toNamespace`
11
+ - `toEnv`, `toPublicEnv`
12
+
13
+ CLI-oriented storage/export rules to be aware of:
14
+ - user-defined values and secrets remain private by default
15
+ - public/browser exposure comes from `public.promote`
16
+ - shell env export comes from explicit `envMapping.explicit`
17
+ - local secret material lives outside the repo in encrypted vault storage under `~/.cnos/secrets`
18
+
5
19
  Use `@kitsy/cnos-vite` for Vite projects and `@kitsy/cnos-next` for Next.js projects when you want CNOS public values projected into framework-native env surfaces.
@@ -2,7 +2,7 @@ import {
2
2
  envVarToLogicalKey,
3
3
  resolveWorkspaceScopedPath,
4
4
  toPortablePath
5
- } from "./chunk-K2T4R5WH.js";
5
+ } from "./chunk-JPJ3S3CO.js";
6
6
 
7
7
  // ../../plugins/dotenv/src/index.ts
8
8
  import { readFile } from "fs/promises";
@@ -361,23 +361,30 @@ function flattenObject(value, prefix = "") {
361
361
 
362
362
  // ../core/src/utils/secretStore.ts
363
363
  import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "crypto";
364
- import { mkdir as mkdir2, readFile, writeFile as writeFile2 } from "fs/promises";
364
+ import { mkdir as mkdir2, readdir, readFile, writeFile as writeFile2 } from "fs/promises";
365
365
  import path3 from "path";
366
366
  function isObject(value) {
367
367
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
368
368
  }
369
369
  function isSecretReference(value) {
370
- return isObject(value) && typeof value.provider === "string" && value.provider.trim().length > 0 && typeof value.ref === "string" && value.ref.trim().length > 0 && Object.keys(value).every((key) => ["provider", "ref"].includes(key));
370
+ return isObject(value) && typeof value.provider === "string" && value.provider.trim().length > 0 && typeof value.ref === "string" && value.ref.trim().length > 0 && (value.vault === void 0 && true || typeof value.vault === "string" && value.vault.trim().length > 0) && Object.keys(value).every((key) => ["provider", "ref", "vault"].includes(key));
371
371
  }
372
372
  function resolveSecretStoreRoot(processEnv = process.env) {
373
373
  return path3.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets"));
374
374
  }
375
- function resolveSecretStoreFile(storeRoot, ref) {
376
- return path3.join(storeRoot, "store", ...ref.split("/")).concat(".json");
375
+ function resolveSecretVaultFile(storeRoot, vault = "default") {
376
+ return path3.join(storeRoot, "vaults", `${vault}.json`);
377
+ }
378
+ function resolveSecretStoreFile(storeRoot, ref, vault = "default") {
379
+ return path3.join(storeRoot, "vaults", vault, "store", ...ref.split("/")).concat(".json");
377
380
  }
378
381
  function deriveKey(passphrase, salt) {
379
382
  return scryptSync(passphrase, salt, 32);
380
383
  }
384
+ function resolveSecretPassphrase(vault = "default", processEnv = process.env) {
385
+ const vaultToken = vault.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
386
+ return processEnv[`CNOS_SECRET_PASSPHRASE_${vaultToken}`] ?? processEnv.CNOS_SECRET_PASSPHRASE;
387
+ }
381
388
  function encryptDocument(value, passphrase) {
382
389
  const salt = randomBytes(16);
383
390
  const iv = randomBytes(12);
@@ -405,19 +412,55 @@ function decryptDocument(document, passphrase) {
405
412
  const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
406
413
  return plaintext.toString("utf8");
407
414
  }
408
- async function writeLocalSecret(storeRoot, ref, value, passphrase) {
409
- const filePath = resolveSecretStoreFile(storeRoot, ref);
415
+ async function createSecretVault(storeRoot, vault, passphrase) {
416
+ const normalizedVault = vault.trim() || "default";
417
+ const filePath = resolveSecretVaultFile(storeRoot, normalizedVault);
418
+ await mkdir2(path3.dirname(filePath), { recursive: true });
419
+ const document = {
420
+ version: 1,
421
+ name: normalizedVault,
422
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
423
+ verifier: encryptDocument(`cnos-vault:${normalizedVault}`, passphrase)
424
+ };
425
+ await writeFile2(filePath, JSON.stringify(document, null, 2), "utf8");
426
+ return filePath;
427
+ }
428
+ async function ensureSecretVault(storeRoot, vault, passphrase) {
429
+ const normalizedVault = vault.trim() || "default";
430
+ const filePath = resolveSecretVaultFile(storeRoot, normalizedVault);
431
+ try {
432
+ await readFile(filePath, "utf8");
433
+ return filePath;
434
+ } catch (error) {
435
+ if (error.code !== "ENOENT") {
436
+ throw error;
437
+ }
438
+ }
439
+ return createSecretVault(storeRoot, normalizedVault, passphrase);
440
+ }
441
+ async function listSecretVaults(storeRoot) {
442
+ const vaultRoot = path3.join(storeRoot, "vaults");
443
+ try {
444
+ const entries = await readdir(vaultRoot, { withFileTypes: true });
445
+ return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.replace(/\.json$/, "")).sort((left, right) => left.localeCompare(right));
446
+ } catch {
447
+ return [];
448
+ }
449
+ }
450
+ async function writeLocalSecret(storeRoot, ref, value, passphrase, vault = "default") {
451
+ await ensureSecretVault(storeRoot, vault, passphrase);
452
+ const filePath = resolveSecretStoreFile(storeRoot, ref, vault);
410
453
  await mkdir2(path3.dirname(filePath), { recursive: true });
411
454
  await writeFile2(filePath, JSON.stringify(encryptDocument(value, passphrase), null, 2), "utf8");
412
455
  return filePath;
413
456
  }
414
- async function readLocalSecret(storeRoot, ref, passphrase) {
457
+ async function readLocalSecret(storeRoot, ref, passphrase, vault = "default") {
415
458
  if (!passphrase) {
416
459
  throw new CnosManifestError(
417
460
  `Missing CNOS secret passphrase for local secret ref "${ref}". Set CNOS_SECRET_PASSPHRASE or pass processEnv explicitly.`
418
461
  );
419
462
  }
420
- const filePath = resolveSecretStoreFile(storeRoot, ref);
463
+ const filePath = resolveSecretStoreFile(storeRoot, ref, vault);
421
464
  const source = await readFile(filePath, "utf8");
422
465
  const document = JSON.parse(source);
423
466
  if (document.version !== 1 || document.algorithm !== "aes-256-gcm" || typeof document.salt !== "string" || typeof document.iv !== "string" || typeof document.tag !== "string" || typeof document.ciphertext !== "string") {
@@ -1559,6 +1602,10 @@ export {
1559
1602
  flattenObject,
1560
1603
  isSecretReference,
1561
1604
  resolveSecretStoreRoot,
1605
+ resolveSecretVaultFile,
1606
+ resolveSecretPassphrase,
1607
+ createSecretVault,
1608
+ listSecretVaults,
1562
1609
  writeLocalSecret,
1563
1610
  readLocalSecret,
1564
1611
  validateRuntime
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  applySchemaRules
3
- } from "./chunk-K2T4R5WH.js";
3
+ } from "./chunk-JPJ3S3CO.js";
4
4
 
5
5
  // ../../plugins/basic-schema/src/index.ts
6
6
  function createBasicSchemaPlugin() {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  joinConfigPath
3
- } from "./chunk-K2T4R5WH.js";
3
+ } from "./chunk-JPJ3S3CO.js";
4
4
 
5
5
  // ../../plugins/cli-args/src/index.ts
6
6
  var CLI_ARGS_PLUGIN_ID = "@kitsy/cnos/plugins/cli-args";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  toEnv,
3
3
  toPublicEnv
4
- } from "./chunk-K2T4R5WH.js";
4
+ } from "./chunk-JPJ3S3CO.js";
5
5
 
6
6
  // ../../plugins/env-export/src/index.ts
7
7
  function createEnvExportPlugin() {
@@ -3,9 +3,10 @@ import {
3
3
  isSecretReference,
4
4
  parseYaml,
5
5
  readLocalSecret,
6
+ resolveSecretPassphrase,
6
7
  resolveSecretStoreRoot,
7
8
  toPortablePath
8
- } from "./chunk-K2T4R5WH.js";
9
+ } from "./chunk-JPJ3S3CO.js";
9
10
 
10
11
  // ../../plugins/filesystem/src/helpers.ts
11
12
  import { readdir } from "fs/promises";
@@ -102,13 +103,15 @@ async function resolveSecretValue(value, processEnv) {
102
103
  return value;
103
104
  }
104
105
  if (value.provider === "local") {
105
- if (!processEnv?.CNOS_SECRET_PASSPHRASE) {
106
+ const passphrase = resolveSecretPassphrase(value.vault, processEnv);
107
+ if (!passphrase) {
106
108
  return value;
107
109
  }
108
110
  return readLocalSecret(
109
111
  resolveSecretStoreRoot(processEnv),
110
112
  value.ref,
111
- processEnv?.CNOS_SECRET_PASSPHRASE
113
+ passphrase,
114
+ value.vault
112
115
  );
113
116
  }
114
117
  if (value.provider === "env") {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  envVarToLogicalKey
3
- } from "./chunk-K2T4R5WH.js";
3
+ } from "./chunk-JPJ3S3CO.js";
4
4
 
5
5
  // ../../plugins/process-env/src/index.ts
6
6
  var PROCESS_ENV_PLUGIN_ID = "@kitsy/cnos/plugins/process-env";
package/dist/index.cjs CHANGED
@@ -1391,17 +1391,21 @@ function isObject(value) {
1391
1391
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1392
1392
  }
1393
1393
  function isSecretReference(value) {
1394
- return isObject(value) && typeof value.provider === "string" && value.provider.trim().length > 0 && typeof value.ref === "string" && value.ref.trim().length > 0 && Object.keys(value).every((key) => ["provider", "ref"].includes(key));
1394
+ return isObject(value) && typeof value.provider === "string" && value.provider.trim().length > 0 && typeof value.ref === "string" && value.ref.trim().length > 0 && (value.vault === void 0 && true || typeof value.vault === "string" && value.vault.trim().length > 0) && Object.keys(value).every((key) => ["provider", "ref", "vault"].includes(key));
1395
1395
  }
1396
1396
  function resolveSecretStoreRoot(processEnv = process.env) {
1397
1397
  return import_node_path7.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets"));
1398
1398
  }
1399
- function resolveSecretStoreFile(storeRoot, ref) {
1400
- return import_node_path7.default.join(storeRoot, "store", ...ref.split("/")).concat(".json");
1399
+ function resolveSecretStoreFile(storeRoot, ref, vault = "default") {
1400
+ return import_node_path7.default.join(storeRoot, "vaults", vault, "store", ...ref.split("/")).concat(".json");
1401
1401
  }
1402
1402
  function deriveKey(passphrase, salt) {
1403
1403
  return (0, import_node_crypto.scryptSync)(passphrase, salt, 32);
1404
1404
  }
1405
+ function resolveSecretPassphrase(vault = "default", processEnv = process.env) {
1406
+ const vaultToken = vault.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
1407
+ return processEnv[`CNOS_SECRET_PASSPHRASE_${vaultToken}`] ?? processEnv.CNOS_SECRET_PASSPHRASE;
1408
+ }
1405
1409
  function decryptDocument(document, passphrase) {
1406
1410
  const salt = Buffer.from(document.salt, "base64");
1407
1411
  const iv = Buffer.from(document.iv, "base64");
@@ -1413,13 +1417,13 @@ function decryptDocument(document, passphrase) {
1413
1417
  const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
1414
1418
  return plaintext.toString("utf8");
1415
1419
  }
1416
- async function readLocalSecret(storeRoot, ref, passphrase) {
1420
+ async function readLocalSecret(storeRoot, ref, passphrase, vault = "default") {
1417
1421
  if (!passphrase) {
1418
1422
  throw new CnosManifestError(
1419
1423
  `Missing CNOS secret passphrase for local secret ref "${ref}". Set CNOS_SECRET_PASSPHRASE or pass processEnv explicitly.`
1420
1424
  );
1421
1425
  }
1422
- const filePath = resolveSecretStoreFile(storeRoot, ref);
1426
+ const filePath = resolveSecretStoreFile(storeRoot, ref, vault);
1423
1427
  const source = await (0, import_promises7.readFile)(filePath, "utf8");
1424
1428
  const document = JSON.parse(source);
1425
1429
  if (document.version !== 1 || document.algorithm !== "aes-256-gcm" || typeof document.salt !== "string" || typeof document.iv !== "string" || typeof document.tag !== "string" || typeof document.ciphertext !== "string") {
@@ -1431,7 +1435,7 @@ async function readLocalSecret(storeRoot, ref, passphrase) {
1431
1435
  // package.json
1432
1436
  var package_default = {
1433
1437
  name: "@kitsy/cnos",
1434
- version: "1.0.1",
1438
+ version: "1.1.1",
1435
1439
  description: "Batteries-included CNOS runtime package wired with the official plugins.",
1436
1440
  type: "module",
1437
1441
  main: "./dist/index.cjs",
@@ -1504,10 +1508,11 @@ var package_default = {
1504
1508
  yaml: "^2.8.3"
1505
1509
  },
1506
1510
  scripts: {
1507
- build: "tsup --config tsup.config.ts",
1511
+ build: "rimraf dist && tsup --config tsup.config.ts",
1508
1512
  clean: "rimraf dist",
1509
1513
  dev: "tsup --config tsup.config.ts --watch",
1510
1514
  lint: "eslint src test",
1515
+ prepack: "pnpm build",
1511
1516
  test: "vitest run",
1512
1517
  typecheck: "tsc -p tsconfig.json --noEmit"
1513
1518
  }
@@ -1825,13 +1830,15 @@ async function resolveSecretValue(value, processEnv) {
1825
1830
  return value;
1826
1831
  }
1827
1832
  if (value.provider === "local") {
1828
- if (!processEnv?.CNOS_SECRET_PASSPHRASE) {
1833
+ const passphrase = resolveSecretPassphrase(value.vault, processEnv);
1834
+ if (!passphrase) {
1829
1835
  return value;
1830
1836
  }
1831
1837
  return readLocalSecret(
1832
1838
  resolveSecretStoreRoot(processEnv),
1833
1839
  value.ref,
1834
- processEnv?.CNOS_SECRET_PASSPHRASE
1840
+ passphrase,
1841
+ value.vault
1835
1842
  );
1836
1843
  }
1837
1844
  if (value.provider === "env") {
package/dist/index.js CHANGED
@@ -1,23 +1,23 @@
1
1
  import {
2
2
  createBasicSchemaPlugin
3
- } from "./chunk-H65FPTDM.js";
3
+ } from "./chunk-L3HOQHCH.js";
4
4
  import {
5
5
  createCliArgsPlugin
6
- } from "./chunk-GGYIRIGU.js";
6
+ } from "./chunk-M4S6PYM5.js";
7
7
  import {
8
8
  createDotenvPlugin
9
- } from "./chunk-44JOQPSN.js";
9
+ } from "./chunk-7GNXYEO6.js";
10
10
  import {
11
11
  createEnvExportPlugin,
12
12
  createPublicEnvExportPlugin
13
- } from "./chunk-ASZ7I3JJ.js";
13
+ } from "./chunk-PBU5NAX4.js";
14
14
  import {
15
15
  createFilesystemSecretsPlugin,
16
16
  createFilesystemValuesPlugin
17
- } from "./chunk-KG6OZX5C.js";
17
+ } from "./chunk-QKJ6QLRS.js";
18
18
  import {
19
19
  createProcessEnvPlugin
20
- } from "./chunk-CGTFH4QQ.js";
20
+ } from "./chunk-X4GOXEKX.js";
21
21
  import {
22
22
  createCnos,
23
23
  createProvenanceInspector,
@@ -25,12 +25,12 @@ import {
25
25
  toEnv,
26
26
  toPublicEnv,
27
27
  writeDump
28
- } from "./chunk-K2T4R5WH.js";
28
+ } from "./chunk-JPJ3S3CO.js";
29
29
 
30
30
  // package.json
31
31
  var package_default = {
32
32
  name: "@kitsy/cnos",
33
- version: "1.0.1",
33
+ version: "1.1.1",
34
34
  description: "Batteries-included CNOS runtime package wired with the official plugins.",
35
35
  type: "module",
36
36
  main: "./dist/index.cjs",
@@ -103,10 +103,11 @@ var package_default = {
103
103
  yaml: "^2.8.3"
104
104
  },
105
105
  scripts: {
106
- build: "tsup --config tsup.config.ts",
106
+ build: "rimraf dist && tsup --config tsup.config.ts",
107
107
  clean: "rimraf dist",
108
108
  dev: "tsup --config tsup.config.ts --watch",
109
109
  lint: "eslint src test",
110
+ prepack: "pnpm build",
110
111
  test: "vitest run",
111
112
  typecheck: "tsc -p tsconfig.json --noEmit"
112
113
  }
package/dist/internal.cjs CHANGED
@@ -30,10 +30,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/internal.ts
31
31
  var internal_exports = {};
32
32
  __export(internal_exports, {
33
+ createSecretVault: () => createSecretVault,
33
34
  flattenObject: () => flattenObject,
35
+ listSecretVaults: () => listSecretVaults,
34
36
  parseYaml: () => parseYaml,
35
37
  resolveConfigDocumentPath: () => resolveConfigDocumentPath,
38
+ resolveSecretPassphrase: () => resolveSecretPassphrase,
36
39
  resolveSecretStoreRoot: () => resolveSecretStoreRoot,
40
+ resolveSecretVaultFile: () => resolveSecretVaultFile,
37
41
  stringifyYaml: () => stringifyYaml,
38
42
  validateRuntime: () => validateRuntime,
39
43
  writeLocalSecret: () => writeLocalSecret
@@ -146,12 +150,19 @@ var import_node_path7 = __toESM(require("path"), 1);
146
150
  function resolveSecretStoreRoot(processEnv = process.env) {
147
151
  return import_node_path7.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets"));
148
152
  }
149
- function resolveSecretStoreFile(storeRoot, ref) {
150
- return import_node_path7.default.join(storeRoot, "store", ...ref.split("/")).concat(".json");
153
+ function resolveSecretVaultFile(storeRoot, vault = "default") {
154
+ return import_node_path7.default.join(storeRoot, "vaults", `${vault}.json`);
155
+ }
156
+ function resolveSecretStoreFile(storeRoot, ref, vault = "default") {
157
+ return import_node_path7.default.join(storeRoot, "vaults", vault, "store", ...ref.split("/")).concat(".json");
151
158
  }
152
159
  function deriveKey(passphrase, salt) {
153
160
  return (0, import_node_crypto.scryptSync)(passphrase, salt, 32);
154
161
  }
162
+ function resolveSecretPassphrase(vault = "default", processEnv = process.env) {
163
+ const vaultToken = vault.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
164
+ return processEnv[`CNOS_SECRET_PASSPHRASE_${vaultToken}`] ?? processEnv.CNOS_SECRET_PASSPHRASE;
165
+ }
155
166
  function encryptDocument(value, passphrase) {
156
167
  const salt = (0, import_node_crypto.randomBytes)(16);
157
168
  const iv = (0, import_node_crypto.randomBytes)(12);
@@ -168,8 +179,44 @@ function encryptDocument(value, passphrase) {
168
179
  ciphertext: ciphertext.toString("base64")
169
180
  };
170
181
  }
171
- async function writeLocalSecret(storeRoot, ref, value, passphrase) {
172
- const filePath = resolveSecretStoreFile(storeRoot, ref);
182
+ async function createSecretVault(storeRoot, vault, passphrase) {
183
+ const normalizedVault = vault.trim() || "default";
184
+ const filePath = resolveSecretVaultFile(storeRoot, normalizedVault);
185
+ await (0, import_promises7.mkdir)(import_node_path7.default.dirname(filePath), { recursive: true });
186
+ const document = {
187
+ version: 1,
188
+ name: normalizedVault,
189
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
190
+ verifier: encryptDocument(`cnos-vault:${normalizedVault}`, passphrase)
191
+ };
192
+ await (0, import_promises7.writeFile)(filePath, JSON.stringify(document, null, 2), "utf8");
193
+ return filePath;
194
+ }
195
+ async function ensureSecretVault(storeRoot, vault, passphrase) {
196
+ const normalizedVault = vault.trim() || "default";
197
+ const filePath = resolveSecretVaultFile(storeRoot, normalizedVault);
198
+ try {
199
+ await (0, import_promises7.readFile)(filePath, "utf8");
200
+ return filePath;
201
+ } catch (error) {
202
+ if (error.code !== "ENOENT") {
203
+ throw error;
204
+ }
205
+ }
206
+ return createSecretVault(storeRoot, normalizedVault, passphrase);
207
+ }
208
+ async function listSecretVaults(storeRoot) {
209
+ const vaultRoot = import_node_path7.default.join(storeRoot, "vaults");
210
+ try {
211
+ const entries = await (0, import_promises7.readdir)(vaultRoot, { withFileTypes: true });
212
+ return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.replace(/\.json$/, "")).sort((left, right) => left.localeCompare(right));
213
+ } catch {
214
+ return [];
215
+ }
216
+ }
217
+ async function writeLocalSecret(storeRoot, ref, value, passphrase, vault = "default") {
218
+ await ensureSecretVault(storeRoot, vault, passphrase);
219
+ const filePath = resolveSecretStoreFile(storeRoot, ref, vault);
173
220
  await (0, import_promises7.mkdir)(import_node_path7.default.dirname(filePath), { recursive: true });
174
221
  await (0, import_promises7.writeFile)(filePath, JSON.stringify(encryptDocument(value, passphrase), null, 2), "utf8");
175
222
  return filePath;
@@ -278,10 +325,14 @@ async function validateRuntime(runtime) {
278
325
  }
279
326
  // Annotate the CommonJS export names for ESM import in node:
280
327
  0 && (module.exports = {
328
+ createSecretVault,
281
329
  flattenObject,
330
+ listSecretVaults,
282
331
  parseYaml,
283
332
  resolveConfigDocumentPath,
333
+ resolveSecretPassphrase,
284
334
  resolveSecretStoreRoot,
335
+ resolveSecretVaultFile,
285
336
  stringifyYaml,
286
337
  validateRuntime,
287
338
  writeLocalSecret
@@ -8,13 +8,18 @@ declare function resolveConfigDocumentPath(workspaceRoot: string, namespace: 'va
8
8
  interface SecretReference {
9
9
  provider: string;
10
10
  ref: string;
11
+ vault?: string;
11
12
  }
12
13
  declare function resolveSecretStoreRoot(processEnv?: Record<string, string | undefined>): string;
13
- declare function writeLocalSecret(storeRoot: string, ref: string, value: string, passphrase: string): Promise<string>;
14
+ declare function resolveSecretVaultFile(storeRoot: string, vault?: string): string;
15
+ declare function resolveSecretPassphrase(vault?: string, processEnv?: Record<string, string | undefined>): string | undefined;
16
+ declare function createSecretVault(storeRoot: string, vault: string, passphrase: string): Promise<string>;
17
+ declare function listSecretVaults(storeRoot: string): Promise<string[]>;
18
+ declare function writeLocalSecret(storeRoot: string, ref: string, value: string, passphrase: string, vault?: string): Promise<string>;
14
19
 
15
20
  declare function parseYaml<T>(source: string): T;
16
21
  declare function stringifyYaml(value: unknown): string;
17
22
 
18
23
  declare function validateRuntime(runtime: CnosRuntime): Promise<ValidationSummary>;
19
24
 
20
- export { type SecretReference, ValidationSummary, flattenObject, parseYaml, resolveConfigDocumentPath, resolveSecretStoreRoot, stringifyYaml, validateRuntime, writeLocalSecret };
25
+ export { type SecretReference, ValidationSummary, createSecretVault, flattenObject, listSecretVaults, parseYaml, resolveConfigDocumentPath, resolveSecretPassphrase, resolveSecretStoreRoot, resolveSecretVaultFile, stringifyYaml, validateRuntime, writeLocalSecret };
@@ -8,13 +8,18 @@ declare function resolveConfigDocumentPath(workspaceRoot: string, namespace: 'va
8
8
  interface SecretReference {
9
9
  provider: string;
10
10
  ref: string;
11
+ vault?: string;
11
12
  }
12
13
  declare function resolveSecretStoreRoot(processEnv?: Record<string, string | undefined>): string;
13
- declare function writeLocalSecret(storeRoot: string, ref: string, value: string, passphrase: string): Promise<string>;
14
+ declare function resolveSecretVaultFile(storeRoot: string, vault?: string): string;
15
+ declare function resolveSecretPassphrase(vault?: string, processEnv?: Record<string, string | undefined>): string | undefined;
16
+ declare function createSecretVault(storeRoot: string, vault: string, passphrase: string): Promise<string>;
17
+ declare function listSecretVaults(storeRoot: string): Promise<string[]>;
18
+ declare function writeLocalSecret(storeRoot: string, ref: string, value: string, passphrase: string, vault?: string): Promise<string>;
14
19
 
15
20
  declare function parseYaml<T>(source: string): T;
16
21
  declare function stringifyYaml(value: unknown): string;
17
22
 
18
23
  declare function validateRuntime(runtime: CnosRuntime): Promise<ValidationSummary>;
19
24
 
20
- export { type SecretReference, ValidationSummary, flattenObject, parseYaml, resolveConfigDocumentPath, resolveSecretStoreRoot, stringifyYaml, validateRuntime, writeLocalSecret };
25
+ export { type SecretReference, ValidationSummary, createSecretVault, flattenObject, listSecretVaults, parseYaml, resolveConfigDocumentPath, resolveSecretPassphrase, resolveSecretStoreRoot, resolveSecretVaultFile, stringifyYaml, validateRuntime, writeLocalSecret };
package/dist/internal.js CHANGED
@@ -1,17 +1,25 @@
1
1
  import {
2
+ createSecretVault,
2
3
  flattenObject,
4
+ listSecretVaults,
3
5
  parseYaml,
4
6
  resolveConfigDocumentPath,
7
+ resolveSecretPassphrase,
5
8
  resolveSecretStoreRoot,
9
+ resolveSecretVaultFile,
6
10
  stringifyYaml,
7
11
  validateRuntime,
8
12
  writeLocalSecret
9
- } from "./chunk-K2T4R5WH.js";
13
+ } from "./chunk-JPJ3S3CO.js";
10
14
  export {
15
+ createSecretVault,
11
16
  flattenObject,
17
+ listSecretVaults,
12
18
  parseYaml,
13
19
  resolveConfigDocumentPath,
20
+ resolveSecretPassphrase,
14
21
  resolveSecretStoreRoot,
22
+ resolveSecretVaultFile,
15
23
  stringifyYaml,
16
24
  validateRuntime,
17
25
  writeLocalSecret
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createBasicSchemaPlugin
3
- } from "../chunk-H65FPTDM.js";
4
- import "../chunk-K2T4R5WH.js";
3
+ } from "../chunk-L3HOQHCH.js";
4
+ import "../chunk-JPJ3S3CO.js";
5
5
  export {
6
6
  createBasicSchemaPlugin
7
7
  };
@@ -2,8 +2,8 @@ import {
2
2
  cliArgEntriesFromArgs,
3
3
  createCliArgsPlugin,
4
4
  parseCliArgs
5
- } from "../chunk-GGYIRIGU.js";
6
- import "../chunk-K2T4R5WH.js";
5
+ } from "../chunk-M4S6PYM5.js";
6
+ import "../chunk-JPJ3S3CO.js";
7
7
  export {
8
8
  cliArgEntriesFromArgs,
9
9
  createCliArgsPlugin,
@@ -2,8 +2,8 @@ import {
2
2
  createDotenvPlugin,
3
3
  dotenvEntriesFromObject,
4
4
  parseDotenv
5
- } from "../chunk-44JOQPSN.js";
6
- import "../chunk-K2T4R5WH.js";
5
+ } from "../chunk-7GNXYEO6.js";
6
+ import "../chunk-JPJ3S3CO.js";
7
7
  export {
8
8
  createDotenvPlugin,
9
9
  dotenvEntriesFromObject,
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  createEnvExportPlugin,
3
3
  createPublicEnvExportPlugin
4
- } from "../chunk-ASZ7I3JJ.js";
4
+ } from "../chunk-PBU5NAX4.js";
5
5
  import {
6
6
  toEnv,
7
7
  toPublicEnv
8
- } from "../chunk-K2T4R5WH.js";
8
+ } from "../chunk-JPJ3S3CO.js";
9
9
  export {
10
10
  createEnvExportPlugin,
11
11
  createPublicEnvExportPlugin,
@@ -112,17 +112,21 @@ function isObject(value) {
112
112
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
113
113
  }
114
114
  function isSecretReference(value) {
115
- return isObject(value) && typeof value.provider === "string" && value.provider.trim().length > 0 && typeof value.ref === "string" && value.ref.trim().length > 0 && Object.keys(value).every((key) => ["provider", "ref"].includes(key));
115
+ return isObject(value) && typeof value.provider === "string" && value.provider.trim().length > 0 && typeof value.ref === "string" && value.ref.trim().length > 0 && (value.vault === void 0 && true || typeof value.vault === "string" && value.vault.trim().length > 0) && Object.keys(value).every((key) => ["provider", "ref", "vault"].includes(key));
116
116
  }
117
117
  function resolveSecretStoreRoot(processEnv = process.env) {
118
118
  return import_node_path7.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets"));
119
119
  }
120
- function resolveSecretStoreFile(storeRoot, ref) {
121
- return import_node_path7.default.join(storeRoot, "store", ...ref.split("/")).concat(".json");
120
+ function resolveSecretStoreFile(storeRoot, ref, vault = "default") {
121
+ return import_node_path7.default.join(storeRoot, "vaults", vault, "store", ...ref.split("/")).concat(".json");
122
122
  }
123
123
  function deriveKey(passphrase, salt) {
124
124
  return (0, import_node_crypto.scryptSync)(passphrase, salt, 32);
125
125
  }
126
+ function resolveSecretPassphrase(vault = "default", processEnv = process.env) {
127
+ const vaultToken = vault.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
128
+ return processEnv[`CNOS_SECRET_PASSPHRASE_${vaultToken}`] ?? processEnv.CNOS_SECRET_PASSPHRASE;
129
+ }
126
130
  function decryptDocument(document, passphrase) {
127
131
  const salt = Buffer.from(document.salt, "base64");
128
132
  const iv = Buffer.from(document.iv, "base64");
@@ -134,13 +138,13 @@ function decryptDocument(document, passphrase) {
134
138
  const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
135
139
  return plaintext.toString("utf8");
136
140
  }
137
- async function readLocalSecret(storeRoot, ref, passphrase) {
141
+ async function readLocalSecret(storeRoot, ref, passphrase, vault = "default") {
138
142
  if (!passphrase) {
139
143
  throw new CnosManifestError(
140
144
  `Missing CNOS secret passphrase for local secret ref "${ref}". Set CNOS_SECRET_PASSPHRASE or pass processEnv explicitly.`
141
145
  );
142
146
  }
143
- const filePath = resolveSecretStoreFile(storeRoot, ref);
147
+ const filePath = resolveSecretStoreFile(storeRoot, ref, vault);
144
148
  const source = await (0, import_promises7.readFile)(filePath, "utf8");
145
149
  const document = JSON.parse(source);
146
150
  if (document.version !== 1 || document.algorithm !== "aes-256-gcm" || typeof document.salt !== "string" || typeof document.iv !== "string" || typeof document.tag !== "string" || typeof document.ciphertext !== "string") {
@@ -242,13 +246,15 @@ async function resolveSecretValue(value, processEnv) {
242
246
  return value;
243
247
  }
244
248
  if (value.provider === "local") {
245
- if (!processEnv?.CNOS_SECRET_PASSPHRASE) {
249
+ const passphrase = resolveSecretPassphrase(value.vault, processEnv);
250
+ if (!passphrase) {
246
251
  return value;
247
252
  }
248
253
  return readLocalSecret(
249
254
  resolveSecretStoreRoot(processEnv),
250
255
  value.ref,
251
- processEnv?.CNOS_SECRET_PASSPHRASE
256
+ passphrase,
257
+ value.vault
252
258
  );
253
259
  }
254
260
  if (value.provider === "env") {
@@ -5,8 +5,8 @@ import {
5
5
  filesystemSecretsReader,
6
6
  filesystemValuesReader,
7
7
  yamlObjectToEntries
8
- } from "../chunk-KG6OZX5C.js";
9
- import "../chunk-K2T4R5WH.js";
8
+ } from "../chunk-QKJ6QLRS.js";
9
+ import "../chunk-JPJ3S3CO.js";
10
10
  export {
11
11
  collectFilesystemLayerFiles,
12
12
  createFilesystemSecretsPlugin,
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  createProcessEnvPlugin,
3
3
  processEnvEntriesFromObject
4
- } from "../chunk-CGTFH4QQ.js";
5
- import "../chunk-K2T4R5WH.js";
4
+ } from "../chunk-X4GOXEKX.js";
5
+ import "../chunk-JPJ3S3CO.js";
6
6
  export {
7
7
  createProcessEnvPlugin,
8
8
  processEnvEntriesFromObject
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kitsy/cnos",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Batteries-included CNOS runtime package wired with the official plugins.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -73,7 +73,7 @@
73
73
  "yaml": "^2.8.3"
74
74
  },
75
75
  "scripts": {
76
- "build": "tsup --config tsup.config.ts",
76
+ "build": "rimraf dist && tsup --config tsup.config.ts",
77
77
  "clean": "rimraf dist",
78
78
  "dev": "tsup --config tsup.config.ts --watch",
79
79
  "lint": "eslint src test",