Skip to content

Commit 2518c94

Browse files
committed
MSC: Live Messages via Event Replacement (draft)
Signed-off-by: Igor Somov <[email protected]>
1 parent 130da80 commit 2518c94

File tree

1 file changed

+123
-0
lines changed

1 file changed

+123
-0
lines changed

proposals/4357-live-messages.md

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
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

Comments
 (0)