@centient/secrets 0.9.0 → 0.11.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 +424 -8
- package/dist/audit/hmac-chain.d.ts +117 -0
- package/dist/audit/hmac-chain.d.ts.map +1 -0
- package/dist/audit/hmac-chain.js +811 -0
- package/dist/audit/hmac-chain.js.map +1 -0
- package/dist/audit/ocsf.d.ts +108 -0
- package/dist/audit/ocsf.d.ts.map +1 -0
- package/dist/audit/ocsf.js +204 -0
- package/dist/audit/ocsf.js.map +1 -0
- package/dist/cli/hidden-prompt.d.ts.map +1 -1
- package/dist/cli/hidden-prompt.js +55 -10
- package/dist/cli/hidden-prompt.js.map +1 -1
- package/dist/cli/messages.d.ts +40 -8
- package/dist/cli/messages.d.ts.map +1 -1
- package/dist/cli/messages.js +34 -8
- package/dist/cli/messages.js.map +1 -1
- package/dist/cli/secrets-cli.d.ts.map +1 -1
- package/dist/cli/secrets-cli.js +191 -32
- package/dist/cli/secrets-cli.js.map +1 -1
- package/dist/client.d.ts +26 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +129 -0
- package/dist/client.js.map +1 -0
- package/dist/compatibility.d.ts +199 -0
- package/dist/compatibility.d.ts.map +1 -0
- package/dist/compatibility.js +419 -0
- package/dist/compatibility.js.map +1 -0
- package/dist/crypto/darwin-keychain.d.ts +394 -0
- package/dist/crypto/darwin-keychain.d.ts.map +1 -0
- package/dist/crypto/darwin-keychain.js +793 -0
- package/dist/crypto/darwin-keychain.js.map +1 -0
- package/dist/crypto/vault-common.d.ts +55 -12
- package/dist/crypto/vault-common.d.ts.map +1 -1
- package/dist/crypto/vault-common.js +74 -71
- package/dist/crypto/vault-common.js.map +1 -1
- package/dist/environment/EnvironmentManager.d.ts.map +1 -1
- package/dist/environment/EnvironmentManager.js +5 -10
- package/dist/environment/EnvironmentManager.js.map +1 -1
- package/dist/index.d.ts +37 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38 -6
- package/dist/index.js.map +1 -1
- package/dist/key-providers/keychain-provider.d.ts +30 -2
- package/dist/key-providers/keychain-provider.d.ts.map +1 -1
- package/dist/key-providers/keychain-provider.js +29 -5
- package/dist/key-providers/keychain-provider.js.map +1 -1
- package/dist/key-providers/onepassword-provider.d.ts +65 -5
- package/dist/key-providers/onepassword-provider.d.ts.map +1 -1
- package/dist/key-providers/onepassword-provider.js +159 -67
- package/dist/key-providers/onepassword-provider.js.map +1 -1
- package/dist/key-providers/op-cli.d.ts +78 -0
- package/dist/key-providers/op-cli.d.ts.map +1 -0
- package/dist/key-providers/op-cli.js +123 -0
- package/dist/key-providers/op-cli.js.map +1 -0
- package/dist/key-providers/passphrase-provider.d.ts +20 -0
- package/dist/key-providers/passphrase-provider.d.ts.map +1 -1
- package/dist/key-providers/passphrase-provider.js +49 -0
- package/dist/key-providers/passphrase-provider.js.map +1 -1
- package/dist/key-providers/probe.d.ts +46 -0
- package/dist/key-providers/probe.d.ts.map +1 -0
- package/dist/key-providers/probe.js +70 -0
- package/dist/key-providers/probe.js.map +1 -0
- package/dist/key-providers/types.d.ts +51 -1
- package/dist/key-providers/types.d.ts.map +1 -1
- package/dist/platform/agent-detect.d.ts +39 -3
- package/dist/platform/agent-detect.d.ts.map +1 -1
- package/dist/platform/agent-detect.js +55 -9
- package/dist/platform/agent-detect.js.map +1 -1
- package/dist/platform/session-context.d.ts +77 -0
- package/dist/platform/session-context.d.ts.map +1 -0
- package/dist/platform/session-context.js +83 -0
- package/dist/platform/session-context.js.map +1 -0
- package/dist/policies/audit-trail.d.ts +25 -0
- package/dist/policies/audit-trail.d.ts.map +1 -0
- package/dist/policies/audit-trail.js +31 -0
- package/dist/policies/audit-trail.js.map +1 -0
- package/dist/secret-state.d.ts +54 -0
- package/dist/secret-state.d.ts.map +1 -0
- package/dist/secret-state.js +2 -0
- package/dist/secret-state.js.map +1 -0
- package/dist/sinks/index.d.ts +9 -0
- package/dist/sinks/index.d.ts.map +1 -0
- package/dist/sinks/index.js +5 -0
- package/dist/sinks/index.js.map +1 -0
- package/dist/sinks/jsonl.d.ts +16 -0
- package/dist/sinks/jsonl.d.ts.map +1 -0
- package/dist/sinks/jsonl.js +51 -0
- package/dist/sinks/jsonl.js.map +1 -0
- package/dist/sinks/otel.d.ts +22 -0
- package/dist/sinks/otel.d.ts.map +1 -0
- package/dist/sinks/otel.js +155 -0
- package/dist/sinks/otel.js.map +1 -0
- package/dist/sinks/queue.d.ts +12 -0
- package/dist/sinks/queue.d.ts.map +1 -0
- package/dist/sinks/queue.js +89 -0
- package/dist/sinks/queue.js.map +1 -0
- package/dist/sinks/syslog.d.ts +17 -0
- package/dist/sinks/syslog.d.ts.map +1 -0
- package/dist/sinks/syslog.js +103 -0
- package/dist/sinks/syslog.js.map +1 -0
- package/dist/sinks/types.d.ts +27 -0
- package/dist/sinks/types.d.ts.map +1 -0
- package/dist/sinks/types.js +18 -0
- package/dist/sinks/types.js.map +1 -0
- package/dist/vault/policy.d.ts +64 -34
- package/dist/vault/policy.d.ts.map +1 -1
- package/dist/vault/policy.js +109 -67
- package/dist/vault/policy.js.map +1 -1
- package/dist/vault/session-vault-errors.d.ts +72 -4
- package/dist/vault/session-vault-errors.d.ts.map +1 -1
- package/dist/vault/session-vault-errors.js +95 -4
- package/dist/vault/session-vault-errors.js.map +1 -1
- package/dist/vault/session-vault.d.ts +145 -2
- package/dist/vault/session-vault.d.ts.map +1 -1
- package/dist/vault/session-vault.js +548 -72
- package/dist/vault/session-vault.js.map +1 -1
- package/dist/vault/sidecar.d.ts +9 -0
- package/dist/vault/sidecar.d.ts.map +1 -1
- package/dist/vault/sidecar.js +72 -3
- package/dist/vault/sidecar.js.map +1 -1
- package/dist/vault/types.d.ts +58 -10
- package/dist/vault/types.d.ts.map +1 -1
- package/dist/vault/types.js.map +1 -1
- package/dist/vault/vault-env.d.ts +43 -50
- package/dist/vault/vault-env.d.ts.map +1 -1
- package/dist/vault/vault-env.js +141 -62
- package/dist/vault/vault-env.js.map +1 -1
- package/dist/vault/vault-gpg.d.ts +6 -1
- package/dist/vault/vault-gpg.d.ts.map +1 -1
- package/dist/vault/vault-gpg.js +10 -8
- package/dist/vault/vault-gpg.js.map +1 -1
- package/dist/vault/vault-libsecret.d.ts +6 -1
- package/dist/vault/vault-libsecret.d.ts.map +1 -1
- package/dist/vault/vault-libsecret.js +16 -14
- package/dist/vault/vault-libsecret.js.map +1 -1
- package/dist/vault/vault-onepassword.d.ts +119 -0
- package/dist/vault/vault-onepassword.d.ts.map +1 -0
- package/dist/vault/vault-onepassword.js +271 -0
- package/dist/vault/vault-onepassword.js.map +1 -0
- package/dist/vault/vault-utils.d.ts +37 -3
- package/dist/vault/vault-utils.d.ts.map +1 -1
- package/dist/vault/vault-utils.js +65 -3
- package/dist/vault/vault-utils.js.map +1 -1
- package/dist/vault/vault-windows.d.ts +7 -2
- package/dist/vault/vault-windows.d.ts.map +1 -1
- package/dist/vault/vault-windows.js +21 -16
- package/dist/vault/vault-windows.js.map +1 -1
- package/dist/vault/vault.d.ts +120 -9
- package/dist/vault/vault.d.ts.map +1 -1
- package/dist/vault/vault.js +222 -139
- package/dist/vault/vault.js.map +1 -1
- package/package.json +8 -2
package/dist/vault/policy.js
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* SecretsPolicy — Middleware layer for credential operations.
|
|
3
3
|
*
|
|
4
|
-
* Policies are cross-cutting concerns (audit, rate limiting, access
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* stack
|
|
4
|
+
* Policies are cross-cutting concerns (audit, rate limiting, access control,
|
|
5
|
+
* attestation) applied to every credential operation. Explicit clients own an
|
|
6
|
+
* isolated stack supplied to `createSecretsClient`; `setSecretsPolicies`
|
|
7
|
+
* remains as the deprecated configuration path for the module-level shims and
|
|
8
|
+
* SessionVault. The API shape is designed for the full stack in ADR-002.
|
|
9
9
|
*
|
|
10
10
|
* Execution model:
|
|
11
11
|
* 1. `before` hooks run top-to-bottom. If one throws, the operation
|
|
12
12
|
* is aborted and the error propagates to the caller — but the
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* rejecting policy and already-entered policies still fire their
|
|
14
|
+
* `after` hooks with a `*_rejected` event, so
|
|
15
15
|
* a policy-denied operation is audited rather than vanishing
|
|
16
|
-
* (
|
|
17
|
-
* not fire — its `before` hook did not complete.
|
|
16
|
+
* (issue #98). Policies not yet reached do not fire.
|
|
18
17
|
* 2. The backend operation executes.
|
|
19
18
|
* 3. `after` hooks run bottom-to-top with a structured event
|
|
20
19
|
* describing the outcome. Exceptions in `after` hooks are
|
|
@@ -44,59 +43,57 @@ export function rejectedEventType(op) {
|
|
|
44
43
|
}
|
|
45
44
|
}
|
|
46
45
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if (
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
46
|
+
function snapshotPolicies(configured) {
|
|
47
|
+
const snapshot = configured.map((policy) => {
|
|
48
|
+
// Capture the complete callable shape, not just the containing array.
|
|
49
|
+
// Otherwise a caller could add or replace a before hook after ordering
|
|
50
|
+
// validation and make a rejection observer silently miss denials.
|
|
51
|
+
const name = policy.name;
|
|
52
|
+
const observesPolicyRejections = policy.observesPolicyRejections;
|
|
53
|
+
const configuredBefore = policy.before;
|
|
54
|
+
const configuredAfter = policy.after;
|
|
55
|
+
// ADR-002 permits a policy object to retain mutable internal state. Bind
|
|
56
|
+
// the captured hooks to that original receiver while freezing only the
|
|
57
|
+
// validated public wrapper and its hook identities.
|
|
58
|
+
const before = configuredBefore?.bind(policy);
|
|
59
|
+
const after = configuredAfter?.bind(policy);
|
|
60
|
+
return Object.freeze({
|
|
61
|
+
name,
|
|
62
|
+
...(observesPolicyRejections === true
|
|
63
|
+
? { observesPolicyRejections: true }
|
|
64
|
+
: {}),
|
|
65
|
+
...(before !== undefined ? { before } : {}),
|
|
66
|
+
...(after !== undefined ? { after } : {}),
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
let precedingBeforePolicy;
|
|
70
|
+
for (const policy of snapshot) {
|
|
71
|
+
if (policy.observesPolicyRejections === true &&
|
|
72
|
+
precedingBeforePolicy !== undefined) {
|
|
73
|
+
throw new TypeError(`policy "${policy.name}" observes policy rejections and must precede ` +
|
|
74
|
+
`policy "${precedingBeforePolicy.name}" (and every other policy with a before hook)`);
|
|
76
75
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
runAfterHooksForRange(makeRejectionEvent(msg), i - 1);
|
|
80
|
-
throw err;
|
|
76
|
+
if (policy.before !== undefined && precedingBeforePolicy === undefined) {
|
|
77
|
+
precedingBeforePolicy = policy;
|
|
81
78
|
}
|
|
82
79
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
80
|
+
// The registry's public inspection API returns this same value. Freeze the
|
|
81
|
+
// array so a cast to a mutable type cannot bypass replacement validation.
|
|
82
|
+
return Object.freeze(snapshot);
|
|
86
83
|
}
|
|
87
84
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* warning — audit-infrastructure failures must never break credential
|
|
92
|
-
* operations. `after` hooks are synchronous by contract (see
|
|
93
|
-
* `SecretsPolicy.after`); a hook that returns a rejected `Promise` is
|
|
94
|
-
* not awaited here, so it would surface as an unhandled rejection.
|
|
85
|
+
* Build an isolated policy runner. Its input is snapshotted immediately, and
|
|
86
|
+
* every operation captures the current immutable frame so replacement cannot
|
|
87
|
+
* splice two different stacks across an await boundary.
|
|
95
88
|
*/
|
|
96
|
-
function
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
89
|
+
export function createPolicyRunner(initialPolicies = []) {
|
|
90
|
+
let policies = snapshotPolicies(initialPolicies);
|
|
91
|
+
let afterWarningEmitted = false;
|
|
92
|
+
const runAfterForFrame = (event, frame) => {
|
|
93
|
+
for (let i = frame.length - 1; i >= 0; i--) {
|
|
94
|
+
const policy = frame[i];
|
|
95
|
+
if (!policy.after)
|
|
96
|
+
continue;
|
|
100
97
|
try {
|
|
101
98
|
policy.after(event);
|
|
102
99
|
}
|
|
@@ -108,20 +105,65 @@ function runAfterHooksForRange(event, fromIndex) {
|
|
|
108
105
|
}
|
|
109
106
|
}
|
|
110
107
|
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
export function auditTrail(opts) {
|
|
114
|
-
const includeReads = opts.includeReads ?? true;
|
|
108
|
+
};
|
|
115
109
|
return {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
110
|
+
async runBefore(op, makeRejectionEvent) {
|
|
111
|
+
const frame = policies;
|
|
112
|
+
for (let i = 0; i < frame.length; i++) {
|
|
113
|
+
const policy = frame[i];
|
|
114
|
+
try {
|
|
115
|
+
await policy.before?.(op);
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
119
|
+
// Issue #98 is authoritative: the rejecting policy is part of the
|
|
120
|
+
// unwind, followed by already-entered outer policies.
|
|
121
|
+
runAfterForFrame(makeRejectionEvent(msg), frame.slice(0, i + 1));
|
|
122
|
+
throw err;
|
|
123
|
+
}
|
|
122
124
|
}
|
|
123
|
-
|
|
125
|
+
return frame;
|
|
126
|
+
},
|
|
127
|
+
runAfter(event, frame = policies) {
|
|
128
|
+
runAfterForFrame(event, frame);
|
|
129
|
+
},
|
|
130
|
+
getPolicies() {
|
|
131
|
+
return policies;
|
|
132
|
+
},
|
|
133
|
+
replacePolicies(nextPolicies) {
|
|
134
|
+
policies = snapshotPolicies(nextPolicies);
|
|
135
|
+
afterWarningEmitted = false;
|
|
124
136
|
},
|
|
125
137
|
};
|
|
126
138
|
}
|
|
139
|
+
/** Global runner retained only for legacy module APIs and SessionVault. */
|
|
140
|
+
export const defaultPolicyRunner = createPolicyRunner();
|
|
141
|
+
/**
|
|
142
|
+
* @deprecated Configure policies on an isolated client created with
|
|
143
|
+
* `createSecretsClient`. This setter remains for module-level shims and
|
|
144
|
+
* SessionVault compatibility through the 1.x deprecation window.
|
|
145
|
+
*/
|
|
146
|
+
export function setSecretsPolicies(policies) {
|
|
147
|
+
defaultPolicyRunner.replacePolicies(policies);
|
|
148
|
+
}
|
|
149
|
+
/** @deprecated Inspect the policy list owned by the legacy default client. */
|
|
150
|
+
export function getActivePolicies() {
|
|
151
|
+
return defaultPolicyRunner.getPolicies();
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Run `before` hooks top-to-bottom. If a hook throws, the operation is
|
|
155
|
+
* rejected: the rejecting policy and already-entered policies (indices
|
|
156
|
+
* `0..i`, bottom-to-top) fire `after` with the event built by
|
|
157
|
+
* `makeRejectionEvent` so the denied operation is still audited, then the
|
|
158
|
+
* original error is re-thrown to abort the operation. This follows issue
|
|
159
|
+
* #98's acceptance contract; policies after the rejector are never entered.
|
|
160
|
+
*/
|
|
161
|
+
export async function runBeforeHooks(op, makeRejectionEvent) {
|
|
162
|
+
return defaultPolicyRunner.runBefore(op, makeRejectionEvent);
|
|
163
|
+
}
|
|
164
|
+
export function runAfterHooks(event, frame) {
|
|
165
|
+
defaultPolicyRunner.runAfter(event, frame);
|
|
166
|
+
}
|
|
167
|
+
// Compatibility re-export: the built-in now lives with the other policies.
|
|
168
|
+
export { auditTrail } from "../policies/audit-trail.js";
|
|
127
169
|
//# sourceMappingURL=policy.js.map
|
package/dist/vault/policy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/vault/policy.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/vault/policy.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* sidecar) can throw
|
|
4
|
-
* session-vault.ts.
|
|
2
|
+
* Vault error taxonomy — the package's one `VaultError` hierarchy, extracted
|
|
3
|
+
* so helper modules (file-lock, sidecar, vault-utils) can throw these without
|
|
4
|
+
* creating an import cycle back into session-vault.ts.
|
|
5
|
+
*
|
|
6
|
+
* Most members are SessionVault-specific; `InvalidCredentialKeyError` is
|
|
7
|
+
* raised by the credential cascade (`vault.ts`) and its backends. They share a
|
|
8
|
+
* base class deliberately — a caller wrapping vault work in
|
|
9
|
+
* `catch (e) { if (e instanceof VaultError) … }` should not have to know which
|
|
10
|
+
* storage path produced the failure.
|
|
11
|
+
*
|
|
12
|
+
* This module has no imports on purpose; keep it that way so any vault module
|
|
13
|
+
* can depend on it.
|
|
5
14
|
*
|
|
6
15
|
* Each subclass restores the prototype chain with `Object.setPrototypeOf`
|
|
7
16
|
* so `instanceof VaultError` etc. remains robust when the class is consumed
|
|
8
17
|
* across an ES transpile boundary (L2 — matches the EngramError pattern in
|
|
9
18
|
* `packages/sdk/src/errors.ts`).
|
|
10
19
|
*/
|
|
11
|
-
/** Base class for
|
|
20
|
+
/** Base class for vault errors. */
|
|
12
21
|
export declare class VaultError extends Error {
|
|
13
22
|
readonly code: string;
|
|
14
23
|
constructor(code: string, message: string);
|
|
@@ -31,6 +40,65 @@ export declare class VaultRollbackError extends VaultError {
|
|
|
31
40
|
export declare class VaultClosedError extends VaultError {
|
|
32
41
|
constructor();
|
|
33
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Thrown when a rekey's external commit failed AND restoring the prior
|
|
45
|
+
* ciphertext then failed too.
|
|
46
|
+
*
|
|
47
|
+
* This is the one indeterminate state the rekey path can reach: the vault file
|
|
48
|
+
* may still hold the ciphertext written under the NEW key, so neither key can
|
|
49
|
+
* be assumed to be the right one — and, critically, **no key material for
|
|
50
|
+
* either may be discarded**. Callers that clean up on failure (e.g. deleting
|
|
51
|
+
* freshly written passphrase metadata) must special-case this error; deleting
|
|
52
|
+
* the new key's material here can leave the vault permanently unopenable.
|
|
53
|
+
*/
|
|
54
|
+
export declare class VaultRestoreError extends VaultError {
|
|
55
|
+
/** Absolute path of the vault whose state is indeterminate. */
|
|
56
|
+
readonly path: string;
|
|
57
|
+
/** Version the new ciphertext carries, if it is the one on disk. */
|
|
58
|
+
readonly attemptedVersion: number;
|
|
59
|
+
/** The `commit` failure that triggered the rollback attempt. */
|
|
60
|
+
readonly commitCause: unknown;
|
|
61
|
+
/** The failure of the rollback write itself. */
|
|
62
|
+
readonly restoreCause: unknown;
|
|
63
|
+
constructor(
|
|
64
|
+
/** Absolute path of the vault whose state is indeterminate. */
|
|
65
|
+
path: string,
|
|
66
|
+
/** Version the new ciphertext carries, if it is the one on disk. */
|
|
67
|
+
attemptedVersion: number,
|
|
68
|
+
/** The `commit` failure that triggered the rollback attempt. */
|
|
69
|
+
commitCause: unknown,
|
|
70
|
+
/** The failure of the rollback write itself. */
|
|
71
|
+
restoreCause: unknown);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Thrown when a credential key (or a `listCredentials` prefix) does not match
|
|
75
|
+
* the key grammar every vault backend is required to accept.
|
|
76
|
+
*
|
|
77
|
+
* **Why this throws rather than returning a falsy value (#168).** The backends
|
|
78
|
+
* disagree about non-conforming keys: macOS Keychain and the env fallback
|
|
79
|
+
* store anything, while the 1Password, GPG, libsecret and Windows backends
|
|
80
|
+
* cannot address such a key at all. A key that violates the grammar therefore
|
|
81
|
+
* writes successfully on a Mac and is unreadable everywhere else — a silent
|
|
82
|
+
* write/read asymmetry (P2). Returning `false`/`null` would express that as
|
|
83
|
+
* "the write failed" or, worse on the read path, as "no such credential",
|
|
84
|
+
* which is indistinguishable from a genuine miss. The key is malformed, not
|
|
85
|
+
* missing, and only a distinguishable failure says so.
|
|
86
|
+
*/
|
|
87
|
+
export declare class InvalidCredentialKeyError extends VaultError {
|
|
88
|
+
/** The offending key or prefix. A key name, never credential material. */
|
|
89
|
+
readonly key: string;
|
|
90
|
+
/** The operation that was refused — "read", "write", "delete", "enumerate". */
|
|
91
|
+
readonly operation: string;
|
|
92
|
+
/** Whether `key` was supplied as a full key or as a `listCredentials` prefix. */
|
|
93
|
+
readonly kind: "key" | "prefix";
|
|
94
|
+
constructor(
|
|
95
|
+
/** The offending key or prefix. A key name, never credential material. */
|
|
96
|
+
key: string,
|
|
97
|
+
/** The operation that was refused — "read", "write", "delete", "enumerate". */
|
|
98
|
+
operation: string,
|
|
99
|
+
/** Whether `key` was supplied as a full key or as a `listCredentials` prefix. */
|
|
100
|
+
kind?: "key" | "prefix");
|
|
101
|
+
}
|
|
34
102
|
/** Thrown when the write-path file lock can't be acquired within the timeout. */
|
|
35
103
|
export declare class VaultLockError extends VaultError {
|
|
36
104
|
constructor(message: string);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-vault-errors.d.ts","sourceRoot":"","sources":["../../src/vault/session-vault-errors.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"session-vault-errors.d.ts","sourceRoot":"","sources":["../../src/vault/session-vault-errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,mCAAmC;AACnC,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;;;;;;;;;;GAUG;AACH,qBAAa,iBAAkB,SAAQ,UAAU;IAE7C,+DAA+D;aAC/C,IAAI,EAAE,MAAM;IAC5B,oEAAoE;aACpD,gBAAgB,EAAE,MAAM;IACxC,gEAAgE;aAChD,WAAW,EAAE,OAAO;IACpC,gDAAgD;aAChC,YAAY,EAAE,OAAO;;IAPrC,+DAA+D;IAC/C,IAAI,EAAE,MAAM;IAC5B,oEAAoE;IACpD,gBAAgB,EAAE,MAAM;IACxC,gEAAgE;IAChD,WAAW,EAAE,OAAO;IACpC,gDAAgD;IAChC,YAAY,EAAE,OAAO;CAexC;AAMD;;;;;;;;;;;;;GAaG;AACH,qBAAa,yBAA0B,SAAQ,UAAU;IAErD,0EAA0E;aAC1D,GAAG,EAAE,MAAM;IAC3B,+EAA+E;aAC/D,SAAS,EAAE,MAAM;IACjC,iFAAiF;aACjE,IAAI,EAAE,KAAK,GAAG,QAAQ;;IALtC,0EAA0E;IAC1D,GAAG,EAAE,MAAM;IAC3B,+EAA+E;IAC/D,SAAS,EAAE,MAAM;IACjC,iFAAiF;IACjE,IAAI,GAAE,KAAK,GAAG,QAAgB;CAejD;AAED,iFAAiF;AACjF,qBAAa,cAAe,SAAQ,UAAU;gBAChC,OAAO,EAAE,MAAM;CAK5B"}
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* sidecar) can throw
|
|
4
|
-
* session-vault.ts.
|
|
2
|
+
* Vault error taxonomy — the package's one `VaultError` hierarchy, extracted
|
|
3
|
+
* so helper modules (file-lock, sidecar, vault-utils) can throw these without
|
|
4
|
+
* creating an import cycle back into session-vault.ts.
|
|
5
|
+
*
|
|
6
|
+
* Most members are SessionVault-specific; `InvalidCredentialKeyError` is
|
|
7
|
+
* raised by the credential cascade (`vault.ts`) and its backends. They share a
|
|
8
|
+
* base class deliberately — a caller wrapping vault work in
|
|
9
|
+
* `catch (e) { if (e instanceof VaultError) … }` should not have to know which
|
|
10
|
+
* storage path produced the failure.
|
|
11
|
+
*
|
|
12
|
+
* This module has no imports on purpose; keep it that way so any vault module
|
|
13
|
+
* can depend on it.
|
|
5
14
|
*
|
|
6
15
|
* Each subclass restores the prototype chain with `Object.setPrototypeOf`
|
|
7
16
|
* so `instanceof VaultError` etc. remains robust when the class is consumed
|
|
8
17
|
* across an ES transpile boundary (L2 — matches the EngramError pattern in
|
|
9
18
|
* `packages/sdk/src/errors.ts`).
|
|
10
19
|
*/
|
|
11
|
-
/** Base class for
|
|
20
|
+
/** Base class for vault errors. */
|
|
12
21
|
export class VaultError extends Error {
|
|
13
22
|
code;
|
|
14
23
|
constructor(code, message) {
|
|
@@ -56,6 +65,88 @@ export class VaultClosedError extends VaultError {
|
|
|
56
65
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
57
66
|
}
|
|
58
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Thrown when a rekey's external commit failed AND restoring the prior
|
|
70
|
+
* ciphertext then failed too.
|
|
71
|
+
*
|
|
72
|
+
* This is the one indeterminate state the rekey path can reach: the vault file
|
|
73
|
+
* may still hold the ciphertext written under the NEW key, so neither key can
|
|
74
|
+
* be assumed to be the right one — and, critically, **no key material for
|
|
75
|
+
* either may be discarded**. Callers that clean up on failure (e.g. deleting
|
|
76
|
+
* freshly written passphrase metadata) must special-case this error; deleting
|
|
77
|
+
* the new key's material here can leave the vault permanently unopenable.
|
|
78
|
+
*/
|
|
79
|
+
export class VaultRestoreError extends VaultError {
|
|
80
|
+
path;
|
|
81
|
+
attemptedVersion;
|
|
82
|
+
commitCause;
|
|
83
|
+
restoreCause;
|
|
84
|
+
constructor(
|
|
85
|
+
/** Absolute path of the vault whose state is indeterminate. */
|
|
86
|
+
path,
|
|
87
|
+
/** Version the new ciphertext carries, if it is the one on disk. */
|
|
88
|
+
attemptedVersion,
|
|
89
|
+
/** The `commit` failure that triggered the rollback attempt. */
|
|
90
|
+
commitCause,
|
|
91
|
+
/** The failure of the rollback write itself. */
|
|
92
|
+
restoreCause) {
|
|
93
|
+
super("VAULT_RESTORE_FAILED", `Rekey of ${path} could not be completed OR undone. The caller's commit ` +
|
|
94
|
+
`failed (${describeCause(commitCause)}) and restoring the prior ` +
|
|
95
|
+
`ciphertext then failed (${describeCause(restoreCause)}). The vault ` +
|
|
96
|
+
`file may still hold the ciphertext written under the NEW key at ` +
|
|
97
|
+
`version ${attemptedVersion}. Do NOT discard either key: try the new ` +
|
|
98
|
+
`key first, then the old one. If neither opens it, restore a backup of ` +
|
|
99
|
+
`the vault file and its sidecar.`);
|
|
100
|
+
this.path = path;
|
|
101
|
+
this.attemptedVersion = attemptedVersion;
|
|
102
|
+
this.commitCause = commitCause;
|
|
103
|
+
this.restoreCause = restoreCause;
|
|
104
|
+
this.name = "VaultRestoreError";
|
|
105
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function describeCause(cause) {
|
|
109
|
+
return cause instanceof Error ? cause.message : String(cause);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Thrown when a credential key (or a `listCredentials` prefix) does not match
|
|
113
|
+
* the key grammar every vault backend is required to accept.
|
|
114
|
+
*
|
|
115
|
+
* **Why this throws rather than returning a falsy value (#168).** The backends
|
|
116
|
+
* disagree about non-conforming keys: macOS Keychain and the env fallback
|
|
117
|
+
* store anything, while the 1Password, GPG, libsecret and Windows backends
|
|
118
|
+
* cannot address such a key at all. A key that violates the grammar therefore
|
|
119
|
+
* writes successfully on a Mac and is unreadable everywhere else — a silent
|
|
120
|
+
* write/read asymmetry (P2). Returning `false`/`null` would express that as
|
|
121
|
+
* "the write failed" or, worse on the read path, as "no such credential",
|
|
122
|
+
* which is indistinguishable from a genuine miss. The key is malformed, not
|
|
123
|
+
* missing, and only a distinguishable failure says so.
|
|
124
|
+
*/
|
|
125
|
+
export class InvalidCredentialKeyError extends VaultError {
|
|
126
|
+
key;
|
|
127
|
+
operation;
|
|
128
|
+
kind;
|
|
129
|
+
constructor(
|
|
130
|
+
/** The offending key or prefix. A key name, never credential material. */
|
|
131
|
+
key,
|
|
132
|
+
/** The operation that was refused — "read", "write", "delete", "enumerate". */
|
|
133
|
+
operation,
|
|
134
|
+
/** Whether `key` was supplied as a full key or as a `listCredentials` prefix. */
|
|
135
|
+
kind = "key") {
|
|
136
|
+
super("VAULT_INVALID_CREDENTIAL_KEY", `Vault ${operation} refused: credential ${kind} ${JSON.stringify(key)} ` +
|
|
137
|
+
`does not match the required key grammar. Keys must be 2-64 characters ` +
|
|
138
|
+
`of lowercase alphanumerics with '-' or '.' as separators, beginning ` +
|
|
139
|
+
`and ending with an alphanumeric` +
|
|
140
|
+
(kind === "prefix" ? " (a prefix may end with a separator)" : "") +
|
|
141
|
+
`. Non-conforming keys store on some backends and are unaddressable on ` +
|
|
142
|
+
`others, so the value would be written and never read back.`);
|
|
143
|
+
this.key = key;
|
|
144
|
+
this.operation = operation;
|
|
145
|
+
this.kind = kind;
|
|
146
|
+
this.name = "InvalidCredentialKeyError";
|
|
147
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
59
150
|
/** Thrown when the write-path file lock can't be acquired within the timeout. */
|
|
60
151
|
export class VaultLockError extends VaultError {
|
|
61
152
|
constructor(message) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-vault-errors.js","sourceRoot":"","sources":["../../src/vault/session-vault-errors.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"session-vault-errors.js","sourceRoot":"","sources":["../../src/vault/session-vault-errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
*/
|
|
40
40
|
import type { KeychainProviderOptions } from "../key-providers/keychain-provider.js";
|
|
41
41
|
import type { KeyProvider, KeyProviderType } from "../key-providers/types.js";
|
|
42
|
-
import { VaultError, VaultUnlockError, VaultDecryptError, VaultRollbackError, VaultClosedError, VaultLockError } from "./session-vault-errors.js";
|
|
43
|
-
export { VaultError, VaultUnlockError, VaultDecryptError, VaultRollbackError, VaultClosedError, VaultLockError, };
|
|
42
|
+
import { VaultError, VaultUnlockError, VaultDecryptError, VaultRollbackError, VaultClosedError, VaultLockError, VaultRestoreError, InvalidCredentialKeyError } from "./session-vault-errors.js";
|
|
43
|
+
export { VaultError, VaultUnlockError, VaultDecryptError, VaultRollbackError, VaultClosedError, VaultLockError, VaultRestoreError, InvalidCredentialKeyError, };
|
|
44
44
|
/** Current payload schema version — bump requires a compat migration. */
|
|
45
45
|
export declare const VAULT_SCHEMA_VERSION = 1;
|
|
46
46
|
/** Default vault file location — same path the CLI uses, so they share state. */
|
|
@@ -123,6 +123,29 @@ export interface OpenVaultOptions {
|
|
|
123
123
|
*/
|
|
124
124
|
keychain?: KeychainProviderOptions;
|
|
125
125
|
}
|
|
126
|
+
/** Options for {@link createVault}. */
|
|
127
|
+
export interface CreateVaultOptions {
|
|
128
|
+
/** Alternate vault file path. Defaults to the same path the CLI uses. */
|
|
129
|
+
path?: string;
|
|
130
|
+
/** Alternate sidecar path. Defaults to vault directory + `vault.seen-version`. */
|
|
131
|
+
sidecarPath?: string;
|
|
132
|
+
/**
|
|
133
|
+
* Caller-owned AES-256 key used to encrypt the empty vault.
|
|
134
|
+
*
|
|
135
|
+
* This function neither stores nor zeroes the key. Key-provider setup is a
|
|
136
|
+
* separate, potentially irreversible transaction; keeping it outside this
|
|
137
|
+
* filesystem primitive prevents a failed create from deleting or replacing
|
|
138
|
+
* a key that another vault may share.
|
|
139
|
+
*/
|
|
140
|
+
key: Buffer;
|
|
141
|
+
}
|
|
142
|
+
/** Result of an idempotent {@link createVault} call. */
|
|
143
|
+
export interface CreateVaultResult {
|
|
144
|
+
/** True when this call created the vault/sidecar pair; false when both existed. */
|
|
145
|
+
created: boolean;
|
|
146
|
+
/** Canonical absolute vault path used for AAD binding. */
|
|
147
|
+
path: string;
|
|
148
|
+
}
|
|
126
149
|
/**
|
|
127
150
|
* A long-lived handle to an unlocked vault. Construct with {@link openVault};
|
|
128
151
|
* close with {@link SessionVault.close}. Operations are async so policy
|
|
@@ -148,6 +171,26 @@ export interface SessionVault {
|
|
|
148
171
|
/** Diagnostic — the current in-memory vault version. */
|
|
149
172
|
readonly vaultVersion: number;
|
|
150
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Create an empty, AAD-bound session vault and its rollback sidecar.
|
|
176
|
+
*
|
|
177
|
+
* Creation is idempotent only for a complete pair: if both files already
|
|
178
|
+
* exist they are left byte-for-byte untouched and `created` is false. A
|
|
179
|
+
* vault-only or sidecar-only state is refused rather than "repaired" because
|
|
180
|
+
* replacing either half could erase rollback evidence or make existing
|
|
181
|
+
* ciphertext permanently unreadable.
|
|
182
|
+
*
|
|
183
|
+
* Both destinations use no-clobber publication: the completed vault temp is
|
|
184
|
+
* linked into place first, then the sidecar is the logical commit point. A
|
|
185
|
+
* successful return therefore means both public paths exist, and a competing
|
|
186
|
+
* destination is never replaced. This is temp-file/link crash safety, not an
|
|
187
|
+
* fsync or power-loss durability guarantee.
|
|
188
|
+
*
|
|
189
|
+
* The caller retains ownership of `opts.key`; this function snapshots it for
|
|
190
|
+
* the duration of the call, zeroes that private copy, and never mutates,
|
|
191
|
+
* zeroes, or stores the caller's buffer.
|
|
192
|
+
*/
|
|
193
|
+
export declare function createVault(opts: CreateVaultOptions): Promise<CreateVaultResult>;
|
|
151
194
|
/**
|
|
152
195
|
* Open an encrypted session vault.
|
|
153
196
|
*
|
|
@@ -174,4 +217,104 @@ export interface SessionVault {
|
|
|
174
217
|
* ```
|
|
175
218
|
*/
|
|
176
219
|
export declare function openVault(opts?: OpenVaultOptions): Promise<SessionVault>;
|
|
220
|
+
/** Options for {@link rekeyVault}. */
|
|
221
|
+
export interface RekeyVaultOptions {
|
|
222
|
+
/** Vault file path. Defaults to the same path the CLI uses. */
|
|
223
|
+
path?: string;
|
|
224
|
+
/** Sidecar path. Defaults to vault directory + `vault.seen-version`. */
|
|
225
|
+
sidecarPath?: string;
|
|
226
|
+
/** The key the vault is currently encrypted under. */
|
|
227
|
+
currentKey: Buffer;
|
|
228
|
+
/** The key the vault should be re-encrypted under. */
|
|
229
|
+
nextKey: Buffer;
|
|
230
|
+
/**
|
|
231
|
+
* Opt-in acceptance of a detected rollback (sidecar version > vault
|
|
232
|
+
* version), mirroring {@link OpenVaultOptions.acceptRollback}. Unlike
|
|
233
|
+
* `openVault`, the sidecar is never lowered to match — the rekeyed vault
|
|
234
|
+
* lands above the recorded high-water mark either way, so accepting a
|
|
235
|
+
* rollback here does not also discard the mark.
|
|
236
|
+
*/
|
|
237
|
+
acceptRollback?: boolean;
|
|
238
|
+
/**
|
|
239
|
+
* Opt-in acceptance of a missing sidecar, mirroring
|
|
240
|
+
* {@link OpenVaultOptions.acceptMissingSidecar}. Legacy (pre-`openVault`,
|
|
241
|
+
* AAD-less) vaults never had a sidecar by construction and are exempt.
|
|
242
|
+
*/
|
|
243
|
+
acceptMissingSidecar?: boolean;
|
|
244
|
+
/**
|
|
245
|
+
* Invoked once the re-encrypted vault has landed on disk, while the write
|
|
246
|
+
* lock is still held and before the sidecar high-water mark is published.
|
|
247
|
+
*
|
|
248
|
+
* This is the seam that makes an *external* commitment part of the same
|
|
249
|
+
* all-or-nothing step. `migrate --to passphrase` uses it to write
|
|
250
|
+
* `secrets.provider` — a config that names a provider which cannot open the
|
|
251
|
+
* vault (or a vault no configured provider can open) is exactly the
|
|
252
|
+
* half-migrated state this exists to prevent. Throwing restores the prior
|
|
253
|
+
* ciphertext byte-for-byte and rethrows; the sidecar is never advanced on
|
|
254
|
+
* that path, so the restored vault opens cleanly.
|
|
255
|
+
*/
|
|
256
|
+
commit?: () => void | Promise<void>;
|
|
257
|
+
}
|
|
258
|
+
/** Outcome of a successful {@link rekeyVault}. */
|
|
259
|
+
export interface RekeyVaultResult {
|
|
260
|
+
/** Number of secrets carried across to the new ciphertext. */
|
|
261
|
+
secretCount: number;
|
|
262
|
+
/** The vault version after the rekey. */
|
|
263
|
+
vaultVersion: number;
|
|
264
|
+
/** True when the source vault was in the pre-`openVault` AAD-less shape. */
|
|
265
|
+
upgradedFromLegacy: boolean;
|
|
266
|
+
/**
|
|
267
|
+
* False when the rekey committed but the sidecar version bump could not be
|
|
268
|
+
* written (warned on stderr). The rekey still succeeded — see the note on
|
|
269
|
+
* the sidecar write in {@link rekeyVault} — but rollback protection is
|
|
270
|
+
* degraded until the next successful vault write catches the sidecar up.
|
|
271
|
+
*/
|
|
272
|
+
sidecarPublished: boolean;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Re-encrypt a vault in place under a different master key.
|
|
276
|
+
*
|
|
277
|
+
* Needed whenever the new key cannot be *chosen* — a `PassphraseProvider`
|
|
278
|
+
* derives its key from what the operator types and cannot store a
|
|
279
|
+
* caller-supplied one, so moving a vault to it means rewriting the ciphertext
|
|
280
|
+
* rather than copying the key to a new home. Provider-to-provider moves that
|
|
281
|
+
* only relocate the same key do not need this.
|
|
282
|
+
*
|
|
283
|
+
* The AAD binds the vault's resolved real path and schema, not the key, so the
|
|
284
|
+
* rekeyed ciphertext stays bound to the same file identity. Legacy AAD-less
|
|
285
|
+
* vaults are upgraded to schema 1 in the same step. The new version is one
|
|
286
|
+
* above both the payload's version and the sidecar's high-water mark, so a
|
|
287
|
+
* rekeyed vault can never land below a version already seen.
|
|
288
|
+
*
|
|
289
|
+
* Any process holding this vault open under the old key will fail its next
|
|
290
|
+
* decrypt with {@link VaultDecryptError} — an honest failure, by design: the
|
|
291
|
+
* old key genuinely no longer opens this vault.
|
|
292
|
+
*
|
|
293
|
+
* **Post-commit guarantee.** With one named exception, a throw means `commit`
|
|
294
|
+
* did not succeed and the prior ciphertext is back in place — nothing after a
|
|
295
|
+
* successful `commit` can throw, because the only step that follows it (the
|
|
296
|
+
* sidecar bump) is reported via {@link RekeyVaultResult.sidecarPublished}
|
|
297
|
+
* rather than thrown. Callers may therefore treat a throw as "nothing was
|
|
298
|
+
* committed" and clean up accordingly; that is what makes it safe for the CLI
|
|
299
|
+
* to delete freshly written passphrase metadata on failure.
|
|
300
|
+
*
|
|
301
|
+
* **The exception: {@link VaultRestoreError}.** If `commit` throws *and* the
|
|
302
|
+
* rollback write then fails, the file on disk may still be the new ciphertext.
|
|
303
|
+
* That state is neither committed nor undone, so it gets its own error type —
|
|
304
|
+
* callers MUST special-case it and must NOT discard key material for either
|
|
305
|
+
* key. Every other throw keeps the plain guarantee.
|
|
306
|
+
*
|
|
307
|
+
* @param opts - {@link RekeyVaultOptions}. `currentKey`/`nextKey` are required;
|
|
308
|
+
* both remain owned by the caller and are never zeroed here.
|
|
309
|
+
* @returns {@link RekeyVaultResult}.
|
|
310
|
+
* @throws {@link VaultError} `VAULT_NOT_FOUND` when the vault file is absent.
|
|
311
|
+
* @throws {@link VaultDecryptError} when `currentKey` does not open the vault.
|
|
312
|
+
* @throws {@link VaultError} `VAULT_SIDECAR_MISSING` when the sidecar is absent
|
|
313
|
+
* and neither `acceptMissingSidecar` nor the legacy exemption applies.
|
|
314
|
+
* @throws {@link VaultRollbackError} when the vault version trails the sidecar
|
|
315
|
+
* and `acceptRollback` is not set.
|
|
316
|
+
* @throws {@link VaultError} `VAULT_ENCRYPT_FAILED` when re-encryption fails.
|
|
317
|
+
* @throws Whatever `commit` throws, after restoring the prior ciphertext.
|
|
318
|
+
*/
|
|
319
|
+
export declare function rekeyVault(opts: RekeyVaultOptions): Promise<RekeyVaultResult>;
|
|
177
320
|
//# sourceMappingURL=session-vault.d.ts.map
|