-
Notifications
You must be signed in to change notification settings - Fork 401
MSC4140: Delayed events #4140
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
toger5
wants to merge
92
commits into
main
Choose a base branch
from
toger5/expiring-events-keep-alive
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+841
−0
Open
MSC4140: Delayed events #4140
Changes from 9 commits
Commits
Show all changes
92 commits
Select commit
Hold shift + click to select a range
480b00a
draft for expiring event PR
toger5 8839b8d
Add msc number
toger5 8bf6db7
add security consideration and alternatives
toger5 8ec6374
alternative name and alternative content
toger5 9f45cfa
review andrewF
toger5 54fff99
draft of iteration two (after meeting with the backend team)
toger5 abdfe1c
timeout_refresh_token is not a well description since the same token …
toger5 53f6186
rename msc, rephrase introduction
toger5 087c74e
Add usecase specific section.
toger5 538b853
add GET futures endpoint
toger5 f7a1aad
shorten introduction
toger5 f5f4b38
add alternative section to not include the `m.send_now` field
toger5 c16afbc
Update proposals/4140-delayed-events-futures.md
toger5 c52c80d
batch sending considerations
toger5 bf22260
Update proposals/4140-delayed-events-futures.md
toger5 7f0d80f
Update proposals/4140-delayed-events-futures.md
toger5 7b192ac
Update proposals/4140-delayed-events-futures.md
toger5 f3bf66d
review
toger5 2d7b27d
add background to usecase specific considerations
toger5 1140ce9
Simplify main proposal for widget api usage
toger5 0a7896e
make `future_group_id` server generated and small adjustments
toger5 8fa33d6
review
toger5 49d5294
user scoping details
toger5 7550d9b
Update proposals/4140-delayed-events-futures.md
toger5 a663bb4
add rate limiting section
toger5 99b3a20
rename `/futures` to `/future`
toger5 eb50a19
Update everything to v1
toger5 9ff051e
Update proposals/4140-delayed-events-futures.md
toger5 425b9bf
review
toger5 2e7be46
Swap the alternative of reusing the send and state request with the m…
toger5 5653fe1
add reference to MSC4143 (MatrixRTC)
toger5 af060cf
Meeting feedback:
toger5 bff704c
remove parent concept.
toger5 fa461e2
Refactor + unstable prefixes
hughns d195218
Update to GET /futures
hughns b5ac9b2
Standardise endpoint for interacting with futures
hughns c07dd9b
Make it clear that events can be cancelled
hughns b79e4e2
Make wording more authoritative
hughns e125901
Remove erroneous old example
hughns 0443cd9
Take view that clients don't act on behalf of a user
hughns 7df0919
Use name delay_id and delayed_events
hughns ff95144
Clean up more references to future
hughns 93c932a
event_type => type
hughns 6cce3bd
Tidying
hughns ac2d2c5
Pagination on GET endpoint
hughns 84e20fd
Add alternative of M_INVALID_PARAM
hughns ac3bd9d
change order of manage <-> get sections
toger5 de77a90
Obey lint rules and more renames
toger5 677d6f3
TOC update
hughns 4caecd1
Additional notes on security considerations
hughns c482d58
Add rate limiting requirements
hughns 9098fea
Describe MatrixRTC use case in terms of heartbeats
hughns 7e06e85
"leave" => "hangup" for consistency
hughns a1b8121
Wording clarification
hughns 3a3a5b5
Additional reference to heartbeat
hughns 97d4141
TOC update (use monospace and dont list msc title
toger5 99c9467
be consistent with "restart" wording (we used reset, refresh and rest…
toger5 6f2aa5e
Add compatibility with Cryptographic Identities to potenail issues
hughns 5e43db0
Add placeholder for MSC3277
hughns 84b8dc0
Add note about MSC2716 batch sending
hughns e7d0986
Add alternative of not providing a send action
hughns 850bf9e
Add alternative of using DELETE HTTP method
hughns e1b460a
Add clarification about regular/non-state events not getting cancelled
hughns 114da1e
Clarify contents of content field on GET response
hughns 772590f
Expose transaction ID in GET response
hughns 4a2ca48
Add context on MSC3277 alternative
hughns 37979cd
Add more context to MQTT style alternative
hughns 195ab6a
Add alternative of typing notifications
hughns 0f8a2d1
Update TOC
hughns 886f378
Fix TOC
hughns 8523ed4
Note on alternative names for `running_since`
hughns 883e6b5
Revert "Expose transaction ID in GET response"
AndrewFerr caece4d
Scope GET to only the requesting user's events
AndrewFerr 28970ec
remove all references to future (except where we actually talk about …
toger5 2f57b0b
add terminated events section to GET endpoint
toger5 9d5c93a
use case specific considerations details
toger5 da3d75e
remove batch sending endpoint since that does not make sense in the s…
toger5 f7e4e9b
Clean up iteration with two significant changes:
toger5 b8e317f
terminated -> finalised
toger5 b499995
andrews changes
toger5 8dc05a4
requried -> required
AndrewFerr 0a777f4
Change example of "canceled" outcome to "cancel"
AndrewFerr a09a883
Minor formatting changes
AndrewFerr 72a808e
Allow servers to discard returned finalised events
AndrewFerr d1a37f0
Define finalised events for /sync & /transactions
AndrewFerr 3358138
use current MatrixRTC MSC types.
toger5 904e3d6
review trivial changes
toger5 c25e5a3
Add rate limit section to `GET` `finalized` and `scheduled` endpoint
toger5 96f9063
clarify why there is no rate limiting for the delayed event managemen…
toger5 99b79ba
remove `on push` delayed event finilization section
toger5 dc5fd93
formatting/cleanup
toger5 bf5df9b
fix synapse and MSC inconsistency
toger5 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,266 @@ | ||
# MSC4140: Delayed events (Futures) | ||
|
||
In the context of matrixRTC where we want | ||
to ignore expired state events of users who left the call without sending a new | ||
state empty `m.call.member` event. | ||
|
||
We would like the homeserver to mark this event as expired/send an expired version | ||
in a reasonable time window after a user disconnected. | ||
|
||
Currently there is no mechanism for a client to provide a reliable way of | ||
communicating that an event is still valid. | ||
The only way to update an event is to post a new one. | ||
In some situations the client just looses connection or fails to sent the expired | ||
version of the event. | ||
|
||
A generic way in which one can automate expirations is desired. | ||
|
||
The described usecase is solved if we allow to send an event in advance | ||
to the homeserver but let the homeserver compute when its actually added to the | ||
DAG. | ||
The condition for actually sending the delayed event would could be a timeout. | ||
|
||
## Proposal | ||
|
||
To make this as generic as possible, the proposed solution is to allow sending | ||
multiple presigned events and delegate the control of when to actually send these | ||
events to an external services. This allows to exactly define what expiration means, | ||
since any event that will be sent once expired can be defined. | ||
|
||
We call those events `Futures`. | ||
|
||
A new endpoint is introduced: | ||
`PUT /_matrix/client/v3/rooms/{roomId}/send/future/{txnId}` | ||
toger5 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
It behaves exactly like the normal send endpoint except that that it allows | ||
to send a list of event contents. The body looks as following: | ||
toger5 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
```json | ||
{ | ||
"m.timeout": 10, | ||
"m.send_on_timeout": { | ||
"content": sendEventBody0, | ||
"type": "m.room.message", | ||
}, | ||
|
||
"m.send_on_action:${actionName}": { | ||
"content": sendEventBody1, | ||
"type": "m.room.message" | ||
}, | ||
|
||
// optional | ||
"m.send_now": { | ||
"content": sendEventBody2, | ||
"type": "m.room.message" | ||
}, | ||
} | ||
``` | ||
toger5 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Each of the `sendEventBody` objects are exactly the same as sending a normal | ||
event. | ||
|
||
There can be an arbitrary amount of `actionName`s. | ||
toger5 marked this conversation as resolved.
Show resolved
Hide resolved
toger5 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
All of the fields are optional except the `timeout` and the `send_on_timeout`. | ||
This guarantees that all tokens will expire eventually. | ||
toger5 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
The homeserver can set a limit to the timeout and return an error if the limit | ||
is exceeded. | ||
|
||
### Response | ||
|
||
The response will mimic the request: | ||
|
||
```json | ||
toger5 marked this conversation as resolved.
Show resolved
Hide resolved
toger5 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
"m.send_on_timeout": { | ||
"eventId": "id_hash" | ||
}, | ||
"m.send_on_action:${actionName}": { | ||
"eventId": "id_hash" | ||
}, | ||
|
||
"future_token": "token", | ||
|
||
// optional | ||
"m.send_now": { "eventId": "id_hash" } | ||
} | ||
``` | ||
|
||
### Delegating futures | ||
|
||
The `token` can be used to call another future related endpoint: | ||
`PUT /_matrix/client/v3/futures/refresh` and `PUT /_matrix/client/v3/futures/action/${actionName}`. | ||
toger5 marked this conversation as resolved.
Show resolved
Hide resolved
toger5 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
where the body is: | ||
|
||
```json | ||
{ | ||
"future_token": "token" | ||
} | ||
``` | ||
|
||
The information required to call this endpoint is very limited so that almost | ||
no metadata is leaked. This allows to share a refresh link to a different | ||
service. This allows to delegate the send time. An SFU for instance, that tracks the current client connection state, | ||
and pings the HS to refresh and call a dedicated action to communicate | ||
that the user has intentionally left the conference. | ||
|
||
The homeserver does the following when receiving a Future. | ||
|
||
- It **sends** the optional `m.send_now` event. | ||
- It **generates** a `future_token` and stores it alongside with the time | ||
of retrieval, the event list and the timeout duration. | ||
- **Starts a timer** for the stored `future_token`. | ||
|
||
- If a `PUT /_matrix/client/v3/futures/refresh` is received, it | ||
**restarts the timer** with the stored timeout duration. | ||
- If a `PUT /_matrix/client/v3/futures/action/${actionName}` is received, it **sends the associated action event** | ||
`m.action:${actionName}`. | ||
toger5 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- If the timer times out, **it sends the timeout event** `m.send_timeout`. | ||
- If the future is a state event and includes a `m.send_now` event | ||
the future is only valid while the `m.send_now` | ||
is still the current state: | ||
AndrewFerr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
- This means, if the homeserver receives | ||
a new state event for the same state key, the **`future_token`** | ||
**gets invalidated and the associated timer is stopped**. | ||
|
||
- There is no race condition here since a possible race between timeout and | ||
new event will always converge to the new event: | ||
- Timeout -> new event: the room state will be updated twice. once by | ||
the content of the `m.send_on_timeout` event but later with the new event. | ||
- new event -> timeout: the new event will invalidate the future. No | ||
toger5 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
- After the homeservers sends a timeout or action future event, the associated | ||
timer and `future_token` is canceled/invalidated. | ||
|
||
So for each Future the client sends, the homeserver will send one event | ||
conditionally at an unknown time that can trigger logic on the client. | ||
This allows for any generic timeout logic. | ||
|
||
Timed messages/reminders or ephemeral events could be implemented using this where | ||
clients send a redact as a future or a room event with intentional mentions. | ||
|
||
In some scenarios it is important to allow to send an event with an associated | ||
future at the same time. | ||
|
||
- One example would be redacting an event. It only makes sense to redact the event | ||
if it exists. | ||
It might be important to have the guarantee, that the redact is received | ||
by the server at the time where the original message is sent. | ||
- In the case of a state event we might want to set the state to `A` and after a | ||
timeout reset it to `{}`. If we have two separate request sending `A` could work | ||
but the event with content `{}` could fail. The state would not automatically | ||
reset to `{}`. | ||
|
||
For this usecase an optional `m.send_now` field can be added to the body. | ||
hughns marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## Usecase specific considerations | ||
|
||
### MatrixRTC | ||
|
||
We want can use the actions and the timeout for matrix rtc for the following situations | ||
|
||
- If the client takes care of its membership, we use a short timeout value (around 5-20 seconds) | ||
The client will have to ping the refresh endpoint approx every 2-19 seconds. | ||
- When the SFU is capable of taking care of managing our connection state and we trust the SFU to | ||
not disconnect a really long value can be chosen (approx. 2-10hours). The SFU will then only send | ||
an action once the user disconnects or looses connection (it could even be a different action for both cases | ||
handling them differently on the client) | ||
This significantly reduces the amount of calls for the `/future` endpoint since the sfu only needs to ping | ||
once per session (per user) and every 2-5hours (instead of every `X` seconds.) | ||
|
||
### Self destructing messages | ||
toger5 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
This MSC also allows to implement self destructing messages: | ||
toger5 marked this conversation as resolved.
Show resolved
Hide resolved
toger5 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
`PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}` | ||
|
||
```json | ||
{ | ||
"m.text": "my msg" | ||
} | ||
``` | ||
|
||
`PUT /_matrix/client/v3/rooms/{roomId}/send/future/{txnId}` | ||
|
||
```json | ||
{ | ||
"m.timeout": 10*60, | ||
"m.send_on_timeout": { | ||
"type":"m.room.readact", | ||
"content":{ | ||
"redacts": "EvId" | ||
} | ||
} | ||
} | ||
``` | ||
|
||
## EventId template variable | ||
toger5 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
It would be useful to be able to send redactions and edits as one http request. | ||
toger5 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
This would make sure that the client cannot loose connection after sending the first event. | ||
toger5 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
For instance sending a self destructing message without the redaction. | ||
toger5 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
The optional proposal is to introduce template variables that are only valid in `Future` events. | ||
`$m.send_now.event_id` in the content of one of the `m.send_on_action:${actionName}` and | ||
`m.send_on_timeout` contents this template variable can be used. | ||
The **Self destructing messages** example would simplify to: | ||
toger5 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
`PUT /_matrix/client/v3/rooms/{roomId}/send/future/{txnId}` | ||
|
||
```json | ||
{ | ||
"m.send_now":{ | ||
"type":"m.room.message", | ||
"content":{ | ||
"m.text": "my msg" | ||
} | ||
}, | ||
"m.timeout": 10*60, | ||
"m.send_on_timeout": { | ||
"type":"m.room.readact", | ||
"content":{ | ||
"redacts": "$m.send_now.event_id" | ||
} | ||
} | ||
} | ||
``` | ||
|
||
## Potential issues | ||
|
||
## Alternatives | ||
|
||
[MSC4018](https://github.com/matrix-org/matrix-spec-proposals/pull/4018) also | ||
proposes a way to make call memberships reliable. It uses the client sync loop as | ||
an indicator to determine if the event is expired. Instead of letting the SFU | ||
inform about the call termination or using the call app ping loop like we propose | ||
here. | ||
|
||
--- | ||
|
||
The following names for the endpoint are considered | ||
|
||
- Future | ||
- DelayedEvents | ||
- RetardedEvents | ||
toger5 marked this conversation as resolved.
Show resolved
Hide resolved
toger5 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## Security considerations | ||
|
||
We are using an unauthenticated endpoint to refresh the expirations. Since we use | ||
the token it is hard to guess a correct request and force one of the actions | ||
events of the Future. | ||
|
||
It is an intentional decision to not provide an endpoint like | ||
`PUT /_matrix/client/v3/futures/room/{roomId}/event/{eventId}` | ||
where any client with access to the room could also `end` or `refresh` | ||
the expiration. With the token the client sending the event has ownership | ||
over the expiration and only intentional delegation of that ownership | ||
(sharing the token) is possible. | ||
|
||
On the other hand the token makes sure that the instance gets as little | ||
information about the matrix metadata of the associated `future` event. It cannot | ||
toger5 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
even tell with which room or user it is interacting. | ||
|
||
## Unstable prefix | ||
|
||
## Dependencies |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.