feat: implement E2EE messaging envelopes and message retraction#269
Merged
codebestia merged 8 commits intoJun 28, 2026
Merged
Conversation
…0-bug/clicked into feat/e2ee-messaging-envelopes
codebestia
approved these changes
Jun 28, 2026
codebestia
left a comment
Owner
There was a problem hiding this comment.
LGTM!
Thank you for contribution.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description & Context
As we move towards a more privacy-centric architecture, this PR fundamentally overhauls our messaging backend to support full End-to-End Encryption (E2EE). Previously, message contents were stored in plaintext on the server, which posed privacy risks and allowed for server-side full-text search.
With this update, the server acts purely as a secure router for encrypted payloads. When a user sends a message, their client encrypts the content individually for every active device in the conversation. These encrypted payloads are stored as
message_envelopes. The server no longer has access to the plaintext content, ensuring that only the authorized recipient devices can decrypt and read the messages.Because the server is now blind to the message contents, this PR also deprecates the server-side full-text search endpoint, paving the way for a client-side search implementation in the future. Finally, we've introduced full message retraction capabilities (using tombstones) so users can reliably delete their messages across all devices without leaving orphaned ciphertext behind.
Key Changes:
Schema Updates:
messagestable since the server can no longer index encrypted content.contentcolumn onmessageswithciphertext(used for system messages or fallback) and added new metadata fields (senderDeviceId,contentType,sequenceNumber).message_envelopestable linked tomessages(id)anduser_devices(id)to securely distribute device-specific payloads.Socket Events (
messaging.ts):send_messagehandler to accept an array of envelopes mapping to devices and insert them transactionally using Drizzle. Idempotency is maintained via client-generatedmessageIds.delete_messageevent handler allowing senders to fully retract their messages. This wipes out the ciphertext and corresponding envelopes across all devices while preserving a tombstone.REST APIs (
routes/conversations.ts&routes/messages.ts):GET /conversations/:id/messagesto properly fetch and mergemessage_envelopesdirected exclusively at the authenticated user'sdeviceId.DELETE /messages/:idto cascade deletions across ciphertext and envelopes while emitting amessage_deletedevent to active clients.GET /conversations/:id/searchendpoint logic with a501 Not Implementederror, formally enforcing the E2EE privacy boundary.Robust Serializations:
serializeMessageto prioritize device-specific envelope contents. If a device is unauthorized or its envelope is missing, it falls back on base ciphertext or safely marks the message as anunavailableplaceholder.Testing:
Closes #177
Closes #182
Closes #184
Closes #189
Type of change
Checklist