@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,9 +2851,11 @@ function defaultPlugins() {
2065
2851
  // src/runtime/state.ts
2066
2852
  var singletonRuntime;
2067
2853
  var singletonReady;
2854
+ var bootstrappedSecretHydrationRequired = false;
2068
2855
  function setSingletonRuntime(runtime) {
2069
2856
  singletonRuntime = runtime;
2070
2857
  singletonReady = Promise.resolve(runtime);
2858
+ bootstrappedSecretHydrationRequired = false;
2071
2859
  return runtime;
2072
2860
  }
2073
2861
 
@@ -2075,6 +2863,7 @@ function setSingletonRuntime(runtime) {
2075
2863
  async function createCnos2(options = {}) {
2076
2864
  const runtime = await createCnos({
2077
2865
  ...options,
2866
+ processEnv: options.processEnv ?? process.env,
2078
2867
  cnosVersion: package_default.version,
2079
2868
  plugins: [...defaultPlugins(), ...options.plugins ?? []]
2080
2869
  });