From c9fe4879e029e6c7f89ceb3589aa57af5a5e94d6 Mon Sep 17 00:00:00 2001 From: byGalax Date: Sat, 16 May 2026 00:58:57 +0200 Subject: [PATCH] fix(shared): stop sending fake install-id as messages.sender_device_id Task 12 (the AuthContext userKeyState refactor) replaced the per-device DeviceRecord lookup with a localStorage UUID via ensureInstallId(). That UUID was then passed straight through to messages.sender_device_id on INSERT. The messages_insert_member RLS policy requires sender_device_id to be NULL OR to match a row in `devices` owned by the caller. The localStorage UUID matches neither -> 403 -> outbox endlessly retries with "Wiederhole". Fix: SendMessageParams.senderDeviceId becomes optional, and the message INSERT coerces undefined to NULL. The column is pure telemetry post-conv- keys so passing NULL is correct. Existing call sites that hand in ensureInstallId() still typecheck (string is assignable to string|null|undefined) but the row is written with NULL until those callers stop passing it. --- packages/shared/src/chat/messages.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/shared/src/chat/messages.ts b/packages/shared/src/chat/messages.ts index df2e295..61754b5 100644 --- a/packages/shared/src/chat/messages.ts +++ b/packages/shared/src/chat/messages.ts @@ -91,7 +91,11 @@ export interface SendMessageParams { conversationId: string; plaintext: string; senderUserId: string; - senderDeviceId: string; + // Optional now: post-conv-keys this is pure telemetry. The 0.18 builds + // started passing a localStorage UUID that doesn't exist in the devices + // table; messages.sender_device_id RLS then 403'd every insert. Senders + // pass null (or an actually-registered device id, if they have one). + senderDeviceId?: string | null; senderPrivateKey: Uint8Array; replyToId?: string; // Optional encrypted attachments — their handles are already materialised @@ -124,7 +128,12 @@ export async function sendEncryptedMessage(params: SendMessageParams): Promise = { conversation_id: params.conversationId, sender_id: params.senderUserId, - sender_device_id: params.senderDeviceId, + // ALWAYS null until we re-introduce a real per-install devices row. + // Desktop callers currently pass a localStorage UUID (ensureInstallId) + // which doesn't exist in the devices table; the messages_insert_member + // RLS policy then 403s because the id can't be proven to belong to the + // caller. NULL satisfies the policy ("sender_device_id IS NULL OR …"). + sender_device_id: null, ciphertext: bytesToPgHex(cipher.ciphertext), nonce: bytesToPgHex(cipher.nonce), key_version: handle.keyVersion, @@ -174,7 +183,7 @@ export interface EditMessageParams { // Re-encrypts the message body with the conv-key and updates the row. // Server-side trigger enforces 24h window + sender-only rule. export async function editEncryptedMessage( - params: EditMessageParams & { senderUserId: string; senderDeviceId: string }, + params: EditMessageParams & { senderUserId: string; senderDeviceId?: string | null }, ): Promise { const ownCtx: OwnUserCtx = { userId: params.senderUserId,