fix(storage): let a Blossom-only (keyless) account sync across devices

importAccountRecord hard-required encryptedMasterKey, but a Blossom-only account is keyless (no encrypted payloads) and legitimately has none — so its synced record failed to import. Require the envelope ONLY when the record carries an encrypted connection that actually needs it to unlock.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
thePR0M3TH3ANandClaude Opus 4.8 committed 2026-07-12 10:22:22 -04:00
1 parent ef2e48912d
commit 51072e4871
2 files changed
+55 -1

No files matched your search

+15 -1
View File
@@ -735,7 +735,21 @@ export class StorageService {
if (!pubkey) {
throw new Error("Pubkey required to import storage account record.");
}
if (!record || typeof record !== "object" || !record.encryptedMasterKey) {
if (!record || typeof record !== "object") {
throw new Error("Invalid storage account record.");
}
// A Blossom-only account is keyless (no encrypted payloads), so it legitimately
// has no encryptedMasterKey. Require the envelope ONLY when the record actually
// carries an encrypted connection that would need it to unlock — otherwise a
// Blossom-only account can't sync across devices.
const connections =
record.connections && typeof record.connections === "object"
? record.connections
: {};
const hasEncryptedConnection = Object.values(connections).some(
(conn) => conn && conn.encrypted != null,
);
if (hasEncryptedConnection && !record.encryptedMasterKey) {
throw new Error("Invalid storage account record.");
}
const toSave = { ...record, pubkey };
+40
View File
@@ -240,4 +240,44 @@ describe("saving multiple providers does not clash", () => {
await storageService.importAccountRecord(pubkey, record);
assert.equal(fired, null, "off() detached the observer");
});
test("a Blossom-only (keyless) account syncs without an encryptedMasterKey", async () => {
// Blossom connections are keyless (encrypted: null), so a Blossom-only account
// legitimately has no encryptedMasterKey. Importing it must still work.
const record = {
pubkey,
connections: {
blossom: {
id: "blossom",
provider: "blossom",
meta: { provider: "blossom", servers: ["https://blossom.band"] },
encrypted: null,
},
},
};
await storageService.importAccountRecord(pubkey, record); // must not throw
const conns = await storageService.listConnections(pubkey);
assert.deepEqual(conns.map((c) => c.id), ["blossom"]);
assert.deepEqual(conns[0].meta.servers, ["https://blossom.band"]);
});
test("an encrypted (R2/S3) connection still requires the master-key envelope", async () => {
// A record carrying an encrypted payload can't be unlocked without the
// envelope — importing it without one must still be rejected.
const record = {
pubkey,
connections: {
default: {
id: "default",
provider: "cloudflare_r2",
meta: { provider: "cloudflare_r2" },
encrypted: { cipher: "deadbeef", iv: "00" },
},
},
};
await assert.rejects(
() => storageService.importAccountRecord(pubkey, record),
/Invalid storage account record/,
);
});
});