Skip to content

feat: implement per-device E2EE message envelopes and retraction#257

Closed
testersweb0-bug wants to merge 3 commits into
codebestia:mainfrom
testersweb0-bug:feature/e2ee-messaging-envelopes
Closed

feat: implement per-device E2EE message envelopes and retraction#257
testersweb0-bug wants to merge 3 commits into
codebestia:mainfrom
testersweb0-bug:feature/e2ee-messaging-envelopes

Conversation

@testersweb0-bug

Copy link
Copy Markdown
Contributor

Description

This PR introduces the schema, backend routing, and WebSocket logic required to fully support True End-to-End Encryption (E2EE) using per-device envelopes, alongside a secure, sender-initiated message retraction flow.

Key Features and Fixes:

  • message_envelopes Schema: Created a new database table to store per-recipient-device ciphertexts. Added proper relations, indexes, and cascaded deletes to ensure database integrity when messages or devices are removed.
  • WebSocket send_message Redesign:
    • Updated the WebSocket handler to accept a new payload format containing messageId, contentType, envelopes, and an optional ciphertext fallback.
    • Ensured payload idempotency on duplicated messageIds (acting as client-generated idempotency keys), preventing double-inserts during network retries.
    • Automatically derives and inserts envelopes referencing valid recipientDeviceId identifiers linked to the user's active registered devices.
    • Broadcasts the new message to room members and acknowledges the sender with an assigned, monotonically increasing sequenceNumber.
  • Reworked GET /conversations/:id/messages:
    • The endpoint now explicitly fetches the ciphertext targeted to the requesting user's active device (req.auth!.deviceId).
    • Messages missing a matching envelope for the caller's device degrade gracefully to return a metadata-only unavailable placeholder, without throwing an error.
  • Message Retraction with Tombstones:
    • The DELETE /messages/:id endpoint now securely nullifies the ciphertext on the message instead of just setting deletedAt.
    • It permanently deletes all associated E2EE message_envelopes.
    • Enforces authorization so that only the message sender can initiate retraction (returns 403 otherwise).
    • Broadcasts a real-time message_deleted event to the room when successfully processed.

Related Issues

Closes #182 (Add message_envelopes schema)
Closes #184 (WebSocket send_message accepts per-device envelopes)
Closes #177 (Update GET /conversations/:id/messages to return per-message metadata plus ciphertext)
Closes #189 (Message deletion / retraction with tombstones)

Acceptance Criteria Met

  • Retraction removes ciphertext + envelopes, keeps a tombstone row.
  • message_deleted broadcast to the room.
  • Only sender/authorized role can delete (403 otherwise).
  • Duplicate messageId is idempotent (no double-insert).
  • Envelope device set validated.
  • ACK returned to sender with assigned sequenceNumber.
  • One message → N envelopes (one per active recipient device, excluding revoked).
  • Deleting a message cascades its envelopes.
  • Per-device sync query is index-backed.
  • Response contains only ciphertext the caller's device can decrypt.
  • Pre-link messages surface as unavailable placeholders, not errors.
  • Pagination + membership checks preserved.
  • Non-member → 403.

Testing Steps

  1. Idempotency: Emit duplicate send_message WebSocket events with the same messageId to verify no duplicate records are inserted.
  2. Device Sync: Test message fetching with a registered device that intentionally lacks an envelope to verify the unavailable placeholder is served.
  3. Retraction: Retract a message to ensure all message_envelopes are permanently cleared, the ciphertext is nulled, and the room receives the message_deleted broadcast.

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

@drips-wave

drips-wave Bot commented Jun 27, 2026

Copy link
Copy Markdown

@testersweb0-bug Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@testersweb0-bug testersweb0-bug deleted the feature/e2ee-messaging-envelopes branch June 28, 2026 15:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant