--- description: Message persist, deliver queue, and Redis inbox rules. globs: internal/module/{message,deliver,inbox,sync,conversation}/**/*.go alwaysApply: false --- # Message / deliver / inbox Send path: permission → INSERT `messages` + UPDATE `conversations.last_msg_id` → `DeliverQueue.Push` → `SEND_ACK`. No ACK if enqueue fails. Idempotency: `UNIQUE(sender_id, conversation_id, client_msg_id)`. Conflicts return the original `MsgId`. Queue: `RPUSH` / `BRPOPLPUSH` to `deliver:processing` / `LREM` ack. On boot `RequeueStale`. Dedup with `SETNX deliver:done:{msgId}`. `DeliverJob` flags: `ExcludeDevice`, `NotPush`, `NotMailbox`. Sender device does not get `MESSAGE`; other devices of the sender must. Inbox: Redis ZSET `im:dialog:unread|read:{uid}`, preview, unread hash. Mailbox MySQL is only missed push + control events (recall/kick/friend). Never write `NEW_MESSAGE` mailbox for a successful online push. P2P conversation key is `(user_small, user_big)`. Sync messages with `id > last_msg_id`. `messages.content` is a BLOB of serialized JSON bytes for a typed payload (text/image/voice/video/emoji/gif/file/location/system). Do not store a raw string. Media bytes stay in object storage; content holds the object key plus metadata.