The decrypted event would contain the `type` and `content.sticky_key`.
rate when implementing ephemeral maps (see "Addendum: Implementing an ephemeral map"), as that relies on servers referencing sticky events from other servers.
#### Spam
@ -437,6 +342,109 @@ dropped updates for the latter scenario.
- The sticky key in the `content` of the PDU is `msc4354_sticky_key`.
- To enable this in SSS, the extension name is `org.matrix.msc4354.sticky_events`.
## Addendum
This section explains how sticky events can be used to implement a short-lived, per-user, per-room key-value store.
This technique would be used by MatrixRTC to synchronise RTC members.
### Implementing an ephemeral map
MatrixRTC relies on a per-user, per-device map of RTC member events. To implement this, this MSC proposes
a standardised mechanism for determining keys on sticky events, the `content.sticky_key` property:
```js
{
"type": "m.rtc.member",
"sticky": {
"duration_ms": 300000
},
"sender": "@alice:example.com",
"room_id": "!foo",
"origin_server_ts": 1757920344000,
"content": {
"sticky_key": "LAPTOPXX123",
...
}
}
```
`content.sticky_key` is ignored server-side[^encryption] and is purely informational. Clients which
receive a sticky event with a sticky key SHOULD keep a map with keys determined via the 3-uple[^4uple]
`(room_id, sender, content.sticky_key)` to track the current values in the map. Nothing stops
users sending multiple events with the same `sticky_key`. To deterministically tie-break, clients which
implement this behaviour MUST:
- pick the one with the highest `origin_server_ts`,
- tie break on the one with the highest lexicographical event ID (A <Z).
Clients SHOULD expire sticky events in maps when their stickiness ends. They should use the algorithm described in this proposal
to determine if an event is still sticky. Clients may diverge if they do not expire sticky events as in the following scenario:
```mermaid
sequenceDiagram
HS1->>+HS2: Sticky event S (1h sticky)
HS2->>Client: Sticky event S (1h sticky)
HS2->>-HS1: OK
HS2->>+HS2: Goes offline
HS1->>+HS2: Sticky event S' (1h sticky)
HS1->>+HS2: Retry Sticky event S' (1h sticky)
HS1->>+HS2: Retry Sticky event S' (1h sticky)
HS1->>HS1: Sticky event S' expires
HS2->>HS2: Back online
Client->>Client: Sticky event S still valid!
```
There is no mechanism for sticky events to expire earlier than their timeout value. To remove entries in the map, clients SHOULD
send another sticky event with just `content.sticky_key` set, with all the other application-specific fields omitted.
When clients create multiple events with the same `sticky_key`, they SHOULD use the same sticky duration as the previous
sticky event to avoid clients diverging. This can happen when a client sends a sticky event S with a long timeout, then overwrites it with S’
with a short timeout. If S’ fails to be sent to all servers before the short timeout is hit,
some clients will believe the state is S and others will have no state. This will only resolve once the long timeout is hit.
To illustrate this, consider the scenario when clients use the _same sticky duration_:
```
Event Lifetime
S [=========|==] |
S' [====|=====|=]
| |
A B
```
At the time `A`, the possible states on clients is `{ _, S, S'}` where `_` is nothing due to not seeing either event. At time `B`,
the possible states on clients is `{ _, S'}`.
Contrast with:
```
Event Lifetime
S [=======|====|=]
S' [==|=] |
| |
A B
```
Just like before, at time `A` the possible states are `{ _, S, S'}`, but now at time `B` the possible states are `{ _, S }`.
This is problematic if you're trying to agree on the "latest" values, like you would in a k:v map.
Note that encrypted sticky events will encrypt some parts of the 4-uple. An encrypted sticky event only exposes the room ID and sender to the server: