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