From e70f0ed1ff4717344b00319a3a511de4434afa2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Guti=C3=A9rrez?= <35082514+alezmad@users.noreply.github.com> Date: Tue, 7 Apr 2026 12:56:36 +0100 Subject: [PATCH] fix(broker/cli): e2e get_file owner sealedKey bug broker: owner also fetches sealedKey from mesh.file_key (not skipped), only non-owners are blocked when key is missing cli: explicit error when encrypted file has no sealedKey (no silent raw download) Co-Authored-By: Claude Sonnet 4.6 --- apps/broker/src/index.ts | 17 ++++++++--------- apps/cli/src/mcp/server.ts | 3 ++- 2 files changed, 10 insertions(+), 10 deletions(-) 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();