@centient/secrets 0.5.0 → 0.7.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 +39 -1
- 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/hidden-prompt.d.ts +48 -0
- package/dist/cli/hidden-prompt.d.ts.map +1 -0
- package/dist/cli/hidden-prompt.js +127 -0
- package/dist/cli/hidden-prompt.js.map +1 -0
- package/dist/cli/secrets-cli.d.ts +9 -1
- package/dist/cli/secrets-cli.d.ts.map +1 -1
- package/dist/cli/secrets-cli.js +272 -175
- 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 +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/key-providers/index.d.ts +4 -1
- package/dist/key-providers/index.d.ts.map +1 -1
- package/dist/key-providers/index.js +1 -0
- package/dist/key-providers/index.js.map +1 -1
- package/dist/key-providers/passphrase-provider.d.ts +79 -0
- package/dist/key-providers/passphrase-provider.d.ts.map +1 -0
- package/dist/key-providers/passphrase-provider.js +298 -0
- package/dist/key-providers/passphrase-provider.js.map +1 -0
- package/dist/key-providers/resolve.d.ts +8 -3
- package/dist/key-providers/resolve.d.ts.map +1 -1
- package/dist/key-providers/resolve.js +34 -10
- package/dist/key-providers/resolve.js.map +1 -1
- package/dist/key-providers/types.d.ts +20 -2
- package/dist/key-providers/types.d.ts.map +1 -1
- package/dist/key-providers/types.js +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 +669 -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
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Key Provider — Passphrase
|
|
3
|
+
*
|
|
4
|
+
* Derives the vault encryption key from a human-entered passphrase using a
|
|
5
|
+
* per-vault salt. The passphrase and derived key are never persisted; only
|
|
6
|
+
* non-secret KDF metadata plus a verifier are written beside the vault.
|
|
7
|
+
*/
|
|
8
|
+
import { mkdirSync, readFileSync, rmSync, writeFileSync, } from "node:fs";
|
|
9
|
+
import { dirname, basename, join } from "node:path";
|
|
10
|
+
import { createHmac, randomBytes, scryptSync, timingSafeEqual } from "node:crypto";
|
|
11
|
+
import { homedir } from "node:os";
|
|
12
|
+
import { promptHiddenSync } from "../cli/hidden-prompt.js";
|
|
13
|
+
// =============================================================================
|
|
14
|
+
// Constants
|
|
15
|
+
// =============================================================================
|
|
16
|
+
export const PASSPHRASE_METADATA_VERSION = 1;
|
|
17
|
+
export const PASSPHRASE_KDF = "scrypt";
|
|
18
|
+
export const PASSPHRASE_VERIFIER_ALGORITHM = "hmac-sha256";
|
|
19
|
+
export const PASSPHRASE_VERIFIER_CONTEXT = "centient-secrets-passphrase:v1";
|
|
20
|
+
export const PASSPHRASE_SALT_LENGTH = 32;
|
|
21
|
+
export const DEFAULT_PASSPHRASE_VAULT_PATH = join(homedir(), ".centient", "secrets", "vault.enc");
|
|
22
|
+
export const DEFAULT_PASSPHRASE_KDF_PARAMS = {
|
|
23
|
+
N: 2 ** 17,
|
|
24
|
+
r: 8,
|
|
25
|
+
p: 1,
|
|
26
|
+
keyLength: 32,
|
|
27
|
+
maxmem: 192 * 1024 * 1024,
|
|
28
|
+
};
|
|
29
|
+
// =============================================================================
|
|
30
|
+
// Path helpers
|
|
31
|
+
// =============================================================================
|
|
32
|
+
export function passphraseMetadataPathForVault(vaultPath) {
|
|
33
|
+
const name = basename(vaultPath);
|
|
34
|
+
const metadataName = name.endsWith(".enc")
|
|
35
|
+
? `${name.slice(0, -".enc".length)}.passphrase.json`
|
|
36
|
+
: `${name}.passphrase.json`;
|
|
37
|
+
return join(dirname(vaultPath), metadataName);
|
|
38
|
+
}
|
|
39
|
+
// =============================================================================
|
|
40
|
+
// Implementation
|
|
41
|
+
// =============================================================================
|
|
42
|
+
export class PassphraseProvider {
|
|
43
|
+
name = "passphrase";
|
|
44
|
+
metadataPath;
|
|
45
|
+
prompt;
|
|
46
|
+
setupParams;
|
|
47
|
+
lastError = null;
|
|
48
|
+
constructor(options = {}) {
|
|
49
|
+
this.metadataPath =
|
|
50
|
+
options.metadataPath ??
|
|
51
|
+
passphraseMetadataPathForVault(options.vaultPath ?? DEFAULT_PASSPHRASE_VAULT_PATH);
|
|
52
|
+
this.prompt = options.prompt ?? promptHiddenSync;
|
|
53
|
+
this.setupParams = options.params ?? DEFAULT_PASSPHRASE_KDF_PARAMS;
|
|
54
|
+
}
|
|
55
|
+
/** Returns true when a passphrase can be requested from this process. */
|
|
56
|
+
static detect() {
|
|
57
|
+
return process.stdin.isTTY === true;
|
|
58
|
+
}
|
|
59
|
+
get metadataFile() {
|
|
60
|
+
return this.metadataPath;
|
|
61
|
+
}
|
|
62
|
+
getLastError() {
|
|
63
|
+
return this.lastError;
|
|
64
|
+
}
|
|
65
|
+
getKey() {
|
|
66
|
+
this.clearLastError();
|
|
67
|
+
const metadata = this.readMetadata();
|
|
68
|
+
if (metadata === null)
|
|
69
|
+
return null;
|
|
70
|
+
const passphrase = this.promptForPassphrase("Vault passphrase: ");
|
|
71
|
+
if (passphrase === null)
|
|
72
|
+
return null;
|
|
73
|
+
const salt = Buffer.from(metadata.salt, "base64");
|
|
74
|
+
const expectedVerifier = Buffer.from(metadata.verifier.value, "base64");
|
|
75
|
+
let key;
|
|
76
|
+
try {
|
|
77
|
+
key = derivePassphraseKey(passphrase, salt, metadata.params);
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
this.setLastError("KDF_FAILED", `Failed to derive passphrase key: ${err instanceof Error ? err.message : String(err)}`);
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
if (!verifyKey(key, expectedVerifier)) {
|
|
84
|
+
key.fill(0);
|
|
85
|
+
this.setLastError("PASSPHRASE_VERIFICATION_FAILED", "Passphrase verification failed. Check the passphrase for this vault and try again.");
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
return key;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Passphrase providers do not store caller-supplied random keys.
|
|
92
|
+
* Use setupKey() so the vault key is derived from the passphrase.
|
|
93
|
+
*/
|
|
94
|
+
storeKey(_key) {
|
|
95
|
+
this.setLastError("UNSUPPORTED_OPERATION", "Passphrase provider derives the vault key from a typed passphrase and cannot store a caller-supplied key. Use setupKey() during vault initialization.");
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Initialize passphrase metadata and return the derived key for bootstrap
|
|
100
|
+
* encryption. Only salt, KDF params, and verifier are persisted.
|
|
101
|
+
*/
|
|
102
|
+
setupKey() {
|
|
103
|
+
this.clearLastError();
|
|
104
|
+
const passphrase = this.promptForPassphrase("Create vault passphrase: ");
|
|
105
|
+
if (passphrase === null)
|
|
106
|
+
return null;
|
|
107
|
+
if (passphrase.length === 0) {
|
|
108
|
+
this.setLastError("EMPTY_PASSPHRASE", "Passphrase cannot be empty.");
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
const confirmation = this.promptForPassphrase("Confirm vault passphrase: ");
|
|
112
|
+
if (confirmation === null)
|
|
113
|
+
return null;
|
|
114
|
+
if (passphrase !== confirmation) {
|
|
115
|
+
this.setLastError("PASSPHRASE_MISMATCH", "Passphrases did not match; passphrase metadata was not written.");
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
const salt = randomBytes(PASSPHRASE_SALT_LENGTH);
|
|
119
|
+
let key;
|
|
120
|
+
try {
|
|
121
|
+
key = derivePassphraseKey(passphrase, salt, this.setupParams);
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
this.setLastError("KDF_FAILED", `Failed to derive passphrase key: ${err instanceof Error ? err.message : String(err)}`);
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
const metadata = {
|
|
128
|
+
version: PASSPHRASE_METADATA_VERSION,
|
|
129
|
+
kdf: PASSPHRASE_KDF,
|
|
130
|
+
params: { ...this.setupParams },
|
|
131
|
+
salt: salt.toString("base64"),
|
|
132
|
+
verifier: {
|
|
133
|
+
algorithm: PASSPHRASE_VERIFIER_ALGORITHM,
|
|
134
|
+
value: createVerifier(key).toString("base64"),
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
try {
|
|
138
|
+
mkdirSync(dirname(this.metadataPath), { recursive: true, mode: 0o700 });
|
|
139
|
+
writeFileSync(this.metadataPath, JSON.stringify(metadata, null, 2) + "\n", { encoding: "utf8", mode: 0o600 });
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
key.fill(0);
|
|
143
|
+
this.setLastError("METADATA_WRITE_FAILED", `Failed to write passphrase metadata at ${this.metadataPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
return key;
|
|
147
|
+
}
|
|
148
|
+
deleteKey() {
|
|
149
|
+
this.clearLastError();
|
|
150
|
+
try {
|
|
151
|
+
rmSync(this.metadataPath, { force: true });
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
this.setLastError("METADATA_DELETE_FAILED", `Failed to delete passphrase metadata at ${this.metadataPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
readMetadata() {
|
|
160
|
+
let parsed;
|
|
161
|
+
try {
|
|
162
|
+
parsed = JSON.parse(readFileSync(this.metadataPath, "utf8"));
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
const notFound = isNotFoundError(err);
|
|
166
|
+
this.setLastError(notFound ? "METADATA_NOT_FOUND" : "METADATA_READ_FAILED", notFound
|
|
167
|
+
? `Passphrase provider is not initialized for this vault. Run \`centient secrets init\` from an interactive terminal, or configure another secrets.provider.`
|
|
168
|
+
: `Failed to read passphrase metadata at ${this.metadataPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
const metadata = validateMetadata(parsed);
|
|
172
|
+
if (metadata === null) {
|
|
173
|
+
this.setLastError("METADATA_INVALID", `Passphrase metadata at ${this.metadataPath} is invalid or unsupported.`);
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
return metadata;
|
|
177
|
+
}
|
|
178
|
+
promptForPassphrase(message) {
|
|
179
|
+
try {
|
|
180
|
+
const value = this.prompt(message);
|
|
181
|
+
if (value === null) {
|
|
182
|
+
this.setLastError("PASSPHRASE_INTERRUPTED", "Passphrase entry was interrupted.");
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
return value;
|
|
186
|
+
}
|
|
187
|
+
catch (err) {
|
|
188
|
+
this.setLastError("PASSPHRASE_PROMPT_FAILED", err instanceof Error ? err.message : String(err));
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
clearLastError() {
|
|
193
|
+
this.lastError = null;
|
|
194
|
+
}
|
|
195
|
+
setLastError(code, message) {
|
|
196
|
+
this.lastError = { code, message };
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// =============================================================================
|
|
200
|
+
// Crypto helpers
|
|
201
|
+
// =============================================================================
|
|
202
|
+
export function derivePassphraseKey(passphrase, salt, params = DEFAULT_PASSPHRASE_KDF_PARAMS) {
|
|
203
|
+
return scryptSync(passphrase, salt, params.keyLength, {
|
|
204
|
+
N: params.N,
|
|
205
|
+
r: params.r,
|
|
206
|
+
p: params.p,
|
|
207
|
+
maxmem: params.maxmem,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
function createVerifier(key) {
|
|
211
|
+
return createHmac("sha256", key)
|
|
212
|
+
.update(PASSPHRASE_VERIFIER_CONTEXT, "utf8")
|
|
213
|
+
.digest();
|
|
214
|
+
}
|
|
215
|
+
function verifyKey(key, expectedVerifier) {
|
|
216
|
+
const actualVerifier = createVerifier(key);
|
|
217
|
+
try {
|
|
218
|
+
return (actualVerifier.length === expectedVerifier.length &&
|
|
219
|
+
timingSafeEqual(actualVerifier, expectedVerifier));
|
|
220
|
+
}
|
|
221
|
+
finally {
|
|
222
|
+
actualVerifier.fill(0);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function validateMetadata(value) {
|
|
226
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
const record = value;
|
|
230
|
+
if (record.version !== PASSPHRASE_METADATA_VERSION ||
|
|
231
|
+
record.kdf !== PASSPHRASE_KDF ||
|
|
232
|
+
typeof record.salt !== "string") {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
const params = validateParams(record.params);
|
|
236
|
+
if (params === null)
|
|
237
|
+
return null;
|
|
238
|
+
const verifierRaw = record.verifier;
|
|
239
|
+
if (typeof verifierRaw !== "object" ||
|
|
240
|
+
verifierRaw === null ||
|
|
241
|
+
Array.isArray(verifierRaw)) {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
const verifier = verifierRaw;
|
|
245
|
+
if (verifier.algorithm !== PASSPHRASE_VERIFIER_ALGORITHM ||
|
|
246
|
+
typeof verifier.value !== "string") {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
const salt = Buffer.from(record.salt, "base64");
|
|
250
|
+
const verifierValue = Buffer.from(verifier.value, "base64");
|
|
251
|
+
if (salt.length < 16 || verifierValue.length !== 32)
|
|
252
|
+
return null;
|
|
253
|
+
return {
|
|
254
|
+
version: PASSPHRASE_METADATA_VERSION,
|
|
255
|
+
kdf: PASSPHRASE_KDF,
|
|
256
|
+
params,
|
|
257
|
+
salt: record.salt,
|
|
258
|
+
verifier: {
|
|
259
|
+
algorithm: PASSPHRASE_VERIFIER_ALGORITHM,
|
|
260
|
+
value: verifier.value,
|
|
261
|
+
},
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
function validateParams(value) {
|
|
265
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
const record = value;
|
|
269
|
+
const params = {
|
|
270
|
+
N: Number(record.N),
|
|
271
|
+
r: Number(record.r),
|
|
272
|
+
p: Number(record.p),
|
|
273
|
+
keyLength: Number(record.keyLength),
|
|
274
|
+
maxmem: Number(record.maxmem),
|
|
275
|
+
};
|
|
276
|
+
if (!Number.isSafeInteger(params.N) ||
|
|
277
|
+
!Number.isSafeInteger(params.r) ||
|
|
278
|
+
!Number.isSafeInteger(params.p) ||
|
|
279
|
+
!Number.isSafeInteger(params.keyLength) ||
|
|
280
|
+
!Number.isSafeInteger(params.maxmem) ||
|
|
281
|
+
params.N <= 1 ||
|
|
282
|
+
!isPowerOfTwo(params.N) ||
|
|
283
|
+
params.r <= 0 ||
|
|
284
|
+
params.p <= 0 ||
|
|
285
|
+
params.keyLength !== 32 ||
|
|
286
|
+
params.maxmem <= 0) {
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
return params;
|
|
290
|
+
}
|
|
291
|
+
function isPowerOfTwo(value) {
|
|
292
|
+
return value >= 2 && Math.log2(value) % 1 === 0;
|
|
293
|
+
}
|
|
294
|
+
function isNotFoundError(err) {
|
|
295
|
+
return (err instanceof Error &&
|
|
296
|
+
err.code === "ENOENT");
|
|
297
|
+
}
|
|
298
|
+
//# sourceMappingURL=passphrase-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"passphrase-provider.js","sourceRoot":"","sources":["../../src/key-providers/passphrase-provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,9 +7,14 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Resolution order:
|
|
9
9
|
* 1. Explicit config (secrets.provider field) — use it; fail if unavailable
|
|
10
|
-
* 2. Auto-detection — 1Password if `op` is available, else Keychain on macOS
|
|
10
|
+
* 2. Auto-detection — 1Password if `op` is available, else Keychain on macOS,
|
|
11
|
+
* else passphrase when an interactive TTY is available
|
|
11
12
|
*/
|
|
12
13
|
import type { KeyProvider, KeyProviderType, CentientConfig, SecretsConfig } from "./types.js";
|
|
14
|
+
export interface ResolveKeyProviderOptions {
|
|
15
|
+
/** Vault path used by providers with per-vault metadata. */
|
|
16
|
+
vaultPath?: string;
|
|
17
|
+
}
|
|
13
18
|
/**
|
|
14
19
|
* Load the global centient config file.
|
|
15
20
|
* Returns an empty object if the file doesn't exist or is malformed.
|
|
@@ -27,7 +32,7 @@ export declare function saveSecretsConfig(secrets: SecretsConfig): boolean;
|
|
|
27
32
|
* @returns An object with the resolved provider, or an error message
|
|
28
33
|
* if the explicitly configured provider is unavailable.
|
|
29
34
|
*/
|
|
30
|
-
export declare function resolveKeyProvider(): {
|
|
35
|
+
export declare function resolveKeyProvider(options?: ResolveKeyProviderOptions): {
|
|
31
36
|
ok: true;
|
|
32
37
|
provider: KeyProvider;
|
|
33
38
|
method: "config" | "auto";
|
|
@@ -42,5 +47,5 @@ export declare function resolveKeyProvider(): {
|
|
|
42
47
|
* Get a specific provider by type, regardless of config.
|
|
43
48
|
* Used by the migrate command to construct source/target providers.
|
|
44
49
|
*/
|
|
45
|
-
export declare function getProviderByType(type: KeyProviderType, config?: SecretsConfig): KeyProvider | null;
|
|
50
|
+
export declare function getProviderByType(type: KeyProviderType, config?: SecretsConfig, options?: ResolveKeyProviderOptions): KeyProvider | null;
|
|
46
51
|
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/key-providers/resolve.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/key-providers/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAQH,OAAO,KAAK,EACV,WAAW,EACX,eAAe,EACf,cAAc,EACd,aAAa,EACd,MAAM,YAAY,CAAC;AASpB,MAAM,WAAW,yBAAyB;IACxC,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAMD;;;GAGG;AACH,wBAAgB,UAAU,IAAI,cAAc,CAY3C;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAWjE;AAMD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,yBAA8B,GAAG;IAC3E,EAAE,EAAE,IAAI,CAAC;IACT,QAAQ,EAAE,WAAW,CAAC;IACtB,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC;CAC3B,GAAG;IACF,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1C,CAWA;AA6GD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,eAAe,EACrB,MAAM,CAAC,EAAE,aAAa,EACtB,OAAO,GAAE,yBAA8B,GACtC,WAAW,GAAG,IAAI,CAepB"}
|
|
@@ -7,17 +7,20 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Resolution order:
|
|
9
9
|
* 1. Explicit config (secrets.provider field) — use it; fail if unavailable
|
|
10
|
-
* 2. Auto-detection — 1Password if `op` is available, else Keychain on macOS
|
|
10
|
+
* 2. Auto-detection — 1Password if `op` is available, else Keychain on macOS,
|
|
11
|
+
* else passphrase when an interactive TTY is available
|
|
11
12
|
*/
|
|
12
13
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
13
14
|
import { join } from "path";
|
|
14
15
|
import { homedir } from "os";
|
|
15
16
|
import { KeychainProvider } from "./keychain-provider.js";
|
|
16
17
|
import { OnePasswordProvider } from "./onepassword-provider.js";
|
|
18
|
+
import { PassphraseProvider } from "./passphrase-provider.js";
|
|
17
19
|
// =============================================================================
|
|
18
20
|
// Constants
|
|
19
21
|
// =============================================================================
|
|
20
22
|
const CONFIG_PATH = join(homedir(), ".centient", "config.json");
|
|
23
|
+
const SUPPORTED_PROVIDERS = "keychain, 1password, passphrase";
|
|
21
24
|
// =============================================================================
|
|
22
25
|
// Config I/O
|
|
23
26
|
// =============================================================================
|
|
@@ -67,21 +70,21 @@ export function saveSecretsConfig(secrets) {
|
|
|
67
70
|
* @returns An object with the resolved provider, or an error message
|
|
68
71
|
* if the explicitly configured provider is unavailable.
|
|
69
72
|
*/
|
|
70
|
-
export function resolveKeyProvider() {
|
|
73
|
+
export function resolveKeyProvider(options = {}) {
|
|
71
74
|
const config = loadConfig();
|
|
72
75
|
const secretsConfig = config.secrets;
|
|
73
76
|
// Explicit config — honor it strictly
|
|
74
77
|
if (secretsConfig?.provider) {
|
|
75
|
-
return resolveExplicit(secretsConfig.provider, secretsConfig);
|
|
78
|
+
return resolveExplicit(secretsConfig.provider, secretsConfig, options);
|
|
76
79
|
}
|
|
77
80
|
// Auto-detection fallback
|
|
78
|
-
return resolveAuto(secretsConfig);
|
|
81
|
+
return resolveAuto(secretsConfig, options);
|
|
79
82
|
}
|
|
80
83
|
/**
|
|
81
84
|
* Resolve an explicitly configured provider.
|
|
82
85
|
* Fails if the provider is not available on this system.
|
|
83
86
|
*/
|
|
84
|
-
function resolveExplicit(type, config) {
|
|
87
|
+
function resolveExplicit(type, config, options = {}) {
|
|
85
88
|
switch (type) {
|
|
86
89
|
case "keychain": {
|
|
87
90
|
if (!KeychainProvider.detect()) {
|
|
@@ -115,12 +118,19 @@ function resolveExplicit(type, config) {
|
|
|
115
118
|
method: "config",
|
|
116
119
|
};
|
|
117
120
|
}
|
|
121
|
+
case "passphrase": {
|
|
122
|
+
return {
|
|
123
|
+
ok: true,
|
|
124
|
+
provider: new PassphraseProvider({ vaultPath: options.vaultPath }),
|
|
125
|
+
method: "config",
|
|
126
|
+
};
|
|
127
|
+
}
|
|
118
128
|
default: {
|
|
119
129
|
return {
|
|
120
130
|
ok: false,
|
|
121
131
|
error: {
|
|
122
132
|
code: "UNKNOWN_PROVIDER",
|
|
123
|
-
message: `Unknown key provider "${type}". Supported:
|
|
133
|
+
message: `Unknown key provider "${type}". Supported: ${SUPPORTED_PROVIDERS}.`,
|
|
124
134
|
},
|
|
125
135
|
};
|
|
126
136
|
}
|
|
@@ -128,9 +138,10 @@ function resolveExplicit(type, config) {
|
|
|
128
138
|
}
|
|
129
139
|
/**
|
|
130
140
|
* Auto-detect the best available provider.
|
|
131
|
-
* Prefers 1Password (enables headless), falls back to Keychain on macOS
|
|
141
|
+
* Prefers 1Password (enables headless), falls back to Keychain on macOS,
|
|
142
|
+
* then passphrase for interactive headless/Linux operators.
|
|
132
143
|
*/
|
|
133
|
-
function resolveAuto(config) {
|
|
144
|
+
function resolveAuto(config, options = {}) {
|
|
134
145
|
// Prefer 1Password if available (enables headless use case)
|
|
135
146
|
if (OnePasswordProvider.detect()) {
|
|
136
147
|
return {
|
|
@@ -143,13 +154,22 @@ function resolveAuto(config) {
|
|
|
143
154
|
if (KeychainProvider.detect()) {
|
|
144
155
|
return { ok: true, provider: new KeychainProvider(), method: "auto" };
|
|
145
156
|
}
|
|
157
|
+
// Interactive fallback for headless/Linux hosts without OS key storage.
|
|
158
|
+
if (PassphraseProvider.detect()) {
|
|
159
|
+
return {
|
|
160
|
+
ok: true,
|
|
161
|
+
provider: new PassphraseProvider({ vaultPath: options.vaultPath }),
|
|
162
|
+
method: "auto",
|
|
163
|
+
};
|
|
164
|
+
}
|
|
146
165
|
return {
|
|
147
166
|
ok: false,
|
|
148
167
|
error: {
|
|
149
168
|
code: "NO_PROVIDER",
|
|
150
169
|
message: "No key provider available. " +
|
|
151
170
|
"Install the 1Password CLI (`op`) for headless support, " +
|
|
152
|
-
"
|
|
171
|
+
"run on macOS for Keychain support, " +
|
|
172
|
+
"or run from an interactive terminal to use the passphrase provider.",
|
|
153
173
|
},
|
|
154
174
|
};
|
|
155
175
|
}
|
|
@@ -157,7 +177,7 @@ function resolveAuto(config) {
|
|
|
157
177
|
* Get a specific provider by type, regardless of config.
|
|
158
178
|
* Used by the migrate command to construct source/target providers.
|
|
159
179
|
*/
|
|
160
|
-
export function getProviderByType(type, config) {
|
|
180
|
+
export function getProviderByType(type, config, options = {}) {
|
|
161
181
|
switch (type) {
|
|
162
182
|
case "keychain":
|
|
163
183
|
return KeychainProvider.detect() ? new KeychainProvider() : null;
|
|
@@ -165,6 +185,10 @@ export function getProviderByType(type, config) {
|
|
|
165
185
|
return OnePasswordProvider.detect()
|
|
166
186
|
? new OnePasswordProvider(config?.onePassword)
|
|
167
187
|
: null;
|
|
188
|
+
case "passphrase":
|
|
189
|
+
return PassphraseProvider.detect()
|
|
190
|
+
? new PassphraseProvider({ vaultPath: options.vaultPath })
|
|
191
|
+
: null;
|
|
168
192
|
default:
|
|
169
193
|
return null;
|
|
170
194
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/key-providers/resolve.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/key-providers/resolve.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,13 +3,18 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Abstracts vault encryption key storage behind a provider interface.
|
|
5
5
|
* Implementations retrieve/store the 32-byte AES-256-GCM key from
|
|
6
|
-
* different backends (macOS Keychain, 1Password, etc.).
|
|
6
|
+
* different backends (macOS Keychain, 1Password, passphrase KDF, etc.).
|
|
7
7
|
*
|
|
8
8
|
* All methods return null/false on failure — never throw (consistent
|
|
9
9
|
* with vault-common.ts conventions).
|
|
10
10
|
*/
|
|
11
11
|
/** Supported key provider backends. */
|
|
12
|
-
export type KeyProviderType = "keychain" | "1password";
|
|
12
|
+
export type KeyProviderType = "keychain" | "1password" | "passphrase";
|
|
13
|
+
/** Optional diagnostic exposed after a provider returns null/false. */
|
|
14
|
+
export interface KeyProviderError {
|
|
15
|
+
code: string;
|
|
16
|
+
message: string;
|
|
17
|
+
}
|
|
13
18
|
/**
|
|
14
19
|
* Interface for vault encryption key storage providers.
|
|
15
20
|
*
|
|
@@ -34,12 +39,25 @@ export interface KeyProvider {
|
|
|
34
39
|
* @returns true on success, false on failure.
|
|
35
40
|
*/
|
|
36
41
|
storeKey(key: Buffer): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Optional provider-specific setup that returns the key to use for vault
|
|
44
|
+
* bootstrap encryption. Providers that derive keys rather than store
|
|
45
|
+
* caller-supplied random keys should implement this instead of storing key.
|
|
46
|
+
*/
|
|
47
|
+
setupKey?(): Buffer | null;
|
|
37
48
|
/**
|
|
38
49
|
* Delete the stored vault encryption key.
|
|
39
50
|
*
|
|
40
51
|
* @returns true if deleted (or did not exist), false on unexpected failure.
|
|
41
52
|
*/
|
|
42
53
|
deleteKey(): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Optional diagnostic for the most recent null/false result.
|
|
56
|
+
*
|
|
57
|
+
* This preserves the non-throwing provider contract while allowing callers
|
|
58
|
+
* to surface clear auth failures such as wrong passphrase or non-TTY prompt.
|
|
59
|
+
*/
|
|
60
|
+
getLastError?(): KeyProviderError | null;
|
|
43
61
|
}
|
|
44
62
|
/** 1Password-specific configuration. */
|
|
45
63
|
export interface OnePasswordConfig {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/key-providers/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,uCAAuC;AACvC,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/key-providers/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,uCAAuC;AACvC,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,WAAW,GAAG,YAAY,CAAC;AAEtE,uEAAuE;AACvE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,kDAAkD;IAClD,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAE/B;;;;OAIG;IACH,MAAM,IAAI,MAAM,GAAG,IAAI,CAAC;IAExB;;;;;;;OAOG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAE/B;;;;OAIG;IACH,QAAQ,CAAC,IAAI,MAAM,GAAG,IAAI,CAAC;IAE3B;;;;OAIG;IACH,SAAS,IAAI,OAAO,CAAC;IAErB;;;;;OAKG;IACH,YAAY,CAAC,IAAI,gBAAgB,GAAG,IAAI,CAAC;CAC1C;AAMD,wCAAwC;AACxC,MAAM,WAAW,iBAAiB;IAChC,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,iEAAiE;AACjE,MAAM,WAAW,aAAa;IAC5B,yDAAyD;IACzD,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,mCAAmC;IACnC,WAAW,CAAC,EAAE,iBAAiB,CAAC;CACjC;AAED,sDAAsD;AACtD,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB"}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Abstracts vault encryption key storage behind a provider interface.
|
|
5
5
|
* Implementations retrieve/store the 32-byte AES-256-GCM key from
|
|
6
|
-
* different backends (macOS Keychain, 1Password, etc.).
|
|
6
|
+
* different backends (macOS Keychain, 1Password, passphrase KDF, etc.).
|
|
7
7
|
*
|
|
8
8
|
* All methods return null/false on failure — never throw (consistent
|
|
9
9
|
* with vault-common.ts conventions).
|
|
@@ -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"}
|