Skip to content

Leave a line for apps from before MLS (GRYT-1508) - #244

Merged
sivert-io merged 1 commit into
mainfrom
claude/GRYT-1508-old-apps-placeholder
Sep 28, 2026
Merged

sivert-io merged 1 commit into
mainfrom
claude/GRYT-1508-old-apps-placeholder

Conversation

@sivert-io

@sivert-io sivert-io commented Sep 28, 2026 •

Copy link
Copy Markdown
Member

What an old app shows

On desktop, checked on the web build of client 1.11.46 (9073ec7) against a server from this branch on :5003. A script sent an MLS message as Alice to a guest. The DM showed a quiet system row: the sign-in arrow, an @alice mention pill, then "sent an end-to-end encrypted message. Update Gryt to read it." and the time. The row had no "Not encrypted" mark, live or after a reload, and the DM icon got its unread 1.

On the phone (353440b, the build that went with 1.11.46), from reading the code rather than running it: a row headed "System" saying "@alice sent an end-to-end encrypted message. Update Gryt to read it." The phone skips system messages for its unread pill and its toast, so an old phone gets no nudge. The line is there when you open the DM.

Why a system line

A plain line from the sender reads worse on desktop. ChatView passes unencrypted={conversationKind === "dm" && !m.sealed}, so the row would get a red edge and a "Not encrypted" chip right next to text saying it was encrypted. A sealed envelope nobody can open shows "This message could not be opened.", which gives no hint to update. And a system line doesn't put words in the sender's mouth, which also means their old app can't edit it.

The marker

These rows carry mls_placeholder: { seq, sender_server_id }, live in chat:new and in history. seq is the MLS log entry the line stands in for. An app that reads MLS drops any row that has it. Everything else about the row is an ordinary system message: sender_server_id is "system" and sealed is null.

Unread counts and history

Both count MLS messages, through the line. The server keeps no unread counts, and apps count chat:new, so an old app counts the line like any other message. REST history and chat:fetch return it too. An app that reads MLS drops the line and counts mls:message instead. So an old app sees one new row per message, and a new app has one rule: skip mls_placeholder. Doing that in the apps is GRYT-1517.

What to look at

  • The migration adds messages.mls_seq and messages.mls_sender_server_id, NULL on every other row. mls_sender_server_id is in AUTHOR_COLUMNS, so a guest merge moves it.
  • mls:send takes placeholder: false for application messages a person doesn't read as a message: reactions, edits, deletes. Without it an old app shows a line for every reaction. It does tell the server which application messages are messages. Section 8 of the design already has the server counting messages, so it learns little new, but it's still metadata MLS would otherwise hide.
  • The sender's own sockets don't get the line live. Their old apps find it in history. Since the sender id is "system", a desktop would otherwise notify you about your own message.
  • chat:fetch hides a line whose real sender you blocked, and the live line skips anybody who blocked them. hasWrittenTo counts the line, so contact settings and the friends fallback see MLS messages. Without that, somebody who only allows people they've written to could never get a reply to an MLS message.
  • Deleting an MLS message leaves its line. The server can't tell a delete from any other application message, so old apps keep showing "sent an encrypted message".

Tests

In mls.test.ts: the other side gets the line as a system message with the marker, the sender doesn't, it's stored with its seq, and hasWrittenTo sees it. placeholder: false leaves no line and anything but a boolean is refused. A blocked sender's line is held back live and left out of chat:fetch. Full suite: 1756 pass.

Task: GRYT-1508. Docs: Gryt-chat/docs#141.

🤖 Generated with Claude Code

An MLS message never reached the messages table, so an app from before
stage 1 got nothing for it. Now each application message sent with mls:send
also writes a system line into the DM: "@kari sent an end-to-end encrypted
message. Update Gryt to read it." The row carries
mls_placeholder { seq, sender_server_id }, and apps that read MLS hide it.

It's a system line because desktop 1.11.46 puts a red "Not encrypted" mark
on any plain DM message, right next to text saying it was encrypted. System
rows don't get the mark.

A client passes placeholder: false for application messages that aren't a
message, like reactions, edits and deletes. The sender's own sockets don't
get the line live, a block hides it in history, and hasWrittenTo counts it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@sivert-io
sivert-io marked this pull request as ready for review September 28, 2026 08:05
@sivert-io
sivert-io merged commit 8b878b0 into main Sep 28, 2026
7 checks passed
@sivert-io
sivert-io deleted the claude/GRYT-1508-old-apps-placeholder branch September 28, 2026 08:06
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.

1 participant