@centient/secrets 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/cli/hidden-input.d.ts +52 -0
- package/dist/cli/hidden-input.d.ts.map +1 -0
- package/dist/cli/hidden-input.js +109 -0
- package/dist/cli/hidden-input.js.map +1 -0
- package/dist/cli/secrets-cli.d.ts +7 -0
- package/dist/cli/secrets-cli.d.ts.map +1 -1
- package/dist/cli/secrets-cli.js +234 -161
- package/dist/cli/secrets-cli.js.map +1 -1
- package/dist/crypto/vault-common.d.ts +17 -4
- package/dist/crypto/vault-common.d.ts.map +1 -1
- package/dist/crypto/vault-common.js +23 -6
- package/dist/crypto/vault-common.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/vault/file-lock.d.ts +33 -0
- package/dist/vault/file-lock.d.ts.map +1 -0
- package/dist/vault/file-lock.js +143 -0
- package/dist/vault/file-lock.js.map +1 -0
- package/dist/vault/session-vault-errors.d.ts +38 -0
- package/dist/vault/session-vault-errors.d.ts.map +1 -0
- package/dist/vault/session-vault-errors.js +67 -0
- package/dist/vault/session-vault-errors.js.map +1 -0
- package/dist/vault/session-vault.d.ts +147 -0
- package/dist/vault/session-vault.d.ts.map +1 -0
- package/dist/vault/session-vault.js +667 -0
- package/dist/vault/session-vault.js.map +1 -0
- package/dist/vault/sidecar.d.ts +37 -0
- package/dist/vault/sidecar.d.ts.map +1 -0
- package/dist/vault/sidecar.js +84 -0
- package/dist/vault/sidecar.js.map +1 -0
- package/dist/vault/types.d.ts +1 -1
- package/dist/vault/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -16,23 +16,36 @@ export declare const KEY_LENGTH = 32;
|
|
|
16
16
|
*
|
|
17
17
|
* The returned Buffer layout is:
|
|
18
18
|
* [IV (12 bytes)] [AuthTag (16 bytes)] [Ciphertext (variable)]
|
|
19
|
+
*
|
|
20
|
+
* Optionally accepts `aad` (Additional Authenticated Data) — additional
|
|
21
|
+
* bytes bound into the auth tag but not encrypted. Callers that want to
|
|
22
|
+
* bind a ciphertext to its identity (e.g. vault file path + schema version)
|
|
23
|
+
* should pass a stable `aad`; the same bytes MUST be passed to `decrypt()`
|
|
24
|
+
* or auth-tag verification fails cleanly. When `aad` is omitted, behaviour
|
|
25
|
+
* is unchanged (backward-compatible).
|
|
19
26
|
*/
|
|
20
|
-
export declare function encrypt(plaintext: string, key: Buffer): Buffer;
|
|
27
|
+
export declare function encrypt(plaintext: string, key: Buffer, aad?: Buffer): Buffer;
|
|
21
28
|
/**
|
|
22
29
|
* Decrypt a Buffer produced by `encrypt()`.
|
|
23
30
|
* Returns the plaintext string, or null if decryption fails.
|
|
31
|
+
*
|
|
32
|
+
* If the ciphertext was produced with `aad`, the same bytes must be passed
|
|
33
|
+
* here. Mismatched or missing `aad` fails auth-tag verification and returns
|
|
34
|
+
* null, identical to any other decryption failure. Callers that need to
|
|
35
|
+
* distinguish auth-tag-fail from tampering should inspect context (known
|
|
36
|
+
* key, known AAD) rather than relying on the error shape.
|
|
24
37
|
*/
|
|
25
|
-
export declare function decrypt(data: Buffer, key: Buffer): string | null;
|
|
38
|
+
export declare function decrypt(data: Buffer, key: Buffer, aad?: Buffer): string | null;
|
|
26
39
|
/**
|
|
27
40
|
* Encrypt a JSON-serializable object. Convenience wrapper over `encrypt()`.
|
|
28
41
|
* Returns null if serialization fails (should not happen with plain objects).
|
|
29
42
|
*/
|
|
30
|
-
export declare function encryptObject(data: Record<string, unknown>, key: Buffer): Buffer | null;
|
|
43
|
+
export declare function encryptObject(data: Record<string, unknown>, key: Buffer, aad?: Buffer): Buffer | null;
|
|
31
44
|
/**
|
|
32
45
|
* Decrypt a Buffer to a JSON object. Convenience wrapper over `decrypt()`.
|
|
33
46
|
* Returns null if decryption or parsing fails.
|
|
34
47
|
*/
|
|
35
|
-
export declare function decryptObject(data: Buffer, key: Buffer): Record<string, unknown> | null;
|
|
48
|
+
export declare function decryptObject(data: Buffer, key: Buffer, aad?: Buffer): Record<string, unknown> | null;
|
|
36
49
|
/**
|
|
37
50
|
* Invalidate all cached enumeration results. Called automatically
|
|
38
51
|
* by `storeStringInKeychain` and `deleteFromKeychain` so that
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vault-common.d.ts","sourceRoot":"","sources":["../../src/crypto/vault-common.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,eAAO,MAAM,SAAS,EAAG,aAAsB,CAAC;AAChD,eAAO,MAAM,SAAS,KAAK,CAAC;AAC5B,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,eAAO,MAAM,UAAU,KAAK,CAAC;AAM7B
|
|
1
|
+
{"version":3,"file":"vault-common.d.ts","sourceRoot":"","sources":["../../src/crypto/vault-common.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,eAAO,MAAM,SAAS,EAAG,aAAsB,CAAC;AAChD,eAAO,MAAM,SAAS,KAAK,CAAC;AAC5B,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,eAAO,MAAM,UAAU,KAAK,CAAC;AAM7B;;;;;;;;;;;;GAYG;AACH,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAU5E;AAED;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAiB9E;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,GAAG,EAAE,MAAM,EACX,GAAG,CAAC,EAAE,MAAM,GACX,MAAM,GAAG,IAAI,CAMf;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,GAAG,CAAC,EAAE,MAAM,GACX,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAYhC;AAkBD;;;;GAIG;AACH,wBAAgB,2BAA2B,IAAI,IAAI,CAElD;AAMD;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,MAAM,GAAG,IAAI,CAYf;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,GACV,OAAO,CAsBT;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,GACZ,OAAO,CAsBT;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,MAAM,GAAG,IAAI,CAWf;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,OAAO,CAaT;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM,GACd,MAAM,EAAE,CAsDV"}
|
|
@@ -24,10 +24,19 @@ export const KEY_LENGTH = 32;
|
|
|
24
24
|
*
|
|
25
25
|
* The returned Buffer layout is:
|
|
26
26
|
* [IV (12 bytes)] [AuthTag (16 bytes)] [Ciphertext (variable)]
|
|
27
|
+
*
|
|
28
|
+
* Optionally accepts `aad` (Additional Authenticated Data) — additional
|
|
29
|
+
* bytes bound into the auth tag but not encrypted. Callers that want to
|
|
30
|
+
* bind a ciphertext to its identity (e.g. vault file path + schema version)
|
|
31
|
+
* should pass a stable `aad`; the same bytes MUST be passed to `decrypt()`
|
|
32
|
+
* or auth-tag verification fails cleanly. When `aad` is omitted, behaviour
|
|
33
|
+
* is unchanged (backward-compatible).
|
|
27
34
|
*/
|
|
28
|
-
export function encrypt(plaintext, key) {
|
|
35
|
+
export function encrypt(plaintext, key, aad) {
|
|
29
36
|
const iv = randomBytes(IV_LENGTH);
|
|
30
37
|
const cipher = createCipheriv(ALGORITHM, key, iv);
|
|
38
|
+
if (aad !== undefined)
|
|
39
|
+
cipher.setAAD(aad);
|
|
31
40
|
const encrypted = Buffer.concat([
|
|
32
41
|
cipher.update(plaintext, "utf8"),
|
|
33
42
|
cipher.final(),
|
|
@@ -38,8 +47,14 @@ export function encrypt(plaintext, key) {
|
|
|
38
47
|
/**
|
|
39
48
|
* Decrypt a Buffer produced by `encrypt()`.
|
|
40
49
|
* Returns the plaintext string, or null if decryption fails.
|
|
50
|
+
*
|
|
51
|
+
* If the ciphertext was produced with `aad`, the same bytes must be passed
|
|
52
|
+
* here. Mismatched or missing `aad` fails auth-tag verification and returns
|
|
53
|
+
* null, identical to any other decryption failure. Callers that need to
|
|
54
|
+
* distinguish auth-tag-fail from tampering should inspect context (known
|
|
55
|
+
* key, known AAD) rather than relying on the error shape.
|
|
41
56
|
*/
|
|
42
|
-
export function decrypt(data, key) {
|
|
57
|
+
export function decrypt(data, key, aad) {
|
|
43
58
|
try {
|
|
44
59
|
if (data.length < IV_LENGTH + AUTH_TAG_LENGTH)
|
|
45
60
|
return null;
|
|
@@ -48,6 +63,8 @@ export function decrypt(data, key) {
|
|
|
48
63
|
const ciphertext = data.subarray(IV_LENGTH + AUTH_TAG_LENGTH);
|
|
49
64
|
const decipher = createDecipheriv(ALGORITHM, key, iv);
|
|
50
65
|
decipher.setAuthTag(authTag);
|
|
66
|
+
if (aad !== undefined)
|
|
67
|
+
decipher.setAAD(aad);
|
|
51
68
|
const decrypted = Buffer.concat([
|
|
52
69
|
decipher.update(ciphertext),
|
|
53
70
|
decipher.final(),
|
|
@@ -62,9 +79,9 @@ export function decrypt(data, key) {
|
|
|
62
79
|
* Encrypt a JSON-serializable object. Convenience wrapper over `encrypt()`.
|
|
63
80
|
* Returns null if serialization fails (should not happen with plain objects).
|
|
64
81
|
*/
|
|
65
|
-
export function encryptObject(data, key) {
|
|
82
|
+
export function encryptObject(data, key, aad) {
|
|
66
83
|
try {
|
|
67
|
-
return encrypt(JSON.stringify(data), key);
|
|
84
|
+
return encrypt(JSON.stringify(data), key, aad);
|
|
68
85
|
}
|
|
69
86
|
catch {
|
|
70
87
|
return null;
|
|
@@ -74,8 +91,8 @@ export function encryptObject(data, key) {
|
|
|
74
91
|
* Decrypt a Buffer to a JSON object. Convenience wrapper over `decrypt()`.
|
|
75
92
|
* Returns null if decryption or parsing fails.
|
|
76
93
|
*/
|
|
77
|
-
export function decryptObject(data, key) {
|
|
78
|
-
const plaintext = decrypt(data, key);
|
|
94
|
+
export function decryptObject(data, key, aad) {
|
|
95
|
+
const plaintext = decrypt(data, key, aad);
|
|
79
96
|
if (plaintext === null)
|
|
80
97
|
return null;
|
|
81
98
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vault-common.js","sourceRoot":"","sources":["../../src/crypto/vault-common.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,WAAW,GACZ,MAAM,QAAQ,CAAC;AAChB,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,gFAAgF;AAChF,YAAY;AACZ,gFAAgF;AAEhF,MAAM,CAAC,MAAM,SAAS,GAAG,aAAsB,CAAC;AAChD,MAAM,CAAC,MAAM,SAAS,GAAG,EAAE,CAAC;AAC5B,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAC;AAClC,MAAM,CAAC,MAAM,UAAU,GAAG,EAAE,CAAC;AAE7B,gFAAgF;AAChF,0BAA0B;AAC1B,gFAAgF;AAEhF
|
|
1
|
+
{"version":3,"file":"vault-common.js","sourceRoot":"","sources":["../../src/crypto/vault-common.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -15,4 +15,7 @@ export { resolveKeyProvider, getProviderByType, loadConfig, saveSecretsConfig }
|
|
|
15
15
|
export { isValidKey } from "./vault/vault-utils.js";
|
|
16
16
|
export { setSecretsPolicies, getActivePolicies, auditTrail } from "./vault/policy.js";
|
|
17
17
|
export type { SecretsPolicy, SecretsEvent, SecretsEventType, SecretsOperation, AuditTrailOptions } from "./vault/policy.js";
|
|
18
|
+
export { openVault, VAULT_SCHEMA_VERSION, VAULT_AAD_PREFIX, DEFAULT_VAULT_PATH, DEFAULT_SIDECAR_PATH } from "./vault/session-vault.js";
|
|
19
|
+
export { VaultError, VaultUnlockError, VaultDecryptError, VaultRollbackError, VaultClosedError, VaultLockError, } from "./vault/session-vault.js";
|
|
20
|
+
export type { SessionVault, OpenVaultOptions, CoherenceStrategy } from "./vault/session-vault.js";
|
|
18
21
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC1F,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAG7F,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,gBAAgB,EAAE,eAAe,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACzI,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAGtF,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,gBAAgB,EAAE,eAAe,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3I,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAGhE,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAGhG,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG3D,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAGpJ,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,iBAAiB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/H,OAAO,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AAC9E,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAGlH,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGpD,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACtF,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC1F,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAG7F,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,gBAAgB,EAAE,eAAe,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACzI,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAGtF,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,gBAAgB,EAAE,eAAe,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3I,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAGhE,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAGhG,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG3D,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAGpJ,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,iBAAiB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/H,OAAO,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AAC9E,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAGlH,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGpD,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACtF,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAM5H,OAAO,EAAE,SAAS,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AACvI,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,GACf,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -19,4 +19,10 @@ export { resolveKeyProvider, getProviderByType, loadConfig, saveSecretsConfig }
|
|
|
19
19
|
export { isValidKey } from "./vault/vault-utils.js";
|
|
20
20
|
// Policy
|
|
21
21
|
export { setSecretsPolicies, getActivePolicies, auditTrail } from "./vault/policy.js";
|
|
22
|
+
// Session-backed vault (envelope encryption, single unlock per session)
|
|
23
|
+
// Recommended for long-running processes (daemons) holding N credentials;
|
|
24
|
+
// supersedes per-item `getCredential` calls for those consumers. See
|
|
25
|
+
// packages/secrets/docs/session-vault.md and issue #40 for the threat model.
|
|
26
|
+
export { openVault, VAULT_SCHEMA_VERSION, VAULT_AAD_PREFIX, DEFAULT_VAULT_PATH, DEFAULT_SIDECAR_PATH } from "./vault/session-vault.js";
|
|
27
|
+
export { VaultError, VaultUnlockError, VaultDecryptError, VaultRollbackError, VaultClosedError, VaultLockError, } from "./vault/session-vault.js";
|
|
22
28
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC1F,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAE7F,mBAAmB;AACnB,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,gBAAgB,EAAE,eAAe,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGzI,qBAAqB;AACrB,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,gBAAgB,EAAE,eAAe,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3I,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAEhE,yBAAyB;AACzB,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAEhG,MAAM;AACN,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGlD,2EAA2E;AAC3E,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAIpJ,OAAO,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AAC9E,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAElH,aAAa;AACb,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD,SAAS;AACT,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC1F,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAE7F,mBAAmB;AACnB,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,gBAAgB,EAAE,eAAe,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGzI,qBAAqB;AACrB,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,gBAAgB,EAAE,eAAe,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3I,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAEhE,yBAAyB;AACzB,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAEhG,MAAM;AACN,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGlD,2EAA2E;AAC3E,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAIpJ,OAAO,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AAC9E,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAElH,aAAa;AACb,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD,SAAS;AACT,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAGtF,wEAAwE;AACxE,0EAA0E;AAC1E,qEAAqE;AACrE,6EAA6E;AAC7E,OAAO,EAAE,SAAS,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AACvI,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,GACf,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File lock — native exclusive advisory lock for vault writes.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from session-vault.ts (M3) so session-vault remains focused on
|
|
5
|
+
* orchestration. The lock itself is filesystem-level (O_EXCL create on a
|
|
6
|
+
* `.lock` file) and cooperative: it only protects against writers that call
|
|
7
|
+
* `acquireWriteLock` before mutating.
|
|
8
|
+
*
|
|
9
|
+
* The lock file contains the holding process's PID so stale-lock stealing can
|
|
10
|
+
* distinguish "our PID still owns it" from "the previous holder crashed." On
|
|
11
|
+
* steal we write-and-verify to avoid two racing processes both thinking they
|
|
12
|
+
* stole the same stale lock (M2).
|
|
13
|
+
*/
|
|
14
|
+
/** Max time a writer will wait to acquire the file lock before giving up. */
|
|
15
|
+
export declare const LOCK_TIMEOUT_MS = 5000;
|
|
16
|
+
/** Poll interval when waiting on a held lock. */
|
|
17
|
+
export declare const LOCK_RETRY_INTERVAL_MS = 25;
|
|
18
|
+
/** Stale-lock threshold — if a lock file is older than this, assume crash. */
|
|
19
|
+
export declare const LOCK_STALE_MS = 30000;
|
|
20
|
+
/**
|
|
21
|
+
* Acquire an exclusive write lock via O_EXCL on `{vaultPath}.lock`. Yields
|
|
22
|
+
* the event loop between retries (no busy-spin) up to `LOCK_TIMEOUT_MS`.
|
|
23
|
+
* Locks older than `LOCK_STALE_MS` are considered orphaned (the holding
|
|
24
|
+
* process crashed) and stolen with a pid-verification handshake so two
|
|
25
|
+
* concurrent stealers can't both claim ownership.
|
|
26
|
+
*
|
|
27
|
+
* Returns a release function. Releasing is idempotent and swallows ENOENT
|
|
28
|
+
* (the lock file may have been stolen by another process after we already
|
|
29
|
+
* finished our critical section — that's fine; the lockfile, not the fd,
|
|
30
|
+
* is what matters).
|
|
31
|
+
*/
|
|
32
|
+
export declare function acquireWriteLock(vaultPath: string): Promise<() => void>;
|
|
33
|
+
//# sourceMappingURL=file-lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-lock.d.ts","sourceRoot":"","sources":["../../src/vault/file-lock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAaH,6EAA6E;AAC7E,eAAO,MAAM,eAAe,OAAQ,CAAC;AAErC,iDAAiD;AACjD,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC,8EAA8E;AAC9E,eAAO,MAAM,aAAa,QAAS,CAAC;AAEpC;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAwG7E"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File lock — native exclusive advisory lock for vault writes.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from session-vault.ts (M3) so session-vault remains focused on
|
|
5
|
+
* orchestration. The lock itself is filesystem-level (O_EXCL create on a
|
|
6
|
+
* `.lock` file) and cooperative: it only protects against writers that call
|
|
7
|
+
* `acquireWriteLock` before mutating.
|
|
8
|
+
*
|
|
9
|
+
* The lock file contains the holding process's PID so stale-lock stealing can
|
|
10
|
+
* distinguish "our PID still owns it" from "the previous holder crashed." On
|
|
11
|
+
* steal we write-and-verify to avoid two racing processes both thinking they
|
|
12
|
+
* stole the same stale lock (M2).
|
|
13
|
+
*/
|
|
14
|
+
import { openSync, closeSync, writeFileSync, readFileSync, statSync, unlinkSync, } from "node:fs";
|
|
15
|
+
import { VaultLockError } from "./session-vault-errors.js";
|
|
16
|
+
/** Max time a writer will wait to acquire the file lock before giving up. */
|
|
17
|
+
export const LOCK_TIMEOUT_MS = 5_000;
|
|
18
|
+
/** Poll interval when waiting on a held lock. */
|
|
19
|
+
export const LOCK_RETRY_INTERVAL_MS = 25;
|
|
20
|
+
/** Stale-lock threshold — if a lock file is older than this, assume crash. */
|
|
21
|
+
export const LOCK_STALE_MS = 30_000;
|
|
22
|
+
/**
|
|
23
|
+
* Acquire an exclusive write lock via O_EXCL on `{vaultPath}.lock`. Yields
|
|
24
|
+
* the event loop between retries (no busy-spin) up to `LOCK_TIMEOUT_MS`.
|
|
25
|
+
* Locks older than `LOCK_STALE_MS` are considered orphaned (the holding
|
|
26
|
+
* process crashed) and stolen with a pid-verification handshake so two
|
|
27
|
+
* concurrent stealers can't both claim ownership.
|
|
28
|
+
*
|
|
29
|
+
* Returns a release function. Releasing is idempotent and swallows ENOENT
|
|
30
|
+
* (the lock file may have been stolen by another process after we already
|
|
31
|
+
* finished our critical section — that's fine; the lockfile, not the fd,
|
|
32
|
+
* is what matters).
|
|
33
|
+
*/
|
|
34
|
+
export async function acquireWriteLock(vaultPath) {
|
|
35
|
+
const lockPath = `${vaultPath}.lock`;
|
|
36
|
+
const deadline = Date.now() + LOCK_TIMEOUT_MS;
|
|
37
|
+
const ourPid = process.pid;
|
|
38
|
+
const ourToken = `${ourPid}`;
|
|
39
|
+
while (Date.now() < deadline) {
|
|
40
|
+
// Attempt the happy path: create-exclusive.
|
|
41
|
+
try {
|
|
42
|
+
const fd = openSync(lockPath, "wx");
|
|
43
|
+
try {
|
|
44
|
+
// Write our pid so stale-lock stealing can verify ownership.
|
|
45
|
+
writeFileSync(lockPath, ourToken);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// Best-effort; even if we can't write pid, we still hold the lock.
|
|
49
|
+
}
|
|
50
|
+
// closeSync can throw on exotic filesystems; the lockfile — not the fd —
|
|
51
|
+
// is what matters, so swallow errors here. The release closure unlinks
|
|
52
|
+
// the file regardless (M2).
|
|
53
|
+
try {
|
|
54
|
+
closeSync(fd);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// Non-fatal.
|
|
58
|
+
}
|
|
59
|
+
return () => {
|
|
60
|
+
try {
|
|
61
|
+
unlinkSync(lockPath);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// Lock file may have been removed by stale-lock stealing in another
|
|
65
|
+
// process; ignore — our critical section is over regardless.
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
if (err.code !== "EEXIST")
|
|
71
|
+
throw err;
|
|
72
|
+
// EEXIST: someone holds it. Check for staleness.
|
|
73
|
+
try {
|
|
74
|
+
const lockStat = statSync(lockPath);
|
|
75
|
+
if (Date.now() - lockStat.mtimeMs > LOCK_STALE_MS) {
|
|
76
|
+
// Stale. Steal with a write-and-verify handshake so two racing
|
|
77
|
+
// stealers can't both claim ownership. Whoever's token survives
|
|
78
|
+
// the read-back wins.
|
|
79
|
+
try {
|
|
80
|
+
unlinkSync(lockPath);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// Another process may have already stolen it; loop and retry.
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
const fd = openSync(lockPath, "wx");
|
|
88
|
+
try {
|
|
89
|
+
writeFileSync(lockPath, ourToken);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// Non-fatal; proceed to verification.
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
closeSync(fd);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// Non-fatal.
|
|
99
|
+
}
|
|
100
|
+
// Verification: read back and confirm our token is there. If a
|
|
101
|
+
// racer wrote first we'll see their token and back off.
|
|
102
|
+
try {
|
|
103
|
+
const recorded = readFileSync(lockPath, "utf8").trim();
|
|
104
|
+
if (recorded !== ourToken) {
|
|
105
|
+
// We lost the steal race; loop and retry normally.
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Read failed — treat as loss and retry.
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
return () => {
|
|
114
|
+
try {
|
|
115
|
+
unlinkSync(lockPath);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// Same rationale as happy-path release.
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
catch (stealErr) {
|
|
123
|
+
if (stealErr.code === "EEXIST") {
|
|
124
|
+
// Another process stole it between our unlink and open; retry.
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
throw stealErr;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// statSync failed — lock was just released, or racing cleanup.
|
|
133
|
+
// Fall through to sleep-and-retry.
|
|
134
|
+
}
|
|
135
|
+
// Held by a non-stale writer. Yield the event loop — do NOT busy-spin
|
|
136
|
+
// (C1). This allows every other async task on this event loop to run
|
|
137
|
+
// while we wait.
|
|
138
|
+
await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_INTERVAL_MS));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
throw new VaultLockError(`Timed out after ${LOCK_TIMEOUT_MS}ms waiting for vault write lock at ${lockPath}`);
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=file-lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-lock.js","sourceRoot":"","sources":["../../src/vault/file-lock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,QAAQ,EACR,SAAS,EACT,aAAa,EACb,YAAY,EACZ,QAAQ,EACR,UAAU,GACX,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAE3D,6EAA6E;AAC7E,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,CAAC;AAErC,iDAAiD;AACjD,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAEzC,8EAA8E;AAC9E,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC;AAEpC;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,SAAiB;IACtD,MAAM,QAAQ,GAAG,GAAG,SAAS,OAAO,CAAC;IACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,eAAe,CAAC;IAC9C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAC3B,MAAM,QAAQ,GAAG,GAAG,MAAM,EAAE,CAAC;IAE7B,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;QAC7B,4CAA4C;QAC5C,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YACpC,IAAI,CAAC;gBACH,6DAA6D;gBAC7D,aAAa,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YACpC,CAAC;YAAC,MAAM,CAAC;gBACP,mEAAmE;YACrE,CAAC;YACD,yEAAyE;YACzE,uEAAuE;YACvE,4BAA4B;YAC5B,IAAI,CAAC;gBACH,SAAS,CAAC,EAAE,CAAC,CAAC;YAChB,CAAC;YAAC,MAAM,CAAC;gBACP,aAAa;YACf,CAAC;YACD,OAAO,GAAG,EAAE;gBACV,IAAI,CAAC;oBACH,UAAU,CAAC,QAAQ,CAAC,CAAC;gBACvB,CAAC;gBAAC,MAAM,CAAC;oBACP,oEAAoE;oBACpE,6DAA6D;gBAC/D,CAAC;YACH,CAAC,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,GAAG,CAAC;YAEhE,iDAAiD;YACjD,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;gBACpC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,OAAO,GAAG,aAAa,EAAE,CAAC;oBAClD,+DAA+D;oBAC/D,gEAAgE;oBAChE,sBAAsB;oBACtB,IAAI,CAAC;wBACH,UAAU,CAAC,QAAQ,CAAC,CAAC;oBACvB,CAAC;oBAAC,MAAM,CAAC;wBACP,8DAA8D;wBAC9D,SAAS;oBACX,CAAC;oBACD,IAAI,CAAC;wBACH,MAAM,EAAE,GAAG,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;wBACpC,IAAI,CAAC;4BACH,aAAa,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;wBACpC,CAAC;wBAAC,MAAM,CAAC;4BACP,sCAAsC;wBACxC,CAAC;wBACD,IAAI,CAAC;4BACH,SAAS,CAAC,EAAE,CAAC,CAAC;wBAChB,CAAC;wBAAC,MAAM,CAAC;4BACP,aAAa;wBACf,CAAC;wBACD,+DAA+D;wBAC/D,wDAAwD;wBACxD,IAAI,CAAC;4BACH,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;4BACvD,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;gCAC1B,mDAAmD;gCACnD,SAAS;4BACX,CAAC;wBACH,CAAC;wBAAC,MAAM,CAAC;4BACP,yCAAyC;4BACzC,SAAS;wBACX,CAAC;wBACD,OAAO,GAAG,EAAE;4BACV,IAAI,CAAC;gCACH,UAAU,CAAC,QAAQ,CAAC,CAAC;4BACvB,CAAC;4BAAC,MAAM,CAAC;gCACP,wCAAwC;4BAC1C,CAAC;wBACH,CAAC,CAAC;oBACJ,CAAC;oBAAC,OAAO,QAAQ,EAAE,CAAC;wBAClB,IAAK,QAAkC,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;4BAC1D,+DAA+D;4BAC/D,SAAS;wBACX,CAAC;wBACD,MAAM,QAAQ,CAAC;oBACjB,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,+DAA+D;gBAC/D,mCAAmC;YACrC,CAAC;YAED,sEAAsE;YACtE,qEAAqE;YACrE,iBAAiB;YACjB,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAClC,UAAU,CAAC,OAAO,EAAE,sBAAsB,CAAC,CAC5C,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,IAAI,cAAc,CACtB,mBAAmB,eAAe,sCAAsC,QAAQ,EAAE,CACnF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionVault error classes — extracted so helper modules (file-lock,
|
|
3
|
+
* sidecar) can throw them without creating an import cycle back into
|
|
4
|
+
* session-vault.ts.
|
|
5
|
+
*
|
|
6
|
+
* Each subclass restores the prototype chain with `Object.setPrototypeOf`
|
|
7
|
+
* so `instanceof VaultError` etc. remains robust when the class is consumed
|
|
8
|
+
* across an ES transpile boundary (L2 — matches the EngramError pattern in
|
|
9
|
+
* `packages/sdk/src/errors.ts`).
|
|
10
|
+
*/
|
|
11
|
+
/** Base class for SessionVault errors. */
|
|
12
|
+
export declare class VaultError extends Error {
|
|
13
|
+
readonly code: string;
|
|
14
|
+
constructor(code: string, message: string);
|
|
15
|
+
}
|
|
16
|
+
/** Thrown when the master key can't be retrieved from the configured provider. */
|
|
17
|
+
export declare class VaultUnlockError extends VaultError {
|
|
18
|
+
constructor(message: string);
|
|
19
|
+
}
|
|
20
|
+
/** Thrown when decryption fails — wrong key, corrupted file, or AAD mismatch. */
|
|
21
|
+
export declare class VaultDecryptError extends VaultError {
|
|
22
|
+
constructor(message: string);
|
|
23
|
+
}
|
|
24
|
+
/** Thrown when rollback is detected and not explicitly accepted. */
|
|
25
|
+
export declare class VaultRollbackError extends VaultError {
|
|
26
|
+
readonly expected: number;
|
|
27
|
+
readonly actual: number;
|
|
28
|
+
constructor(expected: number, actual: number);
|
|
29
|
+
}
|
|
30
|
+
/** Thrown when operations are attempted on a closed vault. */
|
|
31
|
+
export declare class VaultClosedError extends VaultError {
|
|
32
|
+
constructor();
|
|
33
|
+
}
|
|
34
|
+
/** Thrown when the write-path file lock can't be acquired within the timeout. */
|
|
35
|
+
export declare class VaultLockError extends VaultError {
|
|
36
|
+
constructor(message: string);
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=session-vault-errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-vault-errors.d.ts","sourceRoot":"","sources":["../../src/vault/session-vault-errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,0CAA0C;AAC1C,qBAAa,UAAW,SAAQ,KAAK;aACP,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAK1D;AAED,kFAAkF;AAClF,qBAAa,gBAAiB,SAAQ,UAAU;gBAClC,OAAO,EAAE,MAAM;CAK5B;AAED,iFAAiF;AACjF,qBAAa,iBAAkB,SAAQ,UAAU;gBACnC,OAAO,EAAE,MAAM;CAK5B;AAED,oEAAoE;AACpE,qBAAa,kBAAmB,SAAQ,UAAU;aAE9B,QAAQ,EAAE,MAAM;aAChB,MAAM,EAAE,MAAM;gBADd,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM;CAWjC;AAED,8DAA8D;AAC9D,qBAAa,gBAAiB,SAAQ,UAAU;;CAM/C;AAED,iFAAiF;AACjF,qBAAa,cAAe,SAAQ,UAAU;gBAChC,OAAO,EAAE,MAAM;CAK5B"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionVault error classes — extracted so helper modules (file-lock,
|
|
3
|
+
* sidecar) can throw them without creating an import cycle back into
|
|
4
|
+
* session-vault.ts.
|
|
5
|
+
*
|
|
6
|
+
* Each subclass restores the prototype chain with `Object.setPrototypeOf`
|
|
7
|
+
* so `instanceof VaultError` etc. remains robust when the class is consumed
|
|
8
|
+
* across an ES transpile boundary (L2 — matches the EngramError pattern in
|
|
9
|
+
* `packages/sdk/src/errors.ts`).
|
|
10
|
+
*/
|
|
11
|
+
/** Base class for SessionVault errors. */
|
|
12
|
+
export class VaultError extends Error {
|
|
13
|
+
code;
|
|
14
|
+
constructor(code, message) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.code = code;
|
|
17
|
+
this.name = "VaultError";
|
|
18
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** Thrown when the master key can't be retrieved from the configured provider. */
|
|
22
|
+
export class VaultUnlockError extends VaultError {
|
|
23
|
+
constructor(message) {
|
|
24
|
+
super("VAULT_UNLOCK_FAILED", message);
|
|
25
|
+
this.name = "VaultUnlockError";
|
|
26
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** Thrown when decryption fails — wrong key, corrupted file, or AAD mismatch. */
|
|
30
|
+
export class VaultDecryptError extends VaultError {
|
|
31
|
+
constructor(message) {
|
|
32
|
+
super("VAULT_DECRYPT_FAILED", message);
|
|
33
|
+
this.name = "VaultDecryptError";
|
|
34
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/** Thrown when rollback is detected and not explicitly accepted. */
|
|
38
|
+
export class VaultRollbackError extends VaultError {
|
|
39
|
+
expected;
|
|
40
|
+
actual;
|
|
41
|
+
constructor(expected, actual) {
|
|
42
|
+
super("VAULT_VERSION_ROLLBACK_DETECTED", `Vault version rollback detected: sidecar expects version >= ${expected}, ` +
|
|
43
|
+
`but vault file reports version ${actual}. If this is an intentional ` +
|
|
44
|
+
`restore, pass { acceptRollback: true } to openVault().`);
|
|
45
|
+
this.expected = expected;
|
|
46
|
+
this.actual = actual;
|
|
47
|
+
this.name = "VaultRollbackError";
|
|
48
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Thrown when operations are attempted on a closed vault. */
|
|
52
|
+
export class VaultClosedError extends VaultError {
|
|
53
|
+
constructor() {
|
|
54
|
+
super("VAULT_CLOSED", "Vault has been closed; reopen with openVault() to continue.");
|
|
55
|
+
this.name = "VaultClosedError";
|
|
56
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** Thrown when the write-path file lock can't be acquired within the timeout. */
|
|
60
|
+
export class VaultLockError extends VaultError {
|
|
61
|
+
constructor(message) {
|
|
62
|
+
super("VAULT_LOCK_FAILED", message);
|
|
63
|
+
this.name = "VaultLockError";
|
|
64
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=session-vault-errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-vault-errors.js","sourceRoot":"","sources":["../../src/vault/session-vault-errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,0CAA0C;AAC1C,MAAM,OAAO,UAAW,SAAQ,KAAK;IACP;IAA5B,YAA4B,IAAY,EAAE,OAAe;QACvD,KAAK,CAAC,OAAO,CAAC,CAAC;QADW,SAAI,GAAJ,IAAI,CAAQ;QAEtC,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACpD,CAAC;CACF;AAED,kFAAkF;AAClF,MAAM,OAAO,gBAAiB,SAAQ,UAAU;IAC9C,YAAY,OAAe;QACzB,KAAK,CAAC,qBAAqB,EAAE,OAAO,CAAC,CAAC;QACtC,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;QAC/B,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACpD,CAAC;CACF;AAED,iFAAiF;AACjF,MAAM,OAAO,iBAAkB,SAAQ,UAAU;IAC/C,YAAY,OAAe;QACzB,KAAK,CAAC,sBAAsB,EAAE,OAAO,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;QAChC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACpD,CAAC;CACF;AAED,oEAAoE;AACpE,MAAM,OAAO,kBAAmB,SAAQ,UAAU;IAE9B;IACA;IAFlB,YACkB,QAAgB,EAChB,MAAc;QAE9B,KAAK,CACH,iCAAiC,EACjC,+DAA+D,QAAQ,IAAI;YACzE,kCAAkC,MAAM,8BAA8B;YACtE,wDAAwD,CAC3D,CAAC;QARc,aAAQ,GAAR,QAAQ,CAAQ;QAChB,WAAM,GAAN,MAAM,CAAQ;QAQ9B,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACpD,CAAC;CACF;AAED,8DAA8D;AAC9D,MAAM,OAAO,gBAAiB,SAAQ,UAAU;IAC9C;QACE,KAAK,CAAC,cAAc,EAAE,6DAA6D,CAAC,CAAC;QACrF,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;QAC/B,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACpD,CAAC;CACF;AAED,iFAAiF;AACjF,MAAM,OAAO,cAAe,SAAQ,UAAU;IAC5C,YAAY,OAAe;QACzB,KAAK,CAAC,mBAAmB,EAAE,OAAO,CAAC,CAAC;QACpC,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC7B,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACpD,CAAC;CACF"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionVault — public session-backed envelope vault API.
|
|
3
|
+
*
|
|
4
|
+
* Opens the CLI's encrypted vault file once per session (one KeyProvider
|
|
5
|
+
* prompt), caches the decrypted contents in RAM, and serves reads without
|
|
6
|
+
* further prompts. External writes (e.g. the CLI in another shell) become
|
|
7
|
+
* visible via mtime-check coherence on every read.
|
|
8
|
+
*
|
|
9
|
+
* Addresses the per-item Keychain-prompt problem flagged in issue #40:
|
|
10
|
+
* long-running daemons (centient-labs/maintainer) holding N credentials
|
|
11
|
+
* across a long lifetime should not reach into the OS keychain on every
|
|
12
|
+
* access. Envelope encryption with a single master-key unlock matches
|
|
13
|
+
* industry standard (KMS, HashiCorp Vault, 1Password, Bitwarden).
|
|
14
|
+
*
|
|
15
|
+
* ## Threat model (what this protects and doesn't)
|
|
16
|
+
*
|
|
17
|
+
* - Protects against filesystem-read-only adversaries (ciphertext is AEAD
|
|
18
|
+
* encrypted; forging plaintext requires the master key).
|
|
19
|
+
* - Protects against live-session and cold-start vault-file rollback by a
|
|
20
|
+
* filesystem-write-only adversary via the combined in-payload
|
|
21
|
+
* `vaultVersion` + sidecar-file `highestSeenVersion` scheme.
|
|
22
|
+
* - Does NOT protect against an adversary with **both** master-key access
|
|
23
|
+
* and filesystem write — game over for any local envelope vault.
|
|
24
|
+
* - Does NOT protect against an adversary with write access to the vault
|
|
25
|
+
* directory who chooses to downgrade both vault and sidecar in lockstep
|
|
26
|
+
* — the sidecar lives next to the vault. If your threat model includes
|
|
27
|
+
* adversarial writes to `~/.centient/secrets/`, use a secrets service
|
|
28
|
+
* with remote attestation (HashiCorp Vault, AWS Secrets Manager,
|
|
29
|
+
* 1Password Connect) instead.
|
|
30
|
+
* - Session key is in process RAM for the full session lifetime. Any code
|
|
31
|
+
* with execution in the process has access to all secrets in the vault.
|
|
32
|
+
* Operators running daemons with this API SHOULD disable core dumps
|
|
33
|
+
* (`ulimit -c 0` / `prlimit --core=0`) and disable the Node.js inspector
|
|
34
|
+
* (`NODE_OPTIONS=--inspect` grants heap read to anyone on the inspector
|
|
35
|
+
* socket — a full master-key compromise vector).
|
|
36
|
+
* - On macOS, a newly-started process will still prompt the user for
|
|
37
|
+
* Keychain access even if another process holds the vault open.
|
|
38
|
+
* Keychain ACLs are per-process, not per-vault-file.
|
|
39
|
+
*/
|
|
40
|
+
import type { KeyProviderType } from "../key-providers/types.js";
|
|
41
|
+
import { VaultError, VaultUnlockError, VaultDecryptError, VaultRollbackError, VaultClosedError, VaultLockError } from "./session-vault-errors.js";
|
|
42
|
+
export { VaultError, VaultUnlockError, VaultDecryptError, VaultRollbackError, VaultClosedError, VaultLockError, };
|
|
43
|
+
/** Current payload schema version — bump requires a compat migration. */
|
|
44
|
+
export declare const VAULT_SCHEMA_VERSION = 1;
|
|
45
|
+
/** Default vault file location — same path the CLI uses, so they share state. */
|
|
46
|
+
export declare const DEFAULT_VAULT_PATH: string;
|
|
47
|
+
/** Default sidecar location — stores highest-ever-seen vault version. */
|
|
48
|
+
export declare const DEFAULT_SIDECAR_PATH: string;
|
|
49
|
+
/**
|
|
50
|
+
* AAD prefix — static byte header mixed into the vault ciphertext's
|
|
51
|
+
* Additional Authenticated Data. Binding this prefix into AAD means a
|
|
52
|
+
* ciphertext from some other AES-GCM user with the same key cannot be
|
|
53
|
+
* substituted into the vault. Exported so test fixtures can produce AAD
|
|
54
|
+
* consistent with the real implementation without duplicating the constant.
|
|
55
|
+
*/
|
|
56
|
+
export declare const VAULT_AAD_PREFIX = "centient-secrets-vault";
|
|
57
|
+
/**
|
|
58
|
+
* Coherence strategy governs how the open vault reconciles in-memory state
|
|
59
|
+
* with concurrent external writes to the vault file.
|
|
60
|
+
*/
|
|
61
|
+
export type CoherenceStrategy = "mtime-check" | "strict" | "best-effort";
|
|
62
|
+
/** Options for {@link openVault}. All fields are optional. */
|
|
63
|
+
export interface OpenVaultOptions {
|
|
64
|
+
/** Alternate vault file path. Defaults to the same path the CLI uses. */
|
|
65
|
+
path?: string;
|
|
66
|
+
/** Alternate sidecar path. Defaults to vault directory + `vault.seen-version`. */
|
|
67
|
+
sidecarPath?: string;
|
|
68
|
+
/**
|
|
69
|
+
* Coherence strategy for concurrent external writes. Default `mtime-check`:
|
|
70
|
+
* stat on every read; re-decrypt if mtime advanced. `strict` throws on stale
|
|
71
|
+
* snapshot. `best-effort` keeps the in-memory snapshot until `reload()`.
|
|
72
|
+
*/
|
|
73
|
+
coherence?: CoherenceStrategy;
|
|
74
|
+
/**
|
|
75
|
+
* Opt-in acceptance of a detected rollback (sidecar version > vault version).
|
|
76
|
+
* Emits a scary warning on stderr. Use only when the operator explicitly
|
|
77
|
+
* intends to restore an older vault (backup restore, etc.).
|
|
78
|
+
*/
|
|
79
|
+
acceptRollback?: boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Opt-in acceptance of a missing sidecar. Default behaviour (`false`) is to
|
|
82
|
+
* **refuse** to open the vault when the sidecar is absent — this enforces
|
|
83
|
+
* the security invariant that rollback protection is always in effect.
|
|
84
|
+
* Pass `true` for legitimate first-use contexts (fresh install, test
|
|
85
|
+
* fixtures, post-migration) to auto-initialize `seenVersion = vaultVersion`
|
|
86
|
+
* with a stderr warning. See docs/session-vault.md §Missing sidecar.
|
|
87
|
+
*/
|
|
88
|
+
acceptMissingSidecar?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Optional auto-close TTL in milliseconds. Not set by default — daemons run
|
|
91
|
+
* forever; forced re-auth undoes the point of a session vault. Useful for
|
|
92
|
+
* short-lived script consumers that want defense-in-depth.
|
|
93
|
+
*/
|
|
94
|
+
ttlMs?: number;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* A long-lived handle to an unlocked vault. Construct with {@link openVault};
|
|
98
|
+
* close with {@link SessionVault.close}. Operations are async so policy
|
|
99
|
+
* `before` hooks can await (e.g. remote attestation).
|
|
100
|
+
*/
|
|
101
|
+
export interface SessionVault {
|
|
102
|
+
/** Read a secret by name. Returns null if the name isn't in the vault. */
|
|
103
|
+
get(name: string): Promise<string | null>;
|
|
104
|
+
/** List all secret names, optionally prefix-filtered. Sorted ascending. */
|
|
105
|
+
list(prefix?: string): Promise<string[]>;
|
|
106
|
+
/** Write a secret. Re-encrypts and saves the vault file atomically. */
|
|
107
|
+
set(name: string, value: string): Promise<void>;
|
|
108
|
+
/** Delete a secret. Returns true if the name existed and was removed. */
|
|
109
|
+
delete(name: string): Promise<boolean>;
|
|
110
|
+
/** Force an immediate reload from disk regardless of coherence strategy. */
|
|
111
|
+
reload(): Promise<void>;
|
|
112
|
+
/** Release the session key and in-memory state. No-op if already closed. */
|
|
113
|
+
close(): void;
|
|
114
|
+
/** Diagnostic — the KeyProvider that unlocked this session. */
|
|
115
|
+
readonly provider: KeyProviderType;
|
|
116
|
+
/** Diagnostic — absolute path of the vault file. */
|
|
117
|
+
readonly path: string;
|
|
118
|
+
/** Diagnostic — the current in-memory vault version. */
|
|
119
|
+
readonly vaultVersion: number;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Open an encrypted session vault.
|
|
123
|
+
*
|
|
124
|
+
* Resolves the configured {@link KeyProvider} to obtain the master key,
|
|
125
|
+
* decrypts the vault file bound to its resolved real path (symlink-aware),
|
|
126
|
+
* checks rollback detection via the sidecar, and returns a long-lived
|
|
127
|
+
* {@link SessionVault} handle that serves reads from memory.
|
|
128
|
+
*
|
|
129
|
+
* @param opts - {@link OpenVaultOptions}. All fields are optional; defaults
|
|
130
|
+
* use the same paths the `centient secrets` CLI uses.
|
|
131
|
+
* @returns An open {@link SessionVault}. Call `close()` when done.
|
|
132
|
+
* @throws {@link VaultError} `VAULT_NOT_FOUND` when the vault file is absent.
|
|
133
|
+
* @throws {@link VaultUnlockError} when the KeyProvider cannot return a key.
|
|
134
|
+
* @throws {@link VaultDecryptError} when decryption fails (wrong key, AAD
|
|
135
|
+
* mismatch, corrupted payload).
|
|
136
|
+
* @throws {@link VaultRollbackError} when the sidecar indicates a rollback
|
|
137
|
+
* and `acceptRollback` is not set.
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```ts
|
|
141
|
+
* const vault = await openVault({ ttlMs: 60_000 });
|
|
142
|
+
* const apiKey = await vault.get("openai-api-key");
|
|
143
|
+
* vault.close();
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
export declare function openVault(opts?: OpenVaultOptions): Promise<SessionVault>;
|
|
147
|
+
//# sourceMappingURL=session-vault.d.ts.map
|