diff --git a/apps/broker/src/index.ts b/apps/broker/src/index.ts index 67dcd1f..317f8f8 100644 --- a/apps/broker/src/index.ts +++ b/apps/broker/src/index.ts @@ -977,19 +977,18 @@ function handleConnection(ws: WebSocket): void { break; } } - // E2E: for encrypted files, fetch the sealed key for this peer + // E2E: for encrypted files, fetch the sealed key for this peer. + // Owners are not blocked if their key is missing (edge case), but + // they still get it returned so the CLI can decrypt normally. let sealedKey: string | null = null; if (file.encrypted) { const peerPubkey = conn.sessionPubkey ?? conn.memberPubkey; - const isOwner = file.ownerPubkey && peerPubkey === file.ownerPubkey; - if (!isOwner) { - sealedKey = peerPubkey ? await getFileKey(gf.fileId, peerPubkey) : null; - if (!sealedKey) { - sendError(conn.ws, "forbidden", "no decryption key for this file"); - break; - } + const isOwner = !!(file.ownerPubkey && peerPubkey === file.ownerPubkey); + sealedKey = peerPubkey ? await getFileKey(gf.fileId, peerPubkey) : null; + if (!sealedKey && !isOwner) { + sendError(conn.ws, "forbidden", "no decryption key for this file"); + break; } - // Owner gets sealedKey = null (they already have Kf from upload) } // Generate presigned URL (60s expiry) const bucket = meshBucketName(conn.meshId); diff --git a/apps/cli/src/mcp/server.ts b/apps/cli/src/mcp/server.ts index a9d2140..b840c98 100644 --- a/apps/cli/src/mcp/server.ts +++ b/apps/cli/src/mcp/server.ts @@ -527,7 +527,8 @@ Your message mode is "${messageMode}". const result = await client.getFile(id); if (!result) return text(`get_file: file ${id} not found`, true); - if (result.encrypted && result.sealedKey) { + if (result.encrypted) { + if (!result.sealedKey) return text("get_file: encrypted file — no decryption key available for your session", true); const { openSealedKey, decryptFile } = await import("../crypto/file-crypto"); const { ensureSodium } = await import("../crypto/keypair"); const myPubkey = client.getSessionPubkey();