@kitsy/cnos 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.
Files changed (64) hide show
  1. package/README.md +3 -3
  2. package/dist/build/index.cjs +902 -113
  3. package/dist/build/index.d.cts +1 -1
  4. package/dist/build/index.d.ts +1 -1
  5. package/dist/build/index.js +22 -10
  6. package/dist/{chunk-WHUGFPE4.js → chunk-CDXJISGB.js} +1 -1
  7. package/dist/{chunk-APCTXRUN.js → chunk-DRKDNY4I.js} +998 -191
  8. package/dist/chunk-E7SE6N26.js +189 -0
  9. package/dist/{chunk-SO5XREEU.js → chunk-EDCLLCNL.js} +32 -11
  10. package/dist/{chunk-SXTMTACL.js → chunk-FC3IV6A7.js} +1 -31
  11. package/dist/{chunk-MLQGYCO7.js → chunk-JDII6O72.js} +1 -1
  12. package/dist/chunk-K6QYI2T4.js +105 -0
  13. package/dist/{chunk-EIN55XXA.js → chunk-OOKFRWTN.js} +1 -1
  14. package/dist/{chunk-ZA74BO47.js → chunk-OWUZQ4OH.js} +1 -1
  15. package/dist/{chunk-RD5WMHPM.js → chunk-QTKXPY3N.js} +1 -1
  16. package/dist/configure/index.cjs +2928 -0
  17. package/dist/configure/index.d.cts +12 -0
  18. package/dist/configure/index.d.ts +12 -0
  19. package/dist/configure/index.js +24 -0
  20. package/dist/{envNaming-BTJpH93W.d.cts → envNaming-D6k66myh.d.cts} +1 -1
  21. package/dist/{envNaming-CcsqAel3.d.ts → envNaming-Dy3WYiGK.d.ts} +1 -1
  22. package/dist/index.cjs +1142 -178
  23. package/dist/index.d.cts +2 -13
  24. package/dist/index.d.ts +2 -13
  25. package/dist/index.js +13 -25
  26. package/dist/internal.cjs +1512 -80
  27. package/dist/internal.d.cts +170 -14
  28. package/dist/internal.d.ts +170 -14
  29. package/dist/internal.js +645 -5
  30. package/dist/plugin/basic-schema.cjs +29 -2
  31. package/dist/plugin/basic-schema.d.cts +1 -1
  32. package/dist/plugin/basic-schema.d.ts +1 -1
  33. package/dist/plugin/basic-schema.js +2 -2
  34. package/dist/plugin/cli-args.cjs +29 -2
  35. package/dist/plugin/cli-args.d.cts +1 -1
  36. package/dist/plugin/cli-args.d.ts +1 -1
  37. package/dist/plugin/cli-args.js +2 -2
  38. package/dist/plugin/dotenv.cjs +36 -9
  39. package/dist/plugin/dotenv.d.cts +2 -2
  40. package/dist/plugin/dotenv.d.ts +2 -2
  41. package/dist/plugin/dotenv.js +2 -2
  42. package/dist/plugin/env-export.cjs +31 -2
  43. package/dist/plugin/env-export.d.cts +2 -2
  44. package/dist/plugin/env-export.d.ts +2 -2
  45. package/dist/plugin/env-export.js +2 -2
  46. package/dist/plugin/filesystem.cjs +46 -91
  47. package/dist/plugin/filesystem.d.cts +1 -1
  48. package/dist/plugin/filesystem.d.ts +1 -1
  49. package/dist/plugin/filesystem.js +2 -2
  50. package/dist/plugin/process-env.cjs +31 -4
  51. package/dist/plugin/process-env.d.cts +2 -2
  52. package/dist/plugin/process-env.d.ts +2 -2
  53. package/dist/plugin/process-env.js +2 -2
  54. package/dist/{plugin-DkOIT5uI.d.cts → plugin-CyNkf7Dm.d.cts} +14 -2
  55. package/dist/{plugin-DkOIT5uI.d.ts → plugin-CyNkf7Dm.d.ts} +14 -2
  56. package/dist/runtime/index.cjs +956 -128
  57. package/dist/runtime/index.d.cts +1 -1
  58. package/dist/runtime/index.d.ts +1 -1
  59. package/dist/runtime/index.js +11 -186
  60. package/dist/{toPublicEnv-DvFeV3qG.d.cts → toPublicEnv-Cz72m6y0.d.cts} +1 -1
  61. package/dist/{toPublicEnv-C9clvXLo.d.ts → toPublicEnv-D2PZkaN-.d.ts} +1 -1
  62. package/package.json +11 -1
  63. package/dist/chunk-JUHPBAEH.js +0 -20
  64. package/dist/chunk-PQ4KSV76.js +0 -50
@@ -53,6 +53,11 @@ var CnosSecurityError = class extends CnosError {
53
53
  super(message);
54
54
  }
55
55
  };
56
+ var CnosAuthenticationError = class extends CnosError {
57
+ constructor(message) {
58
+ super(message);
59
+ }
60
+ };
56
61
  var CnosKeyNotFoundError = class extends CnosError {
57
62
  constructor(key) {
58
63
  super(`Missing required CNOS config key: ${key}`);
@@ -105,6 +110,68 @@ function createProvenanceInspector() {
105
110
  };
106
111
  }
107
112
 
113
+ // ../core/src/keychain/linux.ts
114
+ var import_node_child_process = require("child_process");
115
+ var import_node_util = require("util");
116
+ var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
117
+ async function readLinuxKeychain(entry) {
118
+ try {
119
+ const { stdout } = await execFileAsync("secret-tool", ["lookup", "service", "cnos", "account", entry]);
120
+ const value = stdout.trim();
121
+ return value.length > 0 ? value : void 0;
122
+ } catch {
123
+ return void 0;
124
+ }
125
+ }
126
+
127
+ // ../core/src/keychain/macos.ts
128
+ var import_node_child_process2 = require("child_process");
129
+ var import_node_util2 = require("util");
130
+ var execFileAsync2 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
131
+ async function readMacosKeychain(entry) {
132
+ try {
133
+ const { stdout } = await execFileAsync2("security", ["find-generic-password", "-a", "cnos", "-s", entry, "-w"]);
134
+ const value = stdout.trim();
135
+ return value.length > 0 ? value : void 0;
136
+ } catch {
137
+ return void 0;
138
+ }
139
+ }
140
+
141
+ // ../core/src/keychain/windows.ts
142
+ var import_node_child_process3 = require("child_process");
143
+ var import_node_util3 = require("util");
144
+ var execFileAsync3 = (0, import_node_util3.promisify)(import_node_child_process3.execFile);
145
+ function wrap(script) {
146
+ return ["-NoProfile", "-Command", script];
147
+ }
148
+ async function readWindowsKeychain(entry) {
149
+ try {
150
+ const { stdout } = await execFileAsync3(
151
+ "powershell",
152
+ wrap(`Add-Type -AssemblyName System.Runtime.WindowsRuntime; [Windows.Security.Credentials.PasswordVault,Windows.Security.Credentials,ContentType=WindowsRuntime] > $null; $vault = New-Object Windows.Security.Credentials.PasswordVault; $credential = $vault.Retrieve('cnos','${entry}'); $credential.RetrievePassword(); Write-Output $credential.Password`)
153
+ );
154
+ const value = stdout.trim();
155
+ return value.length > 0 ? value : void 0;
156
+ } catch {
157
+ return void 0;
158
+ }
159
+ }
160
+
161
+ // ../core/src/keychain/index.ts
162
+ async function readKeychain(entry) {
163
+ if (process.platform === "win32") {
164
+ return readWindowsKeychain(entry);
165
+ }
166
+ if (process.platform === "darwin") {
167
+ return readMacosKeychain(entry);
168
+ }
169
+ if (process.platform === "linux") {
170
+ return readLinuxKeychain(entry);
171
+ }
172
+ return void 0;
173
+ }
174
+
108
175
  // ../core/src/manifest/loadManifest.ts
109
176
  var import_promises2 = require("fs/promises");
110
177
  var import_node_path2 = __toESM(require("path"), 1);
@@ -191,6 +258,9 @@ var import_yaml = require("yaml");
191
258
  function parseYaml(source) {
192
259
  return (0, import_yaml.parse)(source);
193
260
  }
261
+ function stringifyYaml(value) {
262
+ return (0, import_yaml.stringify)(value);
263
+ }
194
264
 
195
265
  // ../core/src/manifest/normalizeManifest.ts
196
266
  var DEFAULT_RESOLVE_FROM = ["cli.profile", "env.CNOS_PROFILE", "default"];
@@ -278,22 +348,82 @@ function normalizeNamespaces(namespaces) {
278
348
  function normalizeVaults(vaults) {
279
349
  return Object.fromEntries(
280
350
  Object.entries(vaults ?? {}).map(([name, definition]) => {
351
+ const legacyPassphrase = definition.passphrase;
352
+ if (legacyPassphrase !== void 0) {
353
+ throw new CnosManifestError(
354
+ `Vault "${name}" uses legacy passphrase configuration. Use vaults.${name}.auth instead.`
355
+ );
356
+ }
281
357
  const provider = definition.provider?.trim();
282
358
  if (!provider) {
283
359
  throw new CnosManifestError(`Vault "${name}" requires a provider`);
284
360
  }
361
+ const normalizedAuth = normalizeVaultAuth(name, provider, definition.auth);
362
+ const normalizedMapping = Object.fromEntries(
363
+ Object.entries(definition.mapping ?? {}).filter(
364
+ (entry) => typeof entry[0] === "string" && typeof entry[1] === "string"
365
+ ).map(([envVar, logicalRef]) => [envVar.trim(), logicalRef.trim()]).filter(([envVar, logicalRef]) => envVar.length > 0 && logicalRef.length > 0)
366
+ );
285
367
  return [
286
368
  name,
287
369
  {
288
370
  provider,
289
- ...definition.passphrase?.trim() ? {
290
- passphrase: definition.passphrase.trim()
371
+ auth: normalizedAuth,
372
+ ...Object.keys(normalizedMapping).length > 0 ? {
373
+ mapping: normalizedMapping
291
374
  } : {}
292
375
  }
293
376
  ];
294
377
  })
295
378
  );
296
379
  }
380
+ function normalizeAuthSources(value) {
381
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
382
+ return void 0;
383
+ }
384
+ const sources = Array.isArray(value.from) ? value.from : void 0;
385
+ const normalized = (sources ?? []).map((entry) => typeof entry === "string" ? entry.trim() : "").filter(Boolean);
386
+ return normalized.length > 0 ? normalized : void 0;
387
+ }
388
+ function normalizeVaultAuth(vaultName, provider, auth) {
389
+ if (provider === "local") {
390
+ const passphraseSources = normalizeAuthSources(auth?.passphrase);
391
+ const defaultToken = vaultName.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
392
+ const defaultSources = [
393
+ ...defaultToken ? [`env:CNOS_SECRET_PASSPHRASE_${defaultToken}`] : [],
394
+ "env:CNOS_SECRET_PASSPHRASE",
395
+ `keychain:cnos/${vaultName}`,
396
+ "prompt"
397
+ ];
398
+ return {
399
+ method: auth?.method ?? "passphrase",
400
+ passphrase: {
401
+ from: passphraseSources ?? defaultSources
402
+ },
403
+ ...auth?.config ? { config: auth.config } : {}
404
+ };
405
+ }
406
+ if (provider === "github-secrets") {
407
+ return {
408
+ method: auth?.method ?? "environment",
409
+ ...auth?.config ? { config: auth.config } : {}
410
+ };
411
+ }
412
+ return {
413
+ ...auth?.method ? { method: auth.method } : {},
414
+ ...normalizeAuthSources(auth?.passphrase) ? {
415
+ passphrase: {
416
+ from: normalizeAuthSources(auth?.passphrase) ?? []
417
+ }
418
+ } : {},
419
+ ...normalizeAuthSources(auth?.token) ? {
420
+ token: {
421
+ from: normalizeAuthSources(auth?.token) ?? []
422
+ }
423
+ } : {},
424
+ ...auth?.config ? { config: auth.config } : {}
425
+ };
426
+ }
297
427
  function normalizeManifest(manifest) {
298
428
  const version = manifest.version ?? 1;
299
429
  if (version !== 1) {
@@ -1097,6 +1227,712 @@ async function runPipeline(options) {
1097
1227
  return collectedEntries.flat();
1098
1228
  }
1099
1229
 
1230
+ // ../core/src/secrets/auditLog.ts
1231
+ var import_promises8 = require("fs/promises");
1232
+ var import_node_path8 = __toESM(require("path"), 1);
1233
+
1234
+ // ../core/src/utils/secretStore.ts
1235
+ var import_node_crypto = require("crypto");
1236
+ var import_promises7 = require("fs/promises");
1237
+ var import_node_path7 = __toESM(require("path"), 1);
1238
+
1239
+ // ../core/src/secrets/sessionStore.ts
1240
+ var import_promises6 = require("fs/promises");
1241
+ var import_node_path6 = __toESM(require("path"), 1);
1242
+ function buildSessionRoot(processEnv = process.env) {
1243
+ return import_node_path6.default.join(import_node_path6.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets")), "sessions");
1244
+ }
1245
+ function buildSessionPath(vault, processEnv) {
1246
+ return import_node_path6.default.join(buildSessionRoot(processEnv), `${vault}.json`);
1247
+ }
1248
+ async function readVaultSessionKey(vault, processEnv) {
1249
+ try {
1250
+ const source = await (0, import_promises6.readFile)(buildSessionPath(vault, processEnv), "utf8");
1251
+ const document = JSON.parse(source);
1252
+ if (document.version !== 1 || typeof document.derivedKey !== "string") {
1253
+ return void 0;
1254
+ }
1255
+ const key = Buffer.from(document.derivedKey, "hex");
1256
+ return key.length > 0 ? key : void 0;
1257
+ } catch {
1258
+ return void 0;
1259
+ }
1260
+ }
1261
+
1262
+ // ../core/src/utils/secretStore.ts
1263
+ var KEY_LENGTH = 32;
1264
+ var SALT_LENGTH = 32;
1265
+ var IV_LENGTH = 12;
1266
+ var AUTH_TAG_LENGTH = 16;
1267
+ var PBKDF2_ITERATIONS = 6e5;
1268
+ var KEYSTORE_VERSION = 1;
1269
+ var METADATA_VERSION = 1;
1270
+ var META_FILENAME = "meta.yml";
1271
+ var KEYSTORE_FILENAME = "keystore.enc";
1272
+ function isObject(value) {
1273
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1274
+ }
1275
+ function isSecretReference(value) {
1276
+ 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));
1277
+ }
1278
+ function resolveSecretStoreRoot(processEnv = process.env) {
1279
+ return import_node_path7.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets"));
1280
+ }
1281
+ function normalizeVaultToken(vault = "default") {
1282
+ return vault.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
1283
+ }
1284
+ function getVaultPassphraseEnvVar(vault = "default") {
1285
+ const vaultToken = normalizeVaultToken(vault);
1286
+ return vaultToken && vaultToken !== "DEFAULT" ? `CNOS_SECRET_PASSPHRASE_${vaultToken}` : "CNOS_SECRET_PASSPHRASE";
1287
+ }
1288
+ function getVaultSessionKeyEnvVar(vault = "default") {
1289
+ const vaultToken = normalizeVaultToken(vault);
1290
+ return `__CNOS_VAULT_KEY_${vaultToken || "DEFAULT"}__`;
1291
+ }
1292
+ function resolveSecretPassphrase(vault = "default", processEnv = process.env) {
1293
+ return processEnv[getVaultPassphraseEnvVar(vault)] ?? processEnv.CNOS_SECRET_PASSPHRASE;
1294
+ }
1295
+ function resolveVaultSessionKey(vault = "default", processEnv = process.env) {
1296
+ const encoded = processEnv[getVaultSessionKeyEnvVar(vault)];
1297
+ if (!encoded) {
1298
+ return readVaultSessionKey(vault, processEnv);
1299
+ }
1300
+ try {
1301
+ const key = Buffer.from(encoded, "hex");
1302
+ return key.length === KEY_LENGTH ? key : void 0;
1303
+ } catch {
1304
+ return void 0;
1305
+ }
1306
+ }
1307
+ function deriveVaultKey(passphrase, salt, iterations = PBKDF2_ITERATIONS) {
1308
+ return (0, import_node_crypto.pbkdf2Sync)(passphrase, salt, iterations, KEY_LENGTH, "sha512");
1309
+ }
1310
+ function buildMetaPath(storeRoot, vault = "default") {
1311
+ return import_node_path7.default.join(storeRoot, "vaults", vault, META_FILENAME);
1312
+ }
1313
+ function buildKeystorePath(storeRoot, vault = "default") {
1314
+ return import_node_path7.default.join(storeRoot, "vaults", vault, KEYSTORE_FILENAME);
1315
+ }
1316
+ function buildLegacyVaultFile(storeRoot, vault = "default") {
1317
+ return import_node_path7.default.join(storeRoot, "vaults", `${vault}.json`);
1318
+ }
1319
+ function buildLegacyVaultStoreRoot(storeRoot, vault = "default") {
1320
+ return import_node_path7.default.join(storeRoot, "vaults", vault, "store");
1321
+ }
1322
+ function assertVaultMetadata(value, filePath) {
1323
+ if (!isObject(value)) {
1324
+ throw new CnosManifestError("Invalid CNOS vault metadata", filePath);
1325
+ }
1326
+ if (value.version !== METADATA_VERSION || value.algorithm !== "aes-256-gcm" || value.kdf !== "pbkdf2-sha512" || typeof value.iterations !== "number" || typeof value.salt !== "string" || typeof value.createdAt !== "string" || typeof value.secretCount !== "number") {
1327
+ throw new CnosManifestError("Invalid CNOS vault metadata", filePath);
1328
+ }
1329
+ return value;
1330
+ }
1331
+ async function exists3(targetPath) {
1332
+ try {
1333
+ await (0, import_promises7.stat)(targetPath);
1334
+ return true;
1335
+ } catch {
1336
+ return false;
1337
+ }
1338
+ }
1339
+ async function detectLegacyVaultFormat(storeRoot, vault = "default") {
1340
+ const legacyFile = buildLegacyVaultFile(storeRoot, vault);
1341
+ const legacyStore = buildLegacyVaultStoreRoot(storeRoot, vault);
1342
+ if (await exists3(legacyFile)) {
1343
+ return legacyFile;
1344
+ }
1345
+ if (await exists3(legacyStore)) {
1346
+ return legacyStore;
1347
+ }
1348
+ return void 0;
1349
+ }
1350
+ async function assertNoLegacyVaultFormat(storeRoot, vault = "default") {
1351
+ const legacyPath = await detectLegacyVaultFormat(storeRoot, vault);
1352
+ if (!legacyPath) {
1353
+ return;
1354
+ }
1355
+ throw new CnosSecurityError(
1356
+ `Legacy CNOS local vault format detected for vault "${vault}" at ${legacyPath}. CNOS 1.4 requires the new keystore format. Remove and recreate the vault.`
1357
+ );
1358
+ }
1359
+ function encryptPayload(payload, key) {
1360
+ const iv = (0, import_node_crypto.randomBytes)(IV_LENGTH);
1361
+ const cipher = (0, import_node_crypto.createCipheriv)("aes-256-gcm", key, iv);
1362
+ const plaintext = Buffer.from(JSON.stringify(payload), "utf8");
1363
+ const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
1364
+ const tag = cipher.getAuthTag();
1365
+ return Buffer.concat([
1366
+ Buffer.from(Uint32Array.of(KEYSTORE_VERSION).buffer),
1367
+ iv,
1368
+ tag,
1369
+ ciphertext
1370
+ ]);
1371
+ }
1372
+ function decryptPayload(buffer, key) {
1373
+ if (buffer.length < 4 + IV_LENGTH + AUTH_TAG_LENGTH) {
1374
+ throw new CnosSecurityError("Invalid CNOS local vault keystore");
1375
+ }
1376
+ const version = buffer.readUInt32LE(0);
1377
+ if (version !== KEYSTORE_VERSION) {
1378
+ throw new CnosSecurityError(`Unsupported CNOS local vault keystore version: ${version}`);
1379
+ }
1380
+ const ivOffset = 4;
1381
+ const tagOffset = ivOffset + IV_LENGTH;
1382
+ const cipherOffset = tagOffset + AUTH_TAG_LENGTH;
1383
+ const iv = buffer.subarray(ivOffset, tagOffset);
1384
+ const tag = buffer.subarray(tagOffset, cipherOffset);
1385
+ const ciphertext = buffer.subarray(cipherOffset);
1386
+ const decipher = (0, import_node_crypto.createDecipheriv)("aes-256-gcm", key, iv);
1387
+ decipher.setAuthTag(tag);
1388
+ try {
1389
+ const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
1390
+ const payload = JSON.parse(plaintext);
1391
+ if (!payload || !isObject(payload.secrets) || !isObject(payload.metadata)) {
1392
+ throw new Error("invalid");
1393
+ }
1394
+ return {
1395
+ secrets: Object.fromEntries(
1396
+ Object.entries(payload.secrets).filter((entry) => typeof entry[1] === "string")
1397
+ ),
1398
+ metadata: Object.fromEntries(
1399
+ Object.entries(payload.metadata).filter(
1400
+ (entry) => isObject(entry[1]) && typeof entry[1].createdAt === "string" && typeof entry[1].updatedAt === "string"
1401
+ )
1402
+ )
1403
+ };
1404
+ } catch {
1405
+ throw new CnosAuthenticationError("Failed to decrypt CNOS local vault. Check vault authentication.");
1406
+ }
1407
+ }
1408
+ function buildInitialPayload() {
1409
+ return {
1410
+ secrets: {},
1411
+ metadata: {}
1412
+ };
1413
+ }
1414
+ async function writeVaultFiles(storeRoot, vault, meta, payload, key) {
1415
+ const metaPath = buildMetaPath(storeRoot, vault);
1416
+ const keystorePath = buildKeystorePath(storeRoot, vault);
1417
+ await (0, import_promises7.mkdir)(import_node_path7.default.dirname(metaPath), { recursive: true });
1418
+ await (0, import_promises7.writeFile)(metaPath, stringifyYaml(meta), "utf8");
1419
+ await (0, import_promises7.writeFile)(keystorePath, encryptPayload(payload, key));
1420
+ }
1421
+ async function readVaultMetadata(storeRoot, vault = "default") {
1422
+ await assertNoLegacyVaultFormat(storeRoot, vault);
1423
+ const metaPath = buildMetaPath(storeRoot, vault);
1424
+ try {
1425
+ const source = await (0, import_promises7.readFile)(metaPath, "utf8");
1426
+ return assertVaultMetadata(parseYaml(source), metaPath);
1427
+ } catch (error) {
1428
+ if (error.code === "ENOENT") {
1429
+ return void 0;
1430
+ }
1431
+ throw error;
1432
+ }
1433
+ }
1434
+ async function createSecretVault(storeRoot, vault, passphrase) {
1435
+ const normalizedVault = vault.trim() || "default";
1436
+ await assertNoLegacyVaultFormat(storeRoot, normalizedVault);
1437
+ const salt = (0, import_node_crypto.randomBytes)(SALT_LENGTH);
1438
+ const key = deriveVaultKey(passphrase, salt, PBKDF2_ITERATIONS);
1439
+ const createdAt = (/* @__PURE__ */ new Date()).toISOString();
1440
+ const meta = {
1441
+ version: METADATA_VERSION,
1442
+ algorithm: "aes-256-gcm",
1443
+ kdf: "pbkdf2-sha512",
1444
+ iterations: PBKDF2_ITERATIONS,
1445
+ salt: salt.toString("base64"),
1446
+ createdAt,
1447
+ secretCount: 0
1448
+ };
1449
+ await writeVaultFiles(storeRoot, normalizedVault, meta, buildInitialPayload(), key);
1450
+ return buildMetaPath(storeRoot, normalizedVault);
1451
+ }
1452
+ async function ensureSecretVault(storeRoot, vault, passphrase) {
1453
+ const normalizedVault = vault.trim() || "default";
1454
+ const meta = await readVaultMetadata(storeRoot, normalizedVault);
1455
+ if (meta) {
1456
+ return buildMetaPath(storeRoot, normalizedVault);
1457
+ }
1458
+ return createSecretVault(storeRoot, normalizedVault, passphrase);
1459
+ }
1460
+ function resolveConfiguredVaultPassphrase(definition, vault = "default", processEnv = process.env) {
1461
+ if (definition?.provider !== "local") {
1462
+ return void 0;
1463
+ }
1464
+ const configuredSources = definition.auth?.passphrase?.from ?? [];
1465
+ for (const source of configuredSources) {
1466
+ if (source.startsWith("env:")) {
1467
+ const value = processEnv[source.slice(4)];
1468
+ if (value) {
1469
+ return value;
1470
+ }
1471
+ }
1472
+ }
1473
+ return resolveSecretPassphrase(vault, processEnv);
1474
+ }
1475
+ async function resolveVaultAccessKey(storeRoot, definition, vault = "default", processEnv = process.env) {
1476
+ if (definition?.provider !== "local") {
1477
+ return definition?.provider === "github-secrets" ? {
1478
+ method: definition.auth?.method ?? "environment",
1479
+ ...definition?.auth?.config ? { config: definition.auth.config } : {}
1480
+ } : void 0;
1481
+ }
1482
+ const sessionKey = await resolveVaultSessionKey(vault, processEnv);
1483
+ if (sessionKey) {
1484
+ return {
1485
+ derivedKey: sessionKey,
1486
+ method: "keychain",
1487
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1488
+ };
1489
+ }
1490
+ const passphrase = resolveConfiguredVaultPassphrase(definition, vault, processEnv);
1491
+ if (passphrase) {
1492
+ return {
1493
+ passphrase,
1494
+ method: "passphrase",
1495
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1496
+ };
1497
+ }
1498
+ const metadata = await readVaultMetadata(storeRoot, vault);
1499
+ if (!metadata) {
1500
+ return void 0;
1501
+ }
1502
+ throw new CnosAuthenticationError(
1503
+ `Cannot authenticate to vault "${vault}". Set ${getVaultPassphraseEnvVar(vault)} or run cnos vault auth ${vault}.`
1504
+ );
1505
+ }
1506
+ async function loadVaultPayload(storeRoot, vault, auth) {
1507
+ const meta = await readVaultMetadata(storeRoot, vault);
1508
+ if (!meta) {
1509
+ throw new CnosManifestError(`Missing CNOS vault metadata for "${vault}"`);
1510
+ }
1511
+ const salt = Buffer.from(meta.salt, "base64");
1512
+ const key = auth.derivedKey ?? (auth.passphrase ? deriveVaultKey(auth.passphrase, salt, meta.iterations) : void 0);
1513
+ if (!key) {
1514
+ throw new CnosAuthenticationError(`Vault "${vault}" requires authentication before access.`);
1515
+ }
1516
+ const buffer = await (0, import_promises7.readFile)(buildKeystorePath(storeRoot, vault));
1517
+ return {
1518
+ meta,
1519
+ payload: decryptPayload(buffer, key),
1520
+ key
1521
+ };
1522
+ }
1523
+ async function writeLocalSecret(storeRoot, ref, value, authOrPassphrase, vault = "default") {
1524
+ const auth = typeof authOrPassphrase === "string" ? {
1525
+ passphrase: authOrPassphrase,
1526
+ method: "passphrase"
1527
+ } : authOrPassphrase;
1528
+ if (auth.passphrase) {
1529
+ await ensureSecretVault(storeRoot, vault, auth.passphrase);
1530
+ } else {
1531
+ const meta2 = await readVaultMetadata(storeRoot, vault);
1532
+ if (!meta2) {
1533
+ throw new CnosAuthenticationError(`Vault "${vault}" requires passphrase-based authentication for initial creation.`);
1534
+ }
1535
+ }
1536
+ const { meta, payload, key } = await loadVaultPayload(storeRoot, vault, auth);
1537
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1538
+ const existing = payload.metadata[ref];
1539
+ payload.secrets[ref] = value;
1540
+ payload.metadata[ref] = {
1541
+ createdAt: existing?.createdAt ?? now,
1542
+ updatedAt: now
1543
+ };
1544
+ const nextMeta = {
1545
+ ...meta,
1546
+ secretCount: Object.keys(payload.secrets).length
1547
+ };
1548
+ await writeVaultFiles(storeRoot, vault, nextMeta, payload, key);
1549
+ return buildKeystorePath(storeRoot, vault);
1550
+ }
1551
+ async function deleteLocalSecret(storeRoot, ref, auth, vault = "default") {
1552
+ const { meta, payload, key } = await loadVaultPayload(storeRoot, vault, auth);
1553
+ if (!(ref in payload.secrets)) {
1554
+ return false;
1555
+ }
1556
+ delete payload.secrets[ref];
1557
+ delete payload.metadata[ref];
1558
+ const nextMeta = {
1559
+ ...meta,
1560
+ secretCount: Object.keys(payload.secrets).length
1561
+ };
1562
+ await writeVaultFiles(storeRoot, vault, nextMeta, payload, key);
1563
+ return true;
1564
+ }
1565
+ async function readLocalSecret(storeRoot, ref, auth, vault = "default") {
1566
+ const { payload } = await loadVaultPayload(storeRoot, vault, auth);
1567
+ const value = payload.secrets[ref];
1568
+ if (value === void 0) {
1569
+ throw new CnosManifestError(`Missing local secret ref "${ref}" in vault "${vault}"`);
1570
+ }
1571
+ return value;
1572
+ }
1573
+ async function listLocalSecrets(storeRoot, auth, vault = "default") {
1574
+ const { payload } = await loadVaultPayload(storeRoot, vault, auth);
1575
+ return Object.keys(payload.secrets).sort((left, right) => left.localeCompare(right));
1576
+ }
1577
+ function resolveVaultDefinition(vaults, vault = "default") {
1578
+ const definition = vaults?.[vault];
1579
+ const provider = definition?.provider ?? "local";
1580
+ return {
1581
+ name: vault,
1582
+ provider,
1583
+ ...definition?.auth ? { auth: definition.auth } : {},
1584
+ ...definition?.mapping ? { mapping: definition.mapping } : {},
1585
+ requiresAuthentication: provider === "local"
1586
+ };
1587
+ }
1588
+
1589
+ // ../core/src/secrets/auditLog.ts
1590
+ async function appendAuditEvent(event, processEnv = process.env) {
1591
+ const auditFile = processEnv.CNOS_AUDIT_FILE ?? import_node_path8.default.join(resolveSecretStoreRoot(processEnv), "audit", "access.log");
1592
+ await (0, import_promises8.mkdir)(import_node_path8.default.dirname(auditFile), { recursive: true });
1593
+ await (0, import_promises8.appendFile)(
1594
+ auditFile,
1595
+ `${JSON.stringify({
1596
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
1597
+ ...event
1598
+ })}
1599
+ `,
1600
+ "utf8"
1601
+ );
1602
+ }
1603
+
1604
+ // ../core/src/secrets/secretCache.ts
1605
+ var SecretCache = class {
1606
+ cache = /* @__PURE__ */ new Map();
1607
+ authenticated = /* @__PURE__ */ new Set();
1608
+ load(vaultId, secrets) {
1609
+ this.authenticated.add(vaultId);
1610
+ for (const [ref, value] of secrets) {
1611
+ this.cache.set(`${vaultId}:${ref}`, value);
1612
+ }
1613
+ }
1614
+ isVaultAuthenticated(vaultId) {
1615
+ return this.authenticated.has(vaultId);
1616
+ }
1617
+ get(vaultId, ref) {
1618
+ return this.cache.get(`${vaultId}:${ref}`);
1619
+ }
1620
+ clear(vaultId) {
1621
+ if (!vaultId) {
1622
+ this.cache.clear();
1623
+ this.authenticated.clear();
1624
+ return;
1625
+ }
1626
+ this.authenticated.delete(vaultId);
1627
+ for (const key of Array.from(this.cache.keys())) {
1628
+ if (key.startsWith(`${vaultId}:`)) {
1629
+ this.cache.delete(key);
1630
+ }
1631
+ }
1632
+ }
1633
+ };
1634
+
1635
+ // ../core/src/secrets/providers/github.ts
1636
+ var GithubSecretsVaultProvider = class {
1637
+ constructor(vaultId, definition, processEnv = process.env) {
1638
+ this.vaultId = vaultId;
1639
+ this.definition = definition;
1640
+ this.processEnv = processEnv;
1641
+ }
1642
+ vaultId;
1643
+ definition;
1644
+ processEnv;
1645
+ authenticated = false;
1646
+ async authenticate(_authConfig) {
1647
+ void _authConfig;
1648
+ this.authenticated = true;
1649
+ }
1650
+ isAuthenticated() {
1651
+ return this.authenticated;
1652
+ }
1653
+ resolveEnvVar(ref) {
1654
+ if (this.processEnv[ref] !== void 0) {
1655
+ return ref;
1656
+ }
1657
+ return Object.entries(this.definition.mapping ?? {}).find(([, logicalRef]) => logicalRef === ref)?.[0];
1658
+ }
1659
+ async batchGet(refs) {
1660
+ this.authenticated = true;
1661
+ const resolved = /* @__PURE__ */ new Map();
1662
+ for (const ref of Array.from(new Set(refs)).sort((left, right) => left.localeCompare(right))) {
1663
+ const envVar = this.resolveEnvVar(ref);
1664
+ const value = envVar ? this.processEnv[envVar] : void 0;
1665
+ if (value !== void 0) {
1666
+ resolved.set(ref, value);
1667
+ }
1668
+ }
1669
+ return resolved;
1670
+ }
1671
+ async get(ref) {
1672
+ const envVar = this.resolveEnvVar(ref);
1673
+ this.authenticated = true;
1674
+ return envVar ? this.processEnv[envVar] : void 0;
1675
+ }
1676
+ async set(ref, value) {
1677
+ void ref;
1678
+ void value;
1679
+ throw new Error(`Vault "${this.vaultId}" is environment-backed and cannot be written by CNOS.`);
1680
+ }
1681
+ async delete(ref) {
1682
+ void ref;
1683
+ throw new Error(`Vault "${this.vaultId}" is environment-backed and cannot be mutated by CNOS.`);
1684
+ }
1685
+ async list() {
1686
+ return Object.values(this.definition.mapping ?? {}).sort((left, right) => left.localeCompare(right));
1687
+ }
1688
+ };
1689
+
1690
+ // ../core/src/secrets/providers/local.ts
1691
+ var LocalSecretVaultProvider = class _LocalSecretVaultProvider {
1692
+ constructor(vaultId, definition, processEnv = process.env, storeRoot = resolveSecretStoreRoot(processEnv)) {
1693
+ this.vaultId = vaultId;
1694
+ this.processEnv = processEnv;
1695
+ this.storeRoot = storeRoot;
1696
+ this.definition = definition;
1697
+ }
1698
+ vaultId;
1699
+ processEnv;
1700
+ storeRoot;
1701
+ authConfig;
1702
+ definition;
1703
+ static fromVaults(vaults, vaultId, processEnv) {
1704
+ const definition = resolveVaultDefinition(vaults, vaultId);
1705
+ return new _LocalSecretVaultProvider(vaultId, definition, processEnv);
1706
+ }
1707
+ async authenticate(authConfig) {
1708
+ this.authConfig = authConfig;
1709
+ await this.list();
1710
+ }
1711
+ isAuthenticated() {
1712
+ return Boolean(this.authConfig);
1713
+ }
1714
+ async requireAuth() {
1715
+ if (this.authConfig) {
1716
+ return this.authConfig;
1717
+ }
1718
+ const resolved = await resolveVaultAccessKey(this.storeRoot, this.definition, this.vaultId, this.processEnv);
1719
+ if (!resolved) {
1720
+ throw new CnosAuthenticationError(
1721
+ `Cannot authenticate to vault "${this.vaultId}". Set the configured passphrase env var or run cnos vault auth ${this.vaultId}.`
1722
+ );
1723
+ }
1724
+ this.authConfig = resolved;
1725
+ return resolved;
1726
+ }
1727
+ async batchGet(refs) {
1728
+ const auth = await this.requireAuth();
1729
+ const entries = await Promise.all(
1730
+ Array.from(new Set(refs)).sort((left, right) => left.localeCompare(right)).map(async (ref) => [ref, await readLocalSecret(this.storeRoot, ref, auth, this.vaultId)])
1731
+ );
1732
+ return new Map(entries);
1733
+ }
1734
+ async get(ref) {
1735
+ const auth = await this.requireAuth();
1736
+ try {
1737
+ return await readLocalSecret(this.storeRoot, ref, auth, this.vaultId);
1738
+ } catch {
1739
+ return void 0;
1740
+ }
1741
+ }
1742
+ async set(ref, value) {
1743
+ const auth = await this.requireAuth();
1744
+ await writeLocalSecret(this.storeRoot, ref, value, auth, this.vaultId);
1745
+ await appendAuditEvent(
1746
+ {
1747
+ action: "write",
1748
+ vault: this.vaultId,
1749
+ ref,
1750
+ caller: "cli"
1751
+ },
1752
+ this.processEnv
1753
+ );
1754
+ }
1755
+ async delete(ref) {
1756
+ const auth = await this.requireAuth();
1757
+ await deleteLocalSecret(this.storeRoot, ref, auth, this.vaultId);
1758
+ await appendAuditEvent(
1759
+ {
1760
+ action: "delete",
1761
+ vault: this.vaultId,
1762
+ ref,
1763
+ caller: "cli"
1764
+ },
1765
+ this.processEnv
1766
+ );
1767
+ }
1768
+ async list() {
1769
+ const auth = this.authConfig ?? await resolveVaultAccessKey(this.storeRoot, this.definition, this.vaultId, this.processEnv);
1770
+ if (!auth) {
1771
+ throw new CnosAuthenticationError(
1772
+ `Cannot authenticate to vault "${this.vaultId}". Set the configured passphrase env var or run cnos vault auth ${this.vaultId}.`
1773
+ );
1774
+ }
1775
+ this.authConfig = auth;
1776
+ return listLocalSecrets(this.storeRoot, auth, this.vaultId);
1777
+ }
1778
+ };
1779
+
1780
+ // ../core/src/secrets/providers/registry.ts
1781
+ function createSecretVaultProvider(vaultId, definition, processEnv) {
1782
+ if (definition.provider === "local") {
1783
+ return new LocalSecretVaultProvider(vaultId, definition, processEnv);
1784
+ }
1785
+ if (definition.provider === "github-secrets") {
1786
+ return new GithubSecretsVaultProvider(vaultId, definition, processEnv);
1787
+ }
1788
+ throw new CnosManifestError(`Unsupported vault provider: ${definition.provider}`);
1789
+ }
1790
+
1791
+ // ../core/src/secrets/prompt.ts
1792
+ var import_node_readline = __toESM(require("readline"), 1);
1793
+ var import_node_stream = require("stream");
1794
+ async function promptHidden(message) {
1795
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
1796
+ return void 0;
1797
+ }
1798
+ const mutableStdout = new WritableMask();
1799
+ const rl = import_node_readline.default.createInterface({
1800
+ input: process.stdin,
1801
+ output: mutableStdout,
1802
+ terminal: true
1803
+ });
1804
+ try {
1805
+ mutableStdout.muted = true;
1806
+ const value = await new Promise((resolve) => {
1807
+ rl.question(message, resolve);
1808
+ });
1809
+ process.stdout.write("\n");
1810
+ return value;
1811
+ } finally {
1812
+ rl.close();
1813
+ }
1814
+ }
1815
+ var WritableMask = class extends import_node_stream.Writable {
1816
+ muted = false;
1817
+ _write(chunk, _encoding, callback) {
1818
+ if (!this.muted) {
1819
+ process.stdout.write(chunk);
1820
+ }
1821
+ callback();
1822
+ }
1823
+ };
1824
+
1825
+ // ../core/src/secrets/resolveAuth.ts
1826
+ function toAuthError(vaultId, sources) {
1827
+ return new CnosAuthenticationError(
1828
+ `Cannot authenticate to vault "${vaultId}". Tried: ${sources.join(", ")}. Set ${getVaultPassphraseEnvVar(vaultId)} or run cnos vault auth ${vaultId}.`
1829
+ );
1830
+ }
1831
+ async function resolveVaultAuth(vaultId, definition, processEnv = process.env) {
1832
+ const sessionKey = await resolveVaultSessionKey(vaultId, processEnv);
1833
+ if (sessionKey) {
1834
+ return {
1835
+ derivedKey: sessionKey,
1836
+ method: "keychain",
1837
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1838
+ };
1839
+ }
1840
+ if (definition.provider === "github-secrets") {
1841
+ return {
1842
+ method: definition.auth?.method ?? "environment",
1843
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1844
+ };
1845
+ }
1846
+ const sources = definition.auth?.passphrase?.from ?? [getVaultPassphraseEnvVar(vaultId)];
1847
+ for (const source of sources) {
1848
+ if (source.startsWith("env:")) {
1849
+ const value = processEnv[source.slice(4)];
1850
+ if (value) {
1851
+ return {
1852
+ passphrase: value,
1853
+ method: "passphrase",
1854
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1855
+ };
1856
+ }
1857
+ }
1858
+ if (source.startsWith("keychain:")) {
1859
+ const value = await readKeychain(source.slice("keychain:".length));
1860
+ if (value) {
1861
+ return {
1862
+ derivedKey: Buffer.from(value, "hex"),
1863
+ method: "keychain",
1864
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1865
+ };
1866
+ }
1867
+ }
1868
+ if (source === "prompt") {
1869
+ const value = await promptHidden(`Enter passphrase for vault "${vaultId}": `);
1870
+ if (value) {
1871
+ return {
1872
+ passphrase: value,
1873
+ method: "passphrase",
1874
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1875
+ };
1876
+ }
1877
+ }
1878
+ }
1879
+ const fallback = resolveSecretPassphrase(vaultId, processEnv);
1880
+ if (fallback) {
1881
+ return {
1882
+ passphrase: fallback,
1883
+ method: "passphrase",
1884
+ ...definition.auth?.config ? { config: definition.auth.config } : {}
1885
+ };
1886
+ }
1887
+ throw toAuthError(vaultId, [getVaultSessionKeyEnvVar(vaultId), ...sources]);
1888
+ }
1889
+
1890
+ // ../core/src/secrets/batchResolve.ts
1891
+ function collectSecretDescriptors(graph) {
1892
+ return Array.from(graph.entries.values()).filter((entry) => entry.namespace === "secret" && isSecretReference(entry.value)).map((entry) => ({
1893
+ logicalKey: entry.key,
1894
+ ref: entry.value
1895
+ }));
1896
+ }
1897
+ async function batchResolveSecrets(graph, manifest, processEnv = process.env) {
1898
+ const cache = new SecretCache();
1899
+ const descriptors = collectSecretDescriptors(graph);
1900
+ const grouped = descriptors.reduce((accumulator, descriptor) => {
1901
+ const vaultId = descriptor.ref.vault ?? "default";
1902
+ const bucket = accumulator.get(vaultId) ?? [];
1903
+ bucket.push(descriptor);
1904
+ accumulator.set(vaultId, bucket);
1905
+ return accumulator;
1906
+ }, /* @__PURE__ */ new Map());
1907
+ for (const [vaultId, refs] of grouped) {
1908
+ const definition = manifest.vaults[vaultId] ?? { provider: "local", auth: { passphrase: { from: [] } } };
1909
+ const provider = createSecretVaultProvider(vaultId, definition, processEnv);
1910
+ const auth = await resolveVaultAuth(vaultId, definition, processEnv);
1911
+ await provider.authenticate(auth);
1912
+ const resolved = await provider.batchGet(refs.map((entry) => entry.ref.ref));
1913
+ cache.load(vaultId, resolved);
1914
+ await appendAuditEvent(
1915
+ {
1916
+ action: "batch_read",
1917
+ vault: vaultId,
1918
+ refs: Array.from(resolved.keys()).sort((left, right) => left.localeCompare(right)),
1919
+ caller: "runtime",
1920
+ workspace: graph.workspace.workspaceId,
1921
+ profile: graph.profile
1922
+ },
1923
+ processEnv
1924
+ );
1925
+ }
1926
+ return cache;
1927
+ }
1928
+ function resolveSecretEntryValue(key, value, cache) {
1929
+ if (!key.startsWith("secret.") || !isSecretReference(value)) {
1930
+ return value;
1931
+ }
1932
+ const vaultId = value.vault ?? "default";
1933
+ return cache.get(vaultId, value.ref) ?? value;
1934
+ }
1935
+
1100
1936
  // ../core/src/runtime/projection.ts
1101
1937
  function setNestedValue(target, pathSegments, value) {
1102
1938
  const [head, ...tail] = pathSegments;
@@ -1147,55 +1983,6 @@ function requireValue(graph, key) {
1147
1983
  return value;
1148
1984
  }
1149
1985
 
1150
- // ../core/src/utils/secretStore.ts
1151
- var import_node_crypto = require("crypto");
1152
- var import_promises6 = require("fs/promises");
1153
- var import_node_path6 = __toESM(require("path"), 1);
1154
- function isObject(value) {
1155
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1156
- }
1157
- function isSecretReference(value) {
1158
- 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));
1159
- }
1160
- function resolveSecretStoreRoot(processEnv = process.env) {
1161
- return import_node_path6.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets"));
1162
- }
1163
- function resolveSecretStoreFile(storeRoot, ref, vault = "default") {
1164
- return import_node_path6.default.join(storeRoot, "vaults", vault, "store", ...ref.split("/")).concat(".json");
1165
- }
1166
- function deriveKey(passphrase, salt) {
1167
- return (0, import_node_crypto.scryptSync)(passphrase, salt, 32);
1168
- }
1169
- function resolveSecretPassphrase(vault = "default", processEnv = process.env) {
1170
- const vaultToken = vault.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
1171
- return processEnv[`CNOS_SECRET_PASSPHRASE_${vaultToken}`] ?? processEnv.CNOS_SECRET_PASSPHRASE;
1172
- }
1173
- function decryptDocument(document, passphrase) {
1174
- const salt = Buffer.from(document.salt, "base64");
1175
- const iv = Buffer.from(document.iv, "base64");
1176
- const tag = Buffer.from(document.tag, "base64");
1177
- const ciphertext = Buffer.from(document.ciphertext, "base64");
1178
- const key = deriveKey(passphrase, salt);
1179
- const decipher = (0, import_node_crypto.createDecipheriv)("aes-256-gcm", key, iv);
1180
- decipher.setAuthTag(tag);
1181
- const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
1182
- return plaintext.toString("utf8");
1183
- }
1184
- async function readLocalSecret(storeRoot, ref, passphrase, vault = "default") {
1185
- if (!passphrase) {
1186
- throw new CnosManifestError(
1187
- `Missing CNOS secret passphrase for local secret ref "${ref}". Set CNOS_SECRET_PASSPHRASE or pass processEnv explicitly.`
1188
- );
1189
- }
1190
- const filePath = resolveSecretStoreFile(storeRoot, ref, vault);
1191
- const source = await (0, import_promises6.readFile)(filePath, "utf8");
1192
- const document = JSON.parse(source);
1193
- 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") {
1194
- throw new CnosManifestError("Invalid local secret document", filePath);
1195
- }
1196
- return decryptDocument(document, passphrase);
1197
- }
1198
-
1199
1986
  // ../core/src/runtime/toEnv.ts
1200
1987
  function normalizeEnvValue(value) {
1201
1988
  if (value === void 0 || value === null) {
@@ -1277,28 +2064,42 @@ function toPublicEnv(graph, manifest, options = {}) {
1277
2064
  }
1278
2065
 
1279
2066
  // ../core/src/orchestrator/runtime.ts
1280
- function createRuntime(manifest, graph, plugins = []) {
2067
+ function createRuntime(manifest, graph, plugins = [], secretCache) {
2068
+ function readLogicalKey(key) {
2069
+ const entry = graph.entries.get(key);
2070
+ if (!entry) {
2071
+ return void 0;
2072
+ }
2073
+ if (!secretCache) {
2074
+ return entry.value;
2075
+ }
2076
+ return resolveSecretEntryValue(key, entry.value, secretCache);
2077
+ }
1281
2078
  return {
1282
2079
  manifest,
1283
2080
  plugins,
1284
2081
  graph,
1285
2082
  read(key) {
1286
- return readValue(graph, key);
2083
+ return readLogicalKey(key);
1287
2084
  },
1288
2085
  require(key) {
1289
- return requireValue(graph, key);
2086
+ const value = readLogicalKey(key);
2087
+ if (value === void 0) {
2088
+ return requireValue(graph, key);
2089
+ }
2090
+ return value;
1290
2091
  },
1291
2092
  readOr(key, fallback) {
1292
2093
  return readOrValue(graph, key, fallback);
1293
2094
  },
1294
- value(path10) {
1295
- return readValue(graph, toLogicalKey("value", path10));
2095
+ value(path12) {
2096
+ return readLogicalKey(toLogicalKey("value", path12));
1296
2097
  },
1297
- secret(path10) {
1298
- return readValue(graph, toLogicalKey("secret", path10));
2098
+ secret(path12) {
2099
+ return readLogicalKey(toLogicalKey("secret", path12));
1299
2100
  },
1300
- meta(path10) {
1301
- return readValue(graph, toLogicalKey("meta", path10));
2101
+ meta(path12) {
2102
+ return readLogicalKey(toLogicalKey("meta", path12));
1302
2103
  },
1303
2104
  inspect(key) {
1304
2105
  return inspectValue(graph, key);
@@ -1456,19 +2257,21 @@ async function createCnos(options = {}) {
1456
2257
  });
1457
2258
  const schemaApplied = applySchemaRules(graph, loadedManifest.manifest.schema);
1458
2259
  const promotedGraph = promoteToPublic(schemaApplied.graph, loadedManifest.manifest);
2260
+ const secretCache = options.secretResolution === "lazy" ? void 0 : await batchResolveSecrets(promotedGraph, loadedManifest.manifest, options.processEnv);
1459
2261
  return createRuntime(
1460
2262
  loadedManifest.manifest,
1461
2263
  appendMetaEntries({
1462
2264
  ...promotedGraph,
1463
2265
  profileSource: activeProfile.source
1464
2266
  }, options.cnosVersion),
1465
- plugins
2267
+ plugins,
2268
+ secretCache
1466
2269
  );
1467
2270
  }
1468
2271
 
1469
2272
  // ../core/src/runtime/dump.ts
1470
- var import_promises7 = require("fs/promises");
1471
- var import_node_path7 = __toESM(require("path"), 1);
2273
+ var import_promises9 = require("fs/promises");
2274
+ var import_node_path9 = __toESM(require("path"), 1);
1472
2275
 
1473
2276
  // ../core/src/utils/envNaming.ts
1474
2277
  function normalizeMappingConfig(config = {}) {
@@ -1477,8 +2280,8 @@ function normalizeMappingConfig(config = {}) {
1477
2280
  explicit: config.explicit ?? {}
1478
2281
  };
1479
2282
  }
1480
- function fromScreamingSnake(path10) {
1481
- return path10.split("_").map((segment) => segment.trim().toLowerCase()).filter(Boolean).join(".");
2283
+ function fromScreamingSnake(path12) {
2284
+ return path12.split("_").map((segment) => segment.trim().toLowerCase()).filter(Boolean).join(".");
1482
2285
  }
1483
2286
  function envVarToLogicalKey(envVar, config = {}) {
1484
2287
  const normalized = normalizeMappingConfig(config);
@@ -1505,7 +2308,7 @@ function envVarToLogicalKey(envVar, config = {}) {
1505
2308
  // package.json
1506
2309
  var package_default = {
1507
2310
  name: "@kitsy/cnos",
1508
- version: "1.2.0",
2311
+ version: "1.3.0",
1509
2312
  description: "Batteries-included CNOS runtime package wired with the official plugins.",
1510
2313
  type: "module",
1511
2314
  main: "./dist/index.cjs",
@@ -1517,6 +2320,16 @@ var package_default = {
1517
2320
  import: "./dist/index.js",
1518
2321
  require: "./dist/index.cjs"
1519
2322
  },
2323
+ "./configure": {
2324
+ types: "./dist/configure/index.d.ts",
2325
+ import: "./dist/configure/index.js",
2326
+ require: "./dist/configure/index.cjs"
2327
+ },
2328
+ "./create": {
2329
+ types: "./dist/configure/index.d.ts",
2330
+ import: "./dist/configure/index.js",
2331
+ require: "./dist/configure/index.cjs"
2332
+ },
1520
2333
  "./internal": {
1521
2334
  types: "./dist/internal.d.ts",
1522
2335
  import: "./dist/internal.js",
@@ -1694,8 +2507,8 @@ function createCliArgsPlugin() {
1694
2507
  }
1695
2508
 
1696
2509
  // ../../plugins/dotenv/src/index.ts
1697
- var import_promises8 = require("fs/promises");
1698
- var import_node_path8 = __toESM(require("path"), 1);
2510
+ var import_promises10 = require("fs/promises");
2511
+ var import_node_path10 = __toESM(require("path"), 1);
1699
2512
  var DOTENV_PLUGIN_ID = "@kitsy/cnos/plugins/dotenv";
1700
2513
  function parseDoubleQuoted(value) {
1701
2514
  return value.replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
@@ -1752,7 +2565,7 @@ function dotenvEntriesFromObject(values, mapping = {}, originFile, workspaceId =
1752
2565
  }
1753
2566
  async function readIfPresent(filePath) {
1754
2567
  try {
1755
- return await (0, import_promises8.readFile)(filePath, "utf8");
2568
+ return await (0, import_promises10.readFile)(filePath, "utf8");
1756
2569
  } catch {
1757
2570
  return void 0;
1758
2571
  }
@@ -1771,7 +2584,7 @@ function createDotenvPlugin() {
1771
2584
  workspace: workspaceRoot.workspaceId
1772
2585
  });
1773
2586
  for (const fileName of fileNames) {
1774
- const absolutePath = import_node_path8.default.join(envRoot, fileName);
2587
+ const absolutePath = import_node_path10.default.join(envRoot, fileName);
1775
2588
  const document = await readIfPresent(absolutePath);
1776
2589
  if (!document) {
1777
2590
  continue;
@@ -1780,7 +2593,7 @@ function createDotenvPlugin() {
1780
2593
  ...dotenvEntriesFromObject(
1781
2594
  parseDotenv(document),
1782
2595
  config.envMapping,
1783
- toPortablePath(import_node_path8.default.relative(import_node_path8.default.dirname(context.manifestRoot), absolutePath)),
2596
+ toPortablePath(import_node_path10.default.relative(import_node_path10.default.dirname(context.manifestRoot), absolutePath)),
1784
2597
  workspaceRoot.workspaceId
1785
2598
  )
1786
2599
  );
@@ -1818,32 +2631,32 @@ function createPublicEnvExportPlugin() {
1818
2631
  }
1819
2632
 
1820
2633
  // ../../plugins/filesystem/src/filesystemSecretsReader.ts
1821
- var import_promises10 = require("fs/promises");
2634
+ var import_promises12 = require("fs/promises");
1822
2635
 
1823
2636
  // ../../plugins/filesystem/src/helpers.ts
1824
- var import_promises9 = require("fs/promises");
1825
- var import_node_path9 = __toESM(require("path"), 1);
2637
+ var import_promises11 = require("fs/promises");
2638
+ var import_node_path11 = __toESM(require("path"), 1);
1826
2639
  var YAML_EXTENSIONS = /* @__PURE__ */ new Set([".yml", ".yaml"]);
1827
2640
  var FILESYSTEM_PLUGIN_ID = "@kitsy/cnos/plugins/filesystem";
1828
2641
  async function existsDirectory(targetPath) {
1829
2642
  try {
1830
- const stat = await (0, import_promises9.readdir)(targetPath);
1831
- void stat;
2643
+ const stat2 = await (0, import_promises11.readdir)(targetPath);
2644
+ void stat2;
1832
2645
  return true;
1833
2646
  } catch {
1834
2647
  return false;
1835
2648
  }
1836
2649
  }
1837
2650
  async function collectYamlFiles(root) {
1838
- const entries = await (0, import_promises9.readdir)(root, { withFileTypes: true });
2651
+ const entries = await (0, import_promises11.readdir)(root, { withFileTypes: true });
1839
2652
  const results = [];
1840
2653
  for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
1841
- const absolutePath = import_node_path9.default.join(root, entry.name);
2654
+ const absolutePath = import_node_path11.default.join(root, entry.name);
1842
2655
  if (entry.isDirectory()) {
1843
2656
  results.push(...await collectYamlFiles(absolutePath));
1844
2657
  continue;
1845
2658
  }
1846
- if (entry.isFile() && YAML_EXTENSIONS.has(import_node_path9.default.extname(entry.name).toLowerCase())) {
2659
+ if (entry.isFile() && YAML_EXTENSIONS.has(import_node_path11.default.extname(entry.name).toLowerCase())) {
1847
2660
  results.push(absolutePath);
1848
2661
  }
1849
2662
  }
@@ -1851,16 +2664,16 @@ async function collectYamlFiles(root) {
1851
2664
  }
1852
2665
  async function collectFilesystemLayerFiles(manifestRoot, workspaceRoots, sourceRoot, activeLayers) {
1853
2666
  const files = [];
1854
- const repoRoot = import_node_path9.default.dirname(manifestRoot);
2667
+ const repoRoot = import_node_path11.default.dirname(manifestRoot);
1855
2668
  for (const workspaceRoot of workspaceRoots) {
1856
- const resolvedRoot = import_node_path9.default.resolve(workspaceRoot.path, sourceRoot);
2669
+ const resolvedRoot = import_node_path11.default.resolve(workspaceRoot.path, sourceRoot);
1857
2670
  for (const layer of activeLayers) {
1858
- const layerRoot = import_node_path9.default.join(resolvedRoot, layer);
2671
+ const layerRoot = import_node_path11.default.join(resolvedRoot, layer);
1859
2672
  if (!await existsDirectory(layerRoot)) {
1860
2673
  continue;
1861
2674
  }
1862
2675
  for (const absolutePath of await collectYamlFiles(layerRoot)) {
1863
- const relativePath = import_node_path9.default.relative(repoRoot, absolutePath);
2676
+ const relativePath = import_node_path11.default.relative(repoRoot, absolutePath);
1864
2677
  files.push({
1865
2678
  absolutePath,
1866
2679
  relativePath: toPortablePath(relativePath.startsWith("..") ? absolutePath : relativePath),
@@ -1910,31 +2723,6 @@ function yamlObjectToEntries(document, filePath, namespace, sourceId, workspaceI
1910
2723
  }
1911
2724
  }));
1912
2725
  }
1913
- async function resolveSecretValue(value, processEnv) {
1914
- if (!isSecretReference(value)) {
1915
- return value;
1916
- }
1917
- if (value.provider === "local") {
1918
- const passphrase = resolveSecretPassphrase(value.vault, processEnv);
1919
- if (!passphrase) {
1920
- return value;
1921
- }
1922
- return readLocalSecret(
1923
- resolveSecretStoreRoot(processEnv),
1924
- value.ref,
1925
- passphrase,
1926
- value.vault
1927
- );
1928
- }
1929
- if (value.provider === "env" || value.provider === "github-secrets") {
1930
- const resolved = processEnv?.[value.ref];
1931
- if (resolved === void 0) {
1932
- return value;
1933
- }
1934
- return resolved;
1935
- }
1936
- return value;
1937
- }
1938
2726
  function toSecretReferenceMetadata(value) {
1939
2727
  if (!isSecretReference(value)) {
1940
2728
  return void 0;
@@ -1962,14 +2750,12 @@ function createFilesystemSecretsPlugin() {
1962
2750
  );
1963
2751
  const entries = [];
1964
2752
  for (const file of files) {
1965
- const document = await (0, import_promises10.readFile)(file.absolutePath, "utf8");
2753
+ const document = await (0, import_promises12.readFile)(file.absolutePath, "utf8");
1966
2754
  const fileEntries = filesystemSecretsReader(file.relativePath, document, file.workspaceId);
1967
2755
  for (const entry of fileEntries) {
1968
2756
  const metadata = toSecretReferenceMetadata(entry.value);
1969
- const resolvedValue = await resolveSecretValue(entry.value, context.processEnv);
1970
2757
  entries.push({
1971
2758
  ...entry,
1972
- value: resolvedValue,
1973
2759
  ...metadata ? { metadata } : {}
1974
2760
  });
1975
2761
  }
@@ -1980,7 +2766,7 @@ function createFilesystemSecretsPlugin() {
1980
2766
  }
1981
2767
 
1982
2768
  // ../../plugins/filesystem/src/filesystemValuesReader.ts
1983
- var import_promises11 = require("fs/promises");
2769
+ var import_promises13 = require("fs/promises");
1984
2770
  function filesystemValuesReader(filePath, document, workspaceId = "default") {
1985
2771
  return yamlObjectToEntries(document, filePath, "value", "filesystem-values", workspaceId);
1986
2772
  }
@@ -1998,7 +2784,7 @@ function createFilesystemValuesPlugin() {
1998
2784
  );
1999
2785
  const entries = [];
2000
2786
  for (const file of files) {
2001
- const document = await (0, import_promises11.readFile)(file.absolutePath, "utf8");
2787
+ const document = await (0, import_promises13.readFile)(file.absolutePath, "utf8");
2002
2788
  entries.push(...filesystemValuesReader(file.relativePath, document, file.workspaceId));
2003
2789
  }
2004
2790
  return entries;
@@ -2065,12 +2851,14 @@ function defaultPlugins() {
2065
2851
  // src/runtime/state.ts
2066
2852
  var singletonRuntime;
2067
2853
  var singletonReady;
2854
+ var bootstrappedSecretHydrationRequired = false;
2068
2855
  function getSingletonRuntime() {
2069
2856
  return singletonRuntime;
2070
2857
  }
2071
2858
  function setSingletonRuntime(runtime) {
2072
2859
  singletonRuntime = runtime;
2073
2860
  singletonReady = Promise.resolve(runtime);
2861
+ bootstrappedSecretHydrationRequired = false;
2074
2862
  return runtime;
2075
2863
  }
2076
2864
  function getSingletonReady() {
@@ -2080,11 +2868,18 @@ function setSingletonReady(promise) {
2080
2868
  singletonReady = promise;
2081
2869
  return promise;
2082
2870
  }
2871
+ function getBootstrappedSecretHydrationRequired() {
2872
+ return bootstrappedSecretHydrationRequired;
2873
+ }
2874
+ function setBootstrappedSecretHydrationRequired(value) {
2875
+ bootstrappedSecretHydrationRequired = value;
2876
+ }
2083
2877
 
2084
2878
  // src/createCnos.ts
2085
2879
  async function createCnos2(options = {}) {
2086
2880
  const runtime = await createCnos({
2087
2881
  ...options,
2882
+ processEnv: options.processEnv ?? process.env,
2088
2883
  cnosVersion: package_default.version,
2089
2884
  plugins: [...defaultPlugins(), ...options.plugins ?? []]
2090
2885
  });
@@ -2093,7 +2888,10 @@ async function createCnos2(options = {}) {
2093
2888
  }
2094
2889
 
2095
2890
  // src/runtime/bootstrap.ts
2891
+ var import_node_crypto2 = require("crypto");
2096
2892
  var CNOS_GRAPH_ENV_VAR = "__CNOS_GRAPH__";
2893
+ var CNOS_SECRET_PAYLOAD_ENV_VAR = "__CNOS_SECRET_PAYLOAD__";
2894
+ var CNOS_SESSION_KEY_ENV_VAR = "__CNOS_SESSION_KEY__";
2097
2895
  function deserializeRuntimeGraph(source) {
2098
2896
  const payload = JSON.parse(source);
2099
2897
  if (!payload || !Array.isArray(payload.entries) || typeof payload.profile !== "string" || typeof payload.resolvedAt !== "string" || !payload.profileSource || !payload.workspace || typeof payload.workspace.workspaceId !== "string" || !Array.isArray(payload.workspace.workspaceChain) || !Array.isArray(payload.workspace.workspaceRoots)) {
@@ -2118,12 +2916,41 @@ function deserializeRuntimeGraph(source) {
2118
2916
  workspace: payload.workspace
2119
2917
  };
2120
2918
  }
2919
+ function decryptSecretPayload(serialized, sessionKey) {
2920
+ const payload = JSON.parse(serialized);
2921
+ if (!payload || typeof payload.iv !== "string" || typeof payload.tag !== "string" || typeof payload.ciphertext !== "string") {
2922
+ throw new Error("Invalid CNOS secret payload");
2923
+ }
2924
+ const key = Buffer.from(sessionKey, "hex");
2925
+ const iv = Buffer.from(payload.iv, "base64");
2926
+ const tag = Buffer.from(payload.tag, "base64");
2927
+ const ciphertext = Buffer.from(payload.ciphertext, "base64");
2928
+ const decipher = (0, import_node_crypto2.createDecipheriv)("aes-256-gcm", key, iv);
2929
+ decipher.setAuthTag(tag);
2930
+ const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
2931
+ return JSON.parse(plaintext);
2932
+ }
2121
2933
  function readRuntimeGraphFromEnv(processEnv = process.env) {
2122
2934
  const serialized = processEnv[CNOS_GRAPH_ENV_VAR];
2123
2935
  if (!serialized) {
2124
2936
  return void 0;
2125
2937
  }
2126
- return deserializeRuntimeGraph(serialized);
2938
+ const graph = deserializeRuntimeGraph(serialized);
2939
+ const secretPayload = processEnv[CNOS_SECRET_PAYLOAD_ENV_VAR];
2940
+ const sessionKey = processEnv[CNOS_SESSION_KEY_ENV_VAR];
2941
+ if (secretPayload && sessionKey) {
2942
+ const decrypted = decryptSecretPayload(secretPayload, sessionKey);
2943
+ for (const [key, value] of Object.entries(decrypted)) {
2944
+ const entry = graph.entries.get(key);
2945
+ if (entry) {
2946
+ entry.value = value;
2947
+ }
2948
+ }
2949
+ }
2950
+ return graph;
2951
+ }
2952
+ function graphRequiresSecretHydration(graph) {
2953
+ return Array.from(graph.entries.values()).some((entry) => entry.namespace === "secret" && isSecretReference(entry.value));
2127
2954
  }
2128
2955
 
2129
2956
  // src/runtime/index.ts
@@ -2206,14 +3033,14 @@ function attachBootstrappedGraph(graph) {
2206
3033
  readOr(key, fallback) {
2207
3034
  return readOrValue(graph, key, fallback);
2208
3035
  },
2209
- value(path10) {
2210
- return readValue(graph, toLogicalKey("value", path10));
3036
+ value(path12) {
3037
+ return readValue(graph, toLogicalKey("value", path12));
2211
3038
  },
2212
- secret(path10) {
2213
- return readValue(graph, toLogicalKey("secret", path10));
3039
+ secret(path12) {
3040
+ return readValue(graph, toLogicalKey("secret", path12));
2214
3041
  },
2215
- meta(path10) {
2216
- return readValue(graph, toLogicalKey("meta", path10));
3042
+ meta(path12) {
3043
+ return readValue(graph, toLogicalKey("meta", path12));
2217
3044
  },
2218
3045
  inspect(key) {
2219
3046
  return inspectValue(graph, key);
@@ -2232,6 +3059,7 @@ function attachBootstrappedGraph(graph) {
2232
3059
  }
2233
3060
  };
2234
3061
  setSingletonRuntime(runtime);
3062
+ setBootstrappedSecretHydrationRequired(graphRequiresSecretHydration(graph));
2235
3063
  }
2236
3064
  function bootstrapFromProcessEnv() {
2237
3065
  if (typeof process === "undefined") {
@@ -2258,21 +3086,21 @@ var cnos = Object.assign(
2258
3086
  readOr(key, fallback) {
2259
3087
  return readOrValue(getRuntimeOrThrow().graph, key, fallback);
2260
3088
  },
2261
- value(path10) {
2262
- return readValue(getRuntimeOrThrow().graph, toLogicalKey("value", path10));
3089
+ value(path12) {
3090
+ return readValue(getRuntimeOrThrow().graph, toLogicalKey("value", path12));
2263
3091
  },
2264
- secret(path10) {
2265
- return readValue(getRuntimeOrThrow().graph, toLogicalKey("secret", path10));
3092
+ secret(path12) {
3093
+ return readValue(getRuntimeOrThrow().graph, toLogicalKey("secret", path12));
2266
3094
  },
2267
- meta(path10) {
2268
- return readValue(getRuntimeOrThrow().graph, toLogicalKey("meta", path10));
3095
+ meta(path12) {
3096
+ return readValue(getRuntimeOrThrow().graph, toLogicalKey("meta", path12));
2269
3097
  },
2270
3098
  async ready() {
2271
- if (getSingletonRuntime()) {
3099
+ if (getSingletonRuntime() && !getBootstrappedSecretHydrationRequired()) {
2272
3100
  return;
2273
3101
  }
2274
3102
  const existing = getSingletonReady();
2275
- if (existing) {
3103
+ if (existing && !getBootstrappedSecretHydrationRequired()) {
2276
3104
  await existing;
2277
3105
  return;
2278
3106
  }