|
| 1 | +# MSC4357: Live Messages via Event Replacement |
| 2 | + |
| 3 | +## Introduction |
| 4 | + |
| 5 | +This proposal introduces **Live Messages** for Matrix: recipients can see a message evolve in near real time while it is |
| 6 | +being typed. The user experience is similar to streaming answers from LLMs (token-by-token) and to Simplex.chat's |
| 7 | +mutable "chat items", but without introducing a new event type. Instead, we rely on Matrix's existing edit mechanism |
| 8 | +(`m.replace` on `m.room.message`). Non-supporting clients degrade gracefully: they see a normal message which was edited |
| 9 | +a few times and always end up with the final content. |
| 10 | + |
| 11 | +Motivation includes more natural conversation flow, better perceived responsiveness, and resilience in unstable |
| 12 | +environments (e.g., live reporting): every chunk that was sent remains stored, so the last known state is preserved even |
| 13 | +if the sender disconnects prematurely. |
| 14 | + |
| 15 | +## Proposal |
| 16 | + |
| 17 | +A Live Message is a session of high-frequency edits applied to a single persistent `m.room.message`. The session starts |
| 18 | +when the user enables "live mode" and ends when the user finalizes the message. We introduce an **unstable marker** in |
| 19 | +event content to identify a live session and specify client/server behavior that reuses existing primitives. |
| 20 | + |
| 21 | +### Event identification |
| 22 | + |
| 23 | +- **Initial event**: a normal `m.room.message` whose `content` **MUST** include the unstable key |
| 24 | + `"org.matrix.msc4357.live": {}` (empty JSON object). This marks the start of a live message session. |
| 25 | +- **Update events**: each update **MUST** be a `m.room.message` with `m.relates_to.rel_type = "m.replace"` and |
| 26 | + `m.relates_to.event_id` referencing the initial event; its `m.new_content` **MUST** contain the *full* updated body |
| 27 | + (and formatted body if applicable). |
| 28 | +- **Final update**: when the user completes the message, the last update **SHOULD NOT** include the live marker. The |
| 29 | + absence of the live marker in the aggregated content signals session completion. |
| 30 | + |
| 31 | +### Client behavior (sending) |
| 32 | + |
| 33 | +- **Activation**: when the user toggles live mode, clients provide a clear UI affordance (e.g., lightning icon). |
| 34 | +- **First send**: upon a natural boundary (e.g., finishing a word) or a short delay, send the initial message with the |
| 35 | + live marker and retain its `event_id` for subsequent `m.replace`. |
| 36 | +- **Periodic updates**: send updates every ~2–3 seconds and/or on natural boundaries. Avoid per-keystroke updates. Each |
| 37 | + update replaces the entire content (`m.new_content` reflects the complete current text). |
| 38 | +- **Completion**: on explicit send or mode exit, send a final update without the live marker. After this, do not send |
| 39 | + further updates for this session. |
| 40 | +- **Rate limiting**: clients **MUST** avoid flooding and respect server guidance; batching is encouraged. |
| 41 | + |
| 42 | +### Client behavior (receiving) |
| 43 | + |
| 44 | +- When the initial event contains the live marker, supporting clients **SHOULD** render it in a "live" state (e.g., |
| 45 | + subtle animation / icon). Updates targeting the same message via `m.replace` **SHOULD** update the displayed text in |
| 46 | + place, without showing a separate "(edited)" marker or spamming notifications. When a final update arrives without the |
| 47 | + live marker, the client **SHOULD** transition the message to a normal finalized state. |
| 48 | +- Non-supporting clients treat the flow as "message followed by edits", ending with the correct final content. |
| 49 | + |
| 50 | +### Server behavior |
| 51 | + |
| 52 | +- No new endpoints or types are required. Homeservers store and federate the initial message and its `m.replace` updates |
| 53 | + as usual. Existing aggregation behavior applies. Homeservers **MAY** apply rate limits to high-frequency edits and/or |
| 54 | + offer retention policies for rooms with heavy live usage. |
| 55 | + |
| 56 | +## Room-level control: `m.room.live_messaging` (optional) |
| 57 | + |
| 58 | +To allow administrators and room moderators to control the feature, we introduce a room state event: |
| 59 | + |
| 60 | +- **Type**: `m.room.live_messaging` (unstable: `org.matrix.msc4357.live_messaging` until accepted) |
| 61 | +- **State key**: `""` |
| 62 | +- **Content**: |
| 63 | + ``` json |
| 64 | + { |
| 65 | + "enabled": true |
| 66 | + } |
| 67 | + ``` |
| 68 | + |
| 69 | +### Semantics: |
| 70 | + |
| 71 | +- If absent, default is enabled (unless server policy states otherwise). |
| 72 | +- If `enabled: false`, clients **MUST NOT** offer live mode in this room. |
| 73 | +- Servers **MAY** enforce rejection of events that carry live markers in rooms where it is disabled. |
| 74 | + |
| 75 | +## Backwards compatibility |
| 76 | + |
| 77 | +Because live sessions are realized as normal edits, older clients retain a consistent view: they see a message which was |
| 78 | +edited. Users always end up with the final text. Supporting clients provide a richer in-place progressive rendering. |
| 79 | + |
| 80 | +## Security and abuse considerations |
| 81 | + |
| 82 | +- **Spam / flooding**: A malicious client could emit updates too frequently. Mitigations include server rate-limits, |
| 83 | + client batching, and room-level disabling via `m.room.live_messaging`. This is not a new class of risk compared to |
| 84 | + rapid normal edits; existing controls apply. |
| 85 | +- **Privacy**: Live updates reveal intermediate drafts. This is a user-experience choice; clients should clearly label |
| 86 | + the mode and allow users to opt out. The content remains subject to the same E2EE/federation properties as standard |
| 87 | + messages. |
| 88 | +- **Storage**: Live sessions produce more events. Deployments can rely on retention policies and rate-limits. |
| 89 | + |
| 90 | +## Alternatives |
| 91 | + |
| 92 | +- **Ephemeral chunks** (`m.ephemeral...`): reduces storage, but loses data on disconnect and degrades UX on older |
| 93 | + clients (they see nothing until the final send). Not recommended. |
| 94 | +- **New relation type** (e.g., `m.live_update`): adds semantic clarity but provides limited practical benefits over |
| 95 | + `m.replace` and worsens compatibility; clients can already infer live sessions via the marker and frequency. |
| 96 | + |
| 97 | +## Use cases |
| 98 | + |
| 99 | +- Natural real-time conversations. |
| 100 | +- Live reporting in unstable networks (maximize delivered information). |
| 101 | +- Streaming responses from bots/LLMs (token-by-token rendering inside Matrix). |
| 102 | +- Customer support scenarios where reduced response latency improves user experience. |
| 103 | +- Real-time collaborative environments where immediate feedback is valuable. |
| 104 | + |
| 105 | +## Unstable identifiers |
| 106 | + |
| 107 | +- Content marker: `"org.matrix.msc4357.live": {}` |
| 108 | +- Room state: `org.matrix.msc4357.live_messaging` |
| 109 | + |
| 110 | +After acceptance, the stable equivalents **MAY** be standardized if needed. |
| 111 | + |
| 112 | +## Implementations |
| 113 | + |
| 114 | +- **Client (qualifying)**: TBD — a reference client implementing live mode UI, periodic `m.replace`, in-place rendering, |
| 115 | + and room state enforcement. |
| 116 | +- **Bot/AS (optional)**: a demo streaming LLM output as live updates. |
| 117 | + |
| 118 | +## Conclusion |
| 119 | + |
| 120 | +This MSC introduces Live Messages with minimal protocol surface by reusing `m.replace`. It preserves compatibility, |
| 121 | +improves perceived responsiveness, and supports important real-time scenarios. With optional room-level control and |
| 122 | +existing server safeguards (rate-limits, retention), it provides a pragmatic, adoptable path to streaming-like UX in |
| 123 | +Matrix. |
0 commit comments