Skip to content

feat: implement E2EE messaging envelopes and message retraction#269

Merged
codebestia merged 8 commits into
codebestia:mainfrom
testersweb0-bug:feat/e2ee-messaging-envelopes
Jun 28, 2026
Merged

feat: implement E2EE messaging envelopes and message retraction#269
codebestia merged 8 commits into
codebestia:mainfrom
testersweb0-bug:feat/e2ee-messaging-envelopes

Conversation

@testersweb0-bug

Copy link
Copy Markdown
Contributor

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:

    • Dropped the server-side full-text search index from the messages table since the server can no longer index encrypted content.
    • Replaced the plain content column on messages with ciphertext (used for system messages or fallback) and added new metadata fields (senderDeviceId, contentType, sequenceNumber).
    • Created the message_envelopes table linked to messages(id) and user_devices(id) to securely distribute device-specific payloads.
  • Socket Events (messaging.ts):

    • Modified the send_message handler to accept an array of envelopes mapping to devices and insert them transactionally using Drizzle. Idempotency is maintained via client-generated messageIds.
    • Added a delete_message event 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):

    • Re-architected GET /conversations/:id/messages to properly fetch and merge message_envelopes directed exclusively at the authenticated user's deviceId.
    • Reworked DELETE /messages/:id to cascade deletions across ciphertext and envelopes while emitting a message_deleted event to active clients.
    • Replaced the GET /conversations/:id/search endpoint logic with a 501 Not Implemented error, formally enforcing the E2EE privacy boundary.
  • Robust Serializations:

    • Updated serializeMessage to 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 an unavailable placeholder.
  • Testing:

    • Adjusted numerous TS types and updated test mocks across the test suite to accommodate the new E2EE schemas. All backend pipeline checks and tests are passing.

Closes #177
Closes #182
Closes #184
Closes #189

Type of change

  • Bug fix
  • New feature
  • Documentation update
  • Other

Checklist

  • I have read the contributing guidelines
  • I have tested my changes locally
  • My code follows the project's coding standards

@codebestia codebestia left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!
Thank you for contribution.

@codebestia codebestia merged commit 39eb8c3 into codebestia:main Jun 28, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants