@byok-sdk/keys 0.2.1 → 0.2.2
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 +7 -0
- package/dist/bin/pi-provider-launcher.js +57 -19
- package/dist/bin/pi-provider-launcher.js.map +1 -1
- package/dist/index.js +26 -14
- package/dist/index.js.map +1 -1
- package/dist/macos-keychain.d.ts +2 -0
- package/dist/pi-provider-launcher-core.d.ts +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -105,6 +105,13 @@ dependency and the workspace release floor.
|
|
|
105
105
|
| `TruthStoreProviderProfileStore` | Node 22.22+ plus an injected tenant-bound `TruthStore` | stale CAS or malformed/hash-mismatched authority fails closed; never falls back to SQLite |
|
|
106
106
|
| `byok-pi-provider-launcher` | Node 22.5+ (`node:sqlite`) and macOS/Windows for authenticated profiles | fails closed; `auth_mode: none` does not require a credential backend |
|
|
107
107
|
|
|
108
|
+
On macOS, an isolated host may select one explicit credential authority with
|
|
109
|
+
`MacOsKeychainSecretStore({ keychainPath: '/absolute/path/to/keychain-db' })`
|
|
110
|
+
or the launcher's `--macos-keychain-path` flag. The path must be absolute and
|
|
111
|
+
single-line. Once selected, availability and every credential operation use
|
|
112
|
+
that keychain only; the store never also searches the user default keychain.
|
|
113
|
+
The launcher rejects this macOS-only flag on other platforms.
|
|
114
|
+
|
|
108
115
|
Only on-disk profile persistence needs the newer runtime. `node:sqlite` shipped
|
|
109
116
|
in Node 22.5 and spent part of the 22.x line behind `--experimental-sqlite`, so
|
|
110
117
|
a version-number comparison would be wrong in both directions; call
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { spawn } from 'child_process';
|
|
3
3
|
import { promises, mkdirSync, existsSync, chmodSync } from 'fs';
|
|
4
4
|
import os from 'os';
|
|
5
|
-
import
|
|
5
|
+
import path2, { dirname } from 'path';
|
|
6
6
|
import { z } from 'zod';
|
|
7
7
|
import { createRequire } from 'module';
|
|
8
8
|
|
|
@@ -100,6 +100,7 @@ var MacOsKeychainSecretStore = class _MacOsKeychainSecretStore {
|
|
|
100
100
|
#account;
|
|
101
101
|
#allowUnprefixedRead;
|
|
102
102
|
#commandRunner;
|
|
103
|
+
#keychainPath;
|
|
103
104
|
#platform;
|
|
104
105
|
#servicePrefix;
|
|
105
106
|
#storagePrefix;
|
|
@@ -107,6 +108,7 @@ var MacOsKeychainSecretStore = class _MacOsKeychainSecretStore {
|
|
|
107
108
|
this.#account = options.account ?? "local-device";
|
|
108
109
|
this.#allowUnprefixedRead = options.allowUnprefixedRead ?? false;
|
|
109
110
|
this.#commandRunner = options.commandRunner ?? runCommand;
|
|
111
|
+
this.#keychainPath = assertKeychainPath(options.keychainPath);
|
|
110
112
|
this.#platform = options.platform ?? process.platform;
|
|
111
113
|
this.#servicePrefix = options.servicePrefix ?? DEFAULT_SECRET_SERVICE_PREFIX;
|
|
112
114
|
this.#storagePrefix = options.storagePrefix ?? DEFAULT_KEYCHAIN_SECRET_STORAGE_PREFIX;
|
|
@@ -114,9 +116,7 @@ var MacOsKeychainSecretStore = class _MacOsKeychainSecretStore {
|
|
|
114
116
|
async available() {
|
|
115
117
|
if (this.#platform !== "darwin") return false;
|
|
116
118
|
const result = await this.#commandRunner("/usr/bin/security", [
|
|
117
|
-
"default-keychain",
|
|
118
|
-
"-d",
|
|
119
|
-
"user"
|
|
119
|
+
...this.#keychainPath === void 0 ? ["default-keychain", "-d", "user"] : ["show-keychain-info", this.#keychainPath]
|
|
120
120
|
]);
|
|
121
121
|
return result.exitCode === 0;
|
|
122
122
|
}
|
|
@@ -128,7 +128,8 @@ var MacOsKeychainSecretStore = class _MacOsKeychainSecretStore {
|
|
|
128
128
|
"-a",
|
|
129
129
|
this.#account,
|
|
130
130
|
"-s",
|
|
131
|
-
service
|
|
131
|
+
service,
|
|
132
|
+
...this.#keychainPath === void 0 ? [] : [this.#keychainPath]
|
|
132
133
|
]);
|
|
133
134
|
if (result.exitCode === SECURITY_ITEM_NOT_FOUND) return false;
|
|
134
135
|
if (result.exitCode !== 0) {
|
|
@@ -148,7 +149,8 @@ var MacOsKeychainSecretStore = class _MacOsKeychainSecretStore {
|
|
|
148
149
|
this.#account,
|
|
149
150
|
"-s",
|
|
150
151
|
service,
|
|
151
|
-
"-w"
|
|
152
|
+
"-w",
|
|
153
|
+
...this.#keychainPath === void 0 ? [] : [this.#keychainPath]
|
|
152
154
|
]);
|
|
153
155
|
if (result.exitCode === SECURITY_ITEM_NOT_FOUND) return void 0;
|
|
154
156
|
if (result.exitCode !== 0) {
|
|
@@ -167,6 +169,7 @@ var MacOsKeychainSecretStore = class _MacOsKeychainSecretStore {
|
|
|
167
169
|
account: this.#account,
|
|
168
170
|
allowUnprefixedRead: this.#allowUnprefixedRead,
|
|
169
171
|
commandRunner: this.#commandRunner,
|
|
172
|
+
keychainPath: this.#keychainPath,
|
|
170
173
|
platform: this.#platform,
|
|
171
174
|
servicePrefix: `${this.#servicePrefix}.scope.${assertSecretNamespace(namespace)}`,
|
|
172
175
|
storagePrefix: this.#storagePrefix
|
|
@@ -190,7 +193,8 @@ var MacOsKeychainSecretStore = class _MacOsKeychainSecretStore {
|
|
|
190
193
|
"-s",
|
|
191
194
|
quoteSecurityInteractiveArgument(service),
|
|
192
195
|
"-w",
|
|
193
|
-
quoteSecurityInteractiveArgument(storedSecret)
|
|
196
|
+
quoteSecurityInteractiveArgument(storedSecret),
|
|
197
|
+
...this.#keychainPath === void 0 ? [] : [quoteSecurityInteractiveArgument(this.#keychainPath)]
|
|
194
198
|
].join(" ");
|
|
195
199
|
const result = await this.#commandRunner(
|
|
196
200
|
"/usr/bin/security",
|
|
@@ -249,6 +253,16 @@ function quoteSecurityInteractiveArgument(value) {
|
|
|
249
253
|
}
|
|
250
254
|
return `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
|
|
251
255
|
}
|
|
256
|
+
function assertKeychainPath(keychainPath) {
|
|
257
|
+
if (keychainPath === void 0) return void 0;
|
|
258
|
+
if (keychainPath.length === 0 || !path2.posix.isAbsolute(keychainPath) || /[\u0000\r\n]/u.test(keychainPath)) {
|
|
259
|
+
throw new ByokKeysError(
|
|
260
|
+
"KEYCHAIN_ARGUMENT_INVALID",
|
|
261
|
+
"macOS keychain path must be a non-empty absolute single-line path"
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
return keychainPath;
|
|
265
|
+
}
|
|
252
266
|
|
|
253
267
|
// src/pi-provider-projection.ts
|
|
254
268
|
var PI_PROJECTED_KEY_ENV = "PI_PROVIDER_API_KEY";
|
|
@@ -476,7 +490,8 @@ function parsePiProviderLauncherOptions(args) {
|
|
|
476
490
|
"--provider",
|
|
477
491
|
"--model",
|
|
478
492
|
"--session-dir",
|
|
479
|
-
"--secret-service-prefix"
|
|
493
|
+
"--secret-service-prefix",
|
|
494
|
+
"--macos-keychain-path"
|
|
480
495
|
]);
|
|
481
496
|
const values = /* @__PURE__ */ new Map();
|
|
482
497
|
for (let index = 0; index < ownArgs.length; index += 2) {
|
|
@@ -505,10 +520,14 @@ function parsePiProviderLauncherOptions(args) {
|
|
|
505
520
|
if (modelId.length > 160) throw new Error("--model exceeds 160 characters");
|
|
506
521
|
const profileDbPath = required("--profile-db");
|
|
507
522
|
const sessionDir = required("--session-dir");
|
|
508
|
-
if (!
|
|
523
|
+
if (!path2.isAbsolute(profileDbPath) || !path2.isAbsolute(sessionDir)) {
|
|
509
524
|
throw new Error("launcher profile database and session directory must be absolute paths");
|
|
510
525
|
}
|
|
511
526
|
const secretServicePrefix = values.get("--secret-service-prefix");
|
|
527
|
+
const macosKeychainPath = values.get("--macos-keychain-path");
|
|
528
|
+
if (macosKeychainPath !== void 0 && !path2.posix.isAbsolute(macosKeychainPath)) {
|
|
529
|
+
throw new Error("--macos-keychain-path must be an absolute path");
|
|
530
|
+
}
|
|
512
531
|
return {
|
|
513
532
|
piBin: required("--pi-bin"),
|
|
514
533
|
profileDbPath,
|
|
@@ -516,6 +535,7 @@ function parsePiProviderLauncherOptions(args) {
|
|
|
516
535
|
modelId,
|
|
517
536
|
sessionDir,
|
|
518
537
|
...secretServicePrefix ? { secretServicePrefix } : {},
|
|
538
|
+
...macosKeychainPath !== void 0 ? { macosKeychainPath } : {},
|
|
519
539
|
piArgs
|
|
520
540
|
};
|
|
521
541
|
}
|
|
@@ -604,19 +624,19 @@ function loadSqliteModule() {
|
|
|
604
624
|
);
|
|
605
625
|
}
|
|
606
626
|
}
|
|
607
|
-
function openSqliteDatabase(
|
|
627
|
+
function openSqliteDatabase(path4, options, faults) {
|
|
608
628
|
const { DatabaseSync } = loadSqliteModule();
|
|
609
629
|
const readOnly = options?.readOnly === true;
|
|
610
|
-
if (
|
|
611
|
-
mkdirSync(dirname(
|
|
630
|
+
if (path4 !== ":memory:" && !readOnly) {
|
|
631
|
+
mkdirSync(dirname(path4), { mode: SECURE_DIR_MODE, recursive: true });
|
|
612
632
|
}
|
|
613
|
-
const database = new DatabaseSync(
|
|
633
|
+
const database = new DatabaseSync(path4, {
|
|
614
634
|
timeout: DEFAULT_BUSY_TIMEOUT_MS,
|
|
615
635
|
...options
|
|
616
636
|
});
|
|
617
637
|
try {
|
|
618
638
|
faults?.onStep?.("after-open");
|
|
619
|
-
if (
|
|
639
|
+
if (path4 !== ":memory:" && !readOnly) {
|
|
620
640
|
database.exec("PRAGMA journal_mode = WAL");
|
|
621
641
|
faults?.onStep?.("after-wal");
|
|
622
642
|
database.exec("PRAGMA synchronous = FULL");
|
|
@@ -1045,10 +1065,19 @@ function assertWindowsCredentialSecret(secret) {
|
|
|
1045
1065
|
}
|
|
1046
1066
|
|
|
1047
1067
|
// src/bin/pi-provider-launcher.ts
|
|
1048
|
-
function createSecretStore(servicePrefix) {
|
|
1068
|
+
function createSecretStore(servicePrefix, macosKeychainPath) {
|
|
1069
|
+
if (macosKeychainPath !== void 0 && process.platform !== "darwin") {
|
|
1070
|
+
throw new ByokKeysError(
|
|
1071
|
+
"KEYCHAIN_UNAVAILABLE",
|
|
1072
|
+
"macOS keychain path is only supported on darwin"
|
|
1073
|
+
);
|
|
1074
|
+
}
|
|
1049
1075
|
switch (process.platform) {
|
|
1050
1076
|
case "darwin":
|
|
1051
|
-
return new MacOsKeychainSecretStore({
|
|
1077
|
+
return new MacOsKeychainSecretStore({
|
|
1078
|
+
keychainPath: macosKeychainPath,
|
|
1079
|
+
servicePrefix
|
|
1080
|
+
});
|
|
1052
1081
|
case "win32":
|
|
1053
1082
|
return new WindowsCredentialManagerSecretStore({ servicePrefix });
|
|
1054
1083
|
default:
|
|
@@ -1059,6 +1088,12 @@ function createSecretStore(servicePrefix) {
|
|
|
1059
1088
|
}
|
|
1060
1089
|
}
|
|
1061
1090
|
async function run(options) {
|
|
1091
|
+
if (options.macosKeychainPath !== void 0 && process.platform !== "darwin") {
|
|
1092
|
+
throw new ByokKeysError(
|
|
1093
|
+
"KEYCHAIN_UNAVAILABLE",
|
|
1094
|
+
"macOS keychain path is only supported on darwin"
|
|
1095
|
+
);
|
|
1096
|
+
}
|
|
1062
1097
|
const profiles = new SqliteProviderProfileStore({
|
|
1063
1098
|
path: options.profileDbPath,
|
|
1064
1099
|
readOnly: true
|
|
@@ -1076,14 +1111,17 @@ async function run(options) {
|
|
|
1076
1111
|
}
|
|
1077
1112
|
const secret = await resolvePiProviderSecret(
|
|
1078
1113
|
profile,
|
|
1079
|
-
() => createSecretStore(
|
|
1114
|
+
() => createSecretStore(
|
|
1115
|
+
options.secretServicePrefix,
|
|
1116
|
+
options.macosKeychainPath
|
|
1117
|
+
)
|
|
1080
1118
|
);
|
|
1081
|
-
projectionDir = await promises.mkdtemp(
|
|
1119
|
+
projectionDir = await promises.mkdtemp(path2.join(os.tmpdir(), "byok-pi-provider-"));
|
|
1082
1120
|
await promises.chmod(projectionDir, 448).catch(() => {
|
|
1083
1121
|
});
|
|
1084
1122
|
await ensurePiSessionDirectory(options.sessionDir);
|
|
1085
1123
|
await promises.writeFile(
|
|
1086
|
-
|
|
1124
|
+
path2.join(projectionDir, "models.json"),
|
|
1087
1125
|
`${JSON.stringify(buildPiProviderProjection(profile))}
|
|
1088
1126
|
`,
|
|
1089
1127
|
{ mode: 384 }
|