Skip to content

fix(messages): serialize list responses and mark unavailable envelopes (#336) - #524

Open
johdanike wants to merge 3 commits into
codebestia:mainfrom
johdanike:fix/message-list-ciphertext-shape
Open

fix(messages): serialize list responses and mark unavailable envelopes (#336)#524
johdanike wants to merge 3 commits into
codebestia:mainfrom
johdanike:fix/message-list-ciphertext-shape

Conversation

@johdanike

Copy link
Copy Markdown
Contributor

Summary

This PR resolves #336 by fixing the response shape of the GET /conversations/:id/messages handler. Previously, the endpoint returned raw row objects instead of mapping them through the shared serialization helper, and clients had no way to distinguish a legitimately empty message from one where the caller's device lacked a matching envelope. This update ensures consistent ciphertext-safe shapes and explicit unavailable markers.

Changes Made

  • Safe Serialization: The message list handler now maps each row through the shared serializeMessage helper, ensuring the returned shape exactly matches what GET /:id produces.
  • Unavailable Marker: Added logic to explicitly attach unavailable: true to any message that lacks a matching envelope for the caller's specific deviceId.
  • Test Coverage: Created a completely new, dedicated test suite for this route to verify standard pagination, assert cursor correctness, and explicitly test the unavailable-envelope fallback state.

Acceptance Criteria Met

  • The handler maps each row through serializeMessage, matching the shape GET /:id already produces.
  • Messages with no matching envelope for the caller's device are explicitly marked unavailable: true.
  • A dedicated test file covers: normal pagination, an unavailable-envelope case, and cursor correctness.

Closes #336

- Updated the conversation list handler to map preview messages through serializeConversation.
- Applied serializeMessage to ensure the preview shape matches the GET /:id response.
- Prevented the accidental leakage of plaintext content fields in the raw conversation object.
- Added explicit tests asserting the list response contains no plaintext fields.
- Verified in tests that the preview message perfectly matches the standard ciphertext-safe shape.

Closes codebestia#338
- Added privacy check to ensure the reader's sendReadReceipts setting is true before broadcasting.
- Implemented a monotonic cursor guard to ensure lastReadMessageId only ever advances forward.
- Updated the handler to stamp messageEnvelopes.readAt for the reader's device envelopes.
- Ignored backwards cursor updates to prevent stale read receipts from regressing state.
- Added comprehensive tests covering the privacy toggle, backwards rejection, and readAt persistence.

Closes codebestia#339
- Updated GET /conversations/:id/messages to map rows through the serializeMessage helper.
- Ensured the message list response shape perfectly matches the GET /:id endpoint.
- Added logic to explicitly mark messages missing a device envelope with unavailable: true.
- Created a dedicated test file covering pagination, unavailable envelopes, and cursor correctness.

Closes codebestia#336
@drips-wave

drips-wave Bot commented Jul 30, 2026

Copy link
Copy Markdown

@johdanike 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GET /conversations/:id/messages: per-device ciphertext shape + unavailable marker

1 participant