fix(cli): no base64 fallback on direct-message decrypt failure
The push handler previously fell through to base64-decoding the raw ciphertext whenever decryptDirect() returned null. For direct (crypto_box) messages that produces garbage binary which surfaces as garbled bytes in Claude's <channel> reminder. Limit the base64 fallback to legacy broadcast/channel messages (no senderPubkey), and emit a clearer "⚠ message from <pubkey> failed to decrypt" warning when direct decryption fails. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -73,8 +73,13 @@ function resolveClient(to: string): {
|
||||
};
|
||||
}
|
||||
|
||||
function decryptFailedWarning(senderPubkey: string): string {
|
||||
const who = senderPubkey ? senderPubkey.slice(0, 12) + "…" : "unknown sender";
|
||||
return `⚠ message from ${who} failed to decrypt (tampered or wrong keypair)`;
|
||||
}
|
||||
|
||||
function formatPush(p: InboundPush, meshSlug: string): string {
|
||||
const body = p.plaintext ?? "(decryption failed)";
|
||||
const body = p.plaintext ?? decryptFailedWarning(p.senderPubkey);
|
||||
return `[${meshSlug}] from ${p.senderPubkey.slice(0, 12)}… (${p.priority}, ${p.createdAt}):\n${body}`;
|
||||
}
|
||||
|
||||
@@ -82,7 +87,7 @@ export async function startMcpServer(): Promise<void> {
|
||||
const config = loadConfig();
|
||||
|
||||
const server = new Server(
|
||||
{ name: "claudemesh", version: "0.1.1" },
|
||||
{ name: "claudemesh", version: "0.1.2" },
|
||||
{
|
||||
capabilities: {
|
||||
experimental: { "claude/channel": {} },
|
||||
@@ -215,7 +220,7 @@ If you have multiple joined meshes, prefix the \`to\` argument of send_message w
|
||||
const fromName = fromPubkey
|
||||
? `peer-${fromPubkey.slice(0, 8)}`
|
||||
: "unknown";
|
||||
const content = msg.plaintext ?? "(decryption failed)";
|
||||
const content = msg.plaintext ?? decryptFailedWarning(fromPubkey);
|
||||
try {
|
||||
await server.notification({
|
||||
method: "notifications/claude/channel",
|
||||
|
||||
@@ -312,10 +312,14 @@ export class BrokerClient {
|
||||
this.mesh.secretKey,
|
||||
);
|
||||
}
|
||||
// If decryption failed, fall back to base64 UTF-8 unwrap —
|
||||
// this covers the legacy plaintext path for broadcasts/channels
|
||||
// until channel crypto lands.
|
||||
if (plaintext === null && ciphertext) {
|
||||
// Legacy/broadcast path: no senderPubkey means the message
|
||||
// was not crypto_box'd, so base64 UTF-8 unwrap is correct.
|
||||
// For direct messages (senderPubkey present) we MUST NOT
|
||||
// base64-decode the ciphertext on decrypt failure — that
|
||||
// produces garbage binary that surfaces as garbled bytes
|
||||
// to Claude. Leave plaintext=null and let consumers emit
|
||||
// a clear "failed to decrypt" warning.
|
||||
if (plaintext === null && ciphertext && !senderPubkey) {
|
||||
try {
|
||||
plaintext = Buffer.from(ciphertext, "base64").toString("utf-8");
|
||||
} catch {
|
||||
|
||||
Reference in New Issue
Block a user