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.
This commit is contained in:
byGalax
2026-05-16 00:58:57 +02:00
parent 61462516d2
commit c9fe4879e0
+12 -3
View File
@@ -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<C
const insertPayload: Record<string, unknown> = {
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<void> {
const ownCtx: OwnUserCtx = {
userId: params.senderUserId,