@bivy/bivy 0.18.5-staging.1 → 0.18.5-staging.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/dist/model-auth-key-recovery.js +22 -0
- package/dist/server.js +22 -7
- package/package.json +1 -1
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 Petter André Sjulstad
|
|
3
|
+
/** Validate keys against the vault before caching them or sharing them with peers.
|
|
4
|
+
* A stale local key must not hide a fresh peer wrap; an unusable wrap must never
|
|
5
|
+
* be persisted as though key recovery succeeded. No credential data is changed.
|
|
6
|
+
*/
|
|
7
|
+
export function recoverModelAuthKey(options) {
|
|
8
|
+
if (options.localKey) {
|
|
9
|
+
try {
|
|
10
|
+
return { key: options.localKey, value: options.decrypt(options.localKey) };
|
|
11
|
+
}
|
|
12
|
+
catch { /* Try the peer's replacement before requesting another wrap. */ }
|
|
13
|
+
}
|
|
14
|
+
if (options.unwrap) {
|
|
15
|
+
try {
|
|
16
|
+
const key = options.unwrap();
|
|
17
|
+
return { key, value: options.decrypt(key) };
|
|
18
|
+
}
|
|
19
|
+
catch { /* The caller must reject this exact wrap and request a new one. */ }
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
package/dist/server.js
CHANGED
|
@@ -149,6 +149,7 @@ import { redactSecrets } from "./redact.js";
|
|
|
149
149
|
import { getSttConfig, isSttProvider, removeSttKey, setSttKey, setSttProvider, transcribeAudio, MAX_AUDIO_BYTES, } from "./stt.js";
|
|
150
150
|
import { synthesizeOpenAiSpeech } from "./tts.js";
|
|
151
151
|
import { seal, open } from "./e2e.js";
|
|
152
|
+
import { recoverModelAuthKey } from "./model-auth-key-recovery.js";
|
|
152
153
|
import { RunDelegationService, parseDelegationSource } from "./run-tools.js";
|
|
153
154
|
import { ControlPlaneTaskPoller, resolveControlPlaneTaskConfig, } from "./control-plane-tasks.js";
|
|
154
155
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -3365,20 +3366,31 @@ async function syncModelAuthFromControlPlane() {
|
|
|
3365
3366
|
}
|
|
3366
3367
|
const targetVault = hostedCustody ? hostedData.hostedVault : data.vault;
|
|
3367
3368
|
let vaultKeyB64 = hostedCustody ? readHostedModelAuthVaultKey() : readLocalModelAuthVaultKey();
|
|
3368
|
-
if (!hostedCustody && !vaultKeyB64 && data.wrappedKey?.wrappedKey) {
|
|
3369
|
-
vaultKeyB64 = pairingStore.unwrapFromNodePublicKey(data.wrappedKey.wrappedByPublicKey, data.wrappedKey.wrappedKey);
|
|
3370
|
-
writeLocalModelAuthVaultKey(vaultKeyB64);
|
|
3371
|
-
}
|
|
3372
3369
|
// Hosted runners may decrypt ONLY the separately encrypted snapshot of
|
|
3373
3370
|
// records that the user explicitly granted to unattended execution.
|
|
3374
3371
|
if (hostedCustody && !vaultKeyB64 && hostedData.hostedKey && hostedData.hostedVault && Buffer.from(hostedData.hostedKey, "base64").length === 32) {
|
|
3375
3372
|
vaultKeyB64 = hostedData.hostedKey;
|
|
3376
3373
|
writeHostedModelAuthVaultKey(vaultKeyB64);
|
|
3377
3374
|
}
|
|
3378
|
-
if (targetVault?.ciphertext && vaultKeyB64) {
|
|
3375
|
+
if (targetVault?.ciphertext && (vaultKeyB64 || (!hostedCustody && data.wrappedKey))) {
|
|
3379
3376
|
let decrypted;
|
|
3380
3377
|
try {
|
|
3381
|
-
|
|
3378
|
+
if (hostedCustody) {
|
|
3379
|
+
decrypted = decryptModelAuthEnvelope(targetVault.ciphertext, vaultKeyB64);
|
|
3380
|
+
}
|
|
3381
|
+
else {
|
|
3382
|
+
const wrapped = data.wrappedKey;
|
|
3383
|
+
const recovered = recoverModelAuthKey({
|
|
3384
|
+
localKey: vaultKeyB64,
|
|
3385
|
+
unwrap: wrapped ? () => pairingStore.unwrapFromNodePublicKey(wrapped.wrappedByPublicKey, wrapped.wrappedKey) : undefined,
|
|
3386
|
+
decrypt: (key) => decryptModelAuthEnvelope(targetVault.ciphertext, key),
|
|
3387
|
+
});
|
|
3388
|
+
if (!recovered)
|
|
3389
|
+
throw new Error("No available key can decrypt the account vault");
|
|
3390
|
+
vaultKeyB64 = recovered.key;
|
|
3391
|
+
decrypted = recovered.value;
|
|
3392
|
+
writeLocalModelAuthVaultKey(vaultKeyB64);
|
|
3393
|
+
}
|
|
3382
3394
|
}
|
|
3383
3395
|
catch (error) {
|
|
3384
3396
|
// Most commonly this node cached the previous generation while another
|
|
@@ -3394,7 +3406,10 @@ async function syncModelAuthFromControlPlane() {
|
|
|
3394
3406
|
else {
|
|
3395
3407
|
forgetLocalModelAuthVaultKey();
|
|
3396
3408
|
lastPushedModelAuthCiphertext = "";
|
|
3397
|
-
await modelAuthFetch("/node/model-auth-key/request", {
|
|
3409
|
+
await modelAuthFetch("/node/model-auth-key/request", {
|
|
3410
|
+
method: "POST",
|
|
3411
|
+
body: JSON.stringify({ publicKey: pairingStore.nodePublicKeyB64(), rejectedWrappedKey: data.wrappedKey?.wrappedKey }),
|
|
3412
|
+
});
|
|
3398
3413
|
ensureModelAuthColdStart();
|
|
3399
3414
|
}
|
|
3400
3415
|
console.warn("[auth-sync] cached vault key is stale; requested the rotated key:", error.message);
|
package/package.json
CHANGED