From d53447b9919d867768bdab000e5ded611d1c7738 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 2 Sep 2020 13:49:37 -0600 Subject: [PATCH 01/11] Proposal to refine/define the send/receive event API for widgets --- proposals/0000-widget-event-receiving.md | 202 +++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 proposals/0000-widget-event-receiving.md diff --git a/proposals/0000-widget-event-receiving.md b/proposals/0000-widget-event-receiving.md new file mode 100644 index 00000000000..ed9f3f087bc --- /dev/null +++ b/proposals/0000-widget-event-receiving.md @@ -0,0 +1,202 @@ +# MSC0000: Allowing widgets to send/receive events + +[MSC1236](https://github.com/matrix-org/matrix-doc/issues/1236) originally specified a Widget API +which supports widgets being able to receive specified events from the client, and for widgets to +be able to send more than stickers. + +Sticker support is already specified for widgets, though support for text and image events has been +excluded from the initial specification, as has MSC1236's event receiving support. These components +have been excluded from the specification due to lack of documentation and lack of reference +implementation to influence the spec writing process. + +This proposal aims to bring the functionality originally proposed by MSC1236 into the widget +specification with the accuracy and implementation validation required by modern MSCs. + +## Prerequisite background + +Widgets are relatively new to Matrix and so the terminology and behaviour might not be known to all +readers. This section should clarify the components of widgets that are applicable to this MSC without +going on a deep dive into widgets in general. + +Widgets are embedded HTML/JS/CSS applications in a client which use the `postMessage` API to talk +to the client. This communication allows widgets to provide enhanced functionality such as sticker +pickers (when applied to a user) or performance dashboards (in rooms). + +One of the first things that happens over this communication channel is a "capabilities negotiation" +where the client asks the widget what permissions it wants, and the widget replies with its ideal +set. The client then either decides or asks the user if the permissions requested are okay. + +All communication over the channel is done in a simple request/response flow, using actions to +describe the request. For the capabilities negotiation, this would be the client sending the widget +a request with an `action` of `capabilities`, and the widget would respond to that request with a +response object. + +The channel in which communication occurs is called a "session", where the session is "established" +after the capabilities negotiation. Sessions can only be terminated by the client. + +The Widget API is split into two parts: `toWidget` (client->widget) and `fromWidget` (widget->client). +They are differentiated by where the request originates. + +For a bit of background, stickers are gated by an `m.sticker` capability and have a `m.sticker` +action on the `fromWidget` API. If the widget was granted the capability and sent a valid request +to the client, the client would send an `m.sticker` event to the currently viewed room as the +user. This is all a bit confusing due to the naming of all the identifiers, but the principle +is that there's prior art for sending events from widgets. + +## Proposal (sending events from widgets) + +As mentioned above in the prerequisite background, sticker messages can currently be sent over the +Widget API but other events are not possible. To facilitate sending other event types to the room, +some new capabilities are introduced to allow clients to easily differentiate between custom +capabilities and custom event types (using the `m.sticker` convention could be confusing between a +capability of `com.example.event` and an event type of the same name). + +The new capabilities are: + +* `m.send.event:` (eg: `m.send.event:m.room.message`) - Used for sending room messages of + a given type. +* `m.send.state_event:` (eg: `m.send.state_event:m.room.topic`) - Used for sending state + events of a given type. + +Being able to send other kinds of events (EDUs, account data, etc) is not currently proposed. + +Clients SHOULD automatically deny `m.send.event` and `m.send.state_event` capability requests for +known event types which do not match the descriptor. For example, `m.send.event:m.room.topic` should +be denied, as should `m.send.state_event:m.room.message`. + +As with capabilities negotiation already, the user SHOULD be prompted to approve these capabilities +if the widget requests them. + +For `m.room.message` specifically, the `msgtype` must be appended to the capability following a `#`. +This ensures that widgets cannot interfere with encryption verification. It is expected that most +widgets looking to use this functionality will request the following: + +* `m.send.event:m.room.message#m.notice` +* `m.send.event:m.room.message#m.text` +* `m.send.event:m.room.message#m.emote` + +To actually send the event, widgets would use a new `fromWidget` request with action `send_event` +which takes the following shape: + +```json +{ + "api": "fromWidget", + "widgetId": "20200827_WidgetExample", + "requestid": "generated-id-1234", + "action": "send_event", + "data": { + "state_key": "", + "type": "m.room.topic", + "content": { + "topic": "Hello world!" + } + } +} +``` + +Under `data`, the `state_key` is omitted if the widget is not sending a state event. The other +properties of `data` are required. + +The client is responsible for encrypting the event before sending, if required by the room. The widget +should not need to be made aware of encryption or have to encrypt events. + +If the widget did not get approved for the capability required to send the event, the client MUST +send an error response (as required currently by the capabilities system for widgets). If the widget +was approved, the client MUST only send the event to the room the user is currently viewing. + +The client SHOULD NOT modify the `type`, `state_key`, or `content` of the request unless required for +encryption. The widget is responsible for producing valid events - the client MUST pass through any +errors to the widget using the standard error response in the Widget API. + +For added clarity, the client picks either the `/send` or `/state` endpoint to use on the homeserver +depending on the presence of a `state_key` in the request data. The client then forms a request using +the `type`, `state_key`, and `content` by matching those against the endpoint's parameters, after +encryption if required. + +If the event is successfully sent by the client, the client sends the following response: + +```json +{ + "api": "fromWidget", + "widgetId": "20200827_WidgetExample", + "requestid": "generated-id-1234", + "action": "send_event", + "data": { + "state_key": "", + "type": "m.room.topic", + "content": { + "topic": "Hello world!" + } + }, + "response": { + "room_id": "!room:example.org", + "event_id": "$example" + } +} +``` + +*Note: Widget API responses are a clone of the request with an added `response` field.* + +Both fields of the `response` are required and represent the room ID in which the event was sent, +and the event ID of that event. + +With this new approach, the `m.sticker` capability and associated action are deprecated in favour of +this MSC. If this proposal is able to land in the specification before the widgets spec has a first +release, the `m.sticker` approach described in the prerequisite background section is not to be +included in the release (existing clients may still support it for legacy purposes). + +## Proposal (receiving events in a widget) + +In addition to being able to send events into the room, some widgets have an interest in reacting +to particular events that appear in the room. Using a similar approach to the sending of events, +a new capability matching `m.receive.event:` and `m.receive.state_event:` +are introduced, with the same formatting requirements as the `m.send.event` and `m.send.state_event` +capabilities above (ie: `m.receive.event:m.room.message#m.text`). + +For each event type requested and approved, the client sends a `toWidget` request with action `event` +is sent to the widget with the `data` being the event itself. For example: + +```json +{ + "api": "fromWidget", + "widgetId": "20200827_WidgetExample", + "requestid": "generated-id-1234", + "action": "send_event", + "data": { + "type": "m.room.topic", + "sender": "@alice:example.org", + "event_id": "$example", + "room_id": "!room:example.org", + "state_key": "", + "origin_server_ts": 1574383781154, + "content": { + "topic": "Hello world!" + }, + "unsigned": { + "age": 12345 + } + } +} +``` + +The widget acknowledges receipt of this request with an empty `response` object. + +The client SHOULD only send events which were received by the client *after* the session has been +established with the widget (after the widget's capabilities are negotiated). Clients are expected +to apply the same semantics as the send event capabilities: widgets don't receive `m.emote` msgtypes +unless they asked for it (and were approved), and they receive *decrypted* events. + +## Security considerations + +Because the widget can implicitly decrypt room history, it is absolutely imperative that clients +prompt for permission to use these capabilities even though the capabilities negotation does not +require this to be done. Clients which approve the capabilities proposed by this MSC without +asking the user first are strongly frowned upon. There are very few use cases where not asking for +the user's permission is valid. + +## Unstable prefix + +While this MSC is not present in the spec, clients and widgets should: + +* Use `org.matrix.msc0000.` in place of `m.` in all identifiers of this MSC. +* Only call/support the `action`s if an API version of `org.matrix.msc0000` is advertised. From 748a57f0a419b53634586d01dc3f9a115935731b Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 2 Sep 2020 13:51:06 -0600 Subject: [PATCH 02/11] Assign number --- ...et-event-receiving.md => 2762-widget-event-receiving.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{0000-widget-event-receiving.md => 2762-widget-event-receiving.md} (98%) diff --git a/proposals/0000-widget-event-receiving.md b/proposals/2762-widget-event-receiving.md similarity index 98% rename from proposals/0000-widget-event-receiving.md rename to proposals/2762-widget-event-receiving.md index ed9f3f087bc..1c35a38d550 100644 --- a/proposals/0000-widget-event-receiving.md +++ b/proposals/2762-widget-event-receiving.md @@ -1,4 +1,4 @@ -# MSC0000: Allowing widgets to send/receive events +# MSC2762: Allowing widgets to send/receive events [MSC1236](https://github.com/matrix-org/matrix-doc/issues/1236) originally specified a Widget API which supports widgets being able to receive specified events from the client, and for widgets to @@ -198,5 +198,5 @@ the user's permission is valid. While this MSC is not present in the spec, clients and widgets should: -* Use `org.matrix.msc0000.` in place of `m.` in all identifiers of this MSC. -* Only call/support the `action`s if an API version of `org.matrix.msc0000` is advertised. +* Use `org.matrix.msc2762.` in place of `m.` in all new identifiers of this MSC. +* Only call/support the `action`s if an API version of `org.matrix.msc2762` is advertised. From a6eafc1aad271248f1bd0ef0c61ec8b2e4868192 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 23 Nov 2020 15:52:43 -0700 Subject: [PATCH 03/11] Apply suggestions from code review --- proposals/2762-widget-event-receiving.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2762-widget-event-receiving.md b/proposals/2762-widget-event-receiving.md index 1c35a38d550..45076d9a131 100644 --- a/proposals/2762-widget-event-receiving.md +++ b/proposals/2762-widget-event-receiving.md @@ -158,7 +158,7 @@ is sent to the widget with the `data` being the event itself. For example: ```json { - "api": "fromWidget", + "api": "toWidget", "widgetId": "20200827_WidgetExample", "requestid": "generated-id-1234", "action": "send_event", From 8ad19262f68bda1e57e369a0245b618bbc9841a1 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 23 Nov 2020 16:38:04 -0700 Subject: [PATCH 04/11] Fix parsing of state keys/msgtypes --- proposals/2762-widget-event-receiving.md | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/proposals/2762-widget-event-receiving.md b/proposals/2762-widget-event-receiving.md index 45076d9a131..b1a53867846 100644 --- a/proposals/2762-widget-event-receiving.md +++ b/proposals/2762-widget-event-receiving.md @@ -67,9 +67,22 @@ be denied, as should `m.send.state_event:m.room.message`. As with capabilities negotiation already, the user SHOULD be prompted to approve these capabilities if the widget requests them. -For `m.room.message` specifically, the `msgtype` must be appended to the capability following a `#`. -This ensures that widgets cannot interfere with encryption verification. It is expected that most -widgets looking to use this functionality will request the following: +State events can have their capabilities requested against specific state keys as well, helping the +client limit its exposure to the room's history. This is done by appending a `#` and the state key +the capability should be against. For example, `m.send.state_event:m.room.name#` will represent an +`m.room.name` state event with an empty state key whereas `m.send.state_event:m.room.name#test` will +be an `m.room.name` state event still, though with `test` as the state key. Clients should only split +on the first `#`, so `m.room.name##test` becomes an event type of `m.room.name` and state key of `#test`. + +To get around an issue where widgets would not be able to request an event type with `#` in it (because +it'll be seen as a state key), widgets can use a `\` character to escape the `#`. For example, +`org.example.\#test#hello` would be parsed as an event type of `org.example.#test` with state key `hello`. +Clients should be careful to parse `\\#` as `\#` (single escape). + +`m.room.message` is the only non-state event which also makes use of this `#` system, though targeting +the `msgtype` of a `m.room.message` event instead. All the same rules apply as they do to state events, +except instead to `msgtype`. This ensures that widgets cannot interfere with encryption verification. +It is expected that most widgets looking to use this functionality will request the following: * `m.send.event:m.room.message#m.notice` * `m.send.event:m.room.message#m.text` From 3aa58ee029f923ecdffe8b307fd328d984881647 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 23 Nov 2020 16:39:21 -0700 Subject: [PATCH 05/11] Clarify rules for other event types --- proposals/2762-widget-event-receiving.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/proposals/2762-widget-event-receiving.md b/proposals/2762-widget-event-receiving.md index b1a53867846..9d174174914 100644 --- a/proposals/2762-widget-event-receiving.md +++ b/proposals/2762-widget-event-receiving.md @@ -88,6 +88,8 @@ It is expected that most widgets looking to use this functionality will request * `m.send.event:m.room.message#m.text` * `m.send.event:m.room.message#m.emote` +Other non-state event types with `#` in them do not get parsed in any special way, and do not need escaping. + To actually send the event, widgets would use a new `fromWidget` request with action `send_event` which takes the following shape: From 9a067449d7253482a7801c24746d6c2ee38fb887 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 14 May 2021 14:01:10 -0600 Subject: [PATCH 06/11] Absorb MSC2876 --- proposals/2762-widget-event-receiving.md | 99 ++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/proposals/2762-widget-event-receiving.md b/proposals/2762-widget-event-receiving.md index 9d174174914..a5ce5243b76 100644 --- a/proposals/2762-widget-event-receiving.md +++ b/proposals/2762-widget-event-receiving.md @@ -201,6 +201,99 @@ established with the widget (after the widget's capabilities are negotiated). Cl to apply the same semantics as the send event capabilities: widgets don't receive `m.emote` msgtypes unless they asked for it (and were approved), and they receive *decrypted* events. +Widgets can also read the events they were approved to receive on demand with the following `fromWidget` +API action: + +```json +{ + "api": "fromWidget", + "widgetId": "20200827_WidgetExample", + "requestid": "generated-id-1234", + "action": "read_events", + "data": { + "state_key": "", + "type": "m.room.topic", + "limit": 25 + } +} +``` + +When a `state_key` is present, the client will respond with state events matching that state key. If +`state_key` is instead a boolean `true`, the client will respond with state events of the given type +with any state key. For clarity, `"state_key": "@alice:example.org"` would return the state event with +the specified state key (there can only be one or zero), while `"state_key": true` would return any +state events of the type, regardless of state key. + +To support the ability to read particular msgtypes, the widget can specify a `msgtype` in place of the +`state_key` for `m.room.message` requests. + +The `type` is simply the event type to go searching for. + +The `limit` is the number of events the widget is looking for. The client can arbitrarily decide to +return less than this limit, though should never return more than the limit. For example, a client +may decide that for privacy reasons a widget can only ever see the last 5 room messages - even though +the widget requested 25, it will only ever get 5 maximum back. When `limit` is not present it is +assumed that the widget wants as many events as the client will give it. When negative, the client +can reject the request with an error. + +The recommended maximum `limit`s are: + +* For `m.room.member` state events, no limit. +* For all other events, 25. + +The client is not required to backfill (use the `/messages` endpoint) to get more events for the +client, and is able to return less than the requested amount of events. When returning state events, +the client should always return the current state event (in the client's view) rather than the history +of an event. For example, `{"type":"m.room.topic", "state_key": "", "limit": 5}` should return zero +or one topic events, not 5, even if the topic has changed more than once. + +The client's response would look like so (note that because of how Widget API actions work, the request +itself is repeated in the response - the actual response from the client is held within the `response` +object): + +```json +{ + "api": "fromWidget", + "widgetId": "20200827_WidgetExample", + "requestid": "generated-id-1234", + "action": "read_events", + "data": { + "state_key": "", + "type": "m.room.topic", + "limit": 25 + }, + "response": { + "events": [ + { + "type": "m.room.topic", + "sender": "@alice:example.org", + "event_id": "$example", + "room_id": "!room:example.org", + "state_key": "", + "origin_server_ts": 1574383781154, + "content": { + "topic": "Hello world!" + }, + "unsigned": { + "age": 12345 + } + } + ] + } +} +``` + +The `events` array is simply the array of events requested. When no matching events are found, this +array must be defined but can be empty. + +## Alternatives + +Widgets could be powered by a bot or some sort of backend which allows them to filter the room state +and timeline themselves, however this can be a large amount of infrastructure for a widget to maintain +and the user experience is not as great. The client already has most of the information a widget would +need, and trying to interact through a bot would generally mean slower response times or technical +challenges on the part of the widget developer. + ## Security considerations Because the widget can implicitly decrypt room history, it is absolutely imperative that clients @@ -209,6 +302,12 @@ require this to be done. Clients which approve the capabilities proposed by this asking the user first are strongly frowned upon. There are very few use cases where not asking for the user's permission is valid. +This MSC allows widgets to arbitrarily read history from a room without the user necessarily knowing. +Clients should apply strict limits to the number of events they are willing to provide to widgets +and ensure that users are prompted to explicitly approve the permissions requested, like in MSC2762. + +Clients may also wish to consider putting iconography next to room messages when a widget reads them. + ## Unstable prefix While this MSC is not present in the spec, clients and widgets should: From 8eedf1d6c4028a39d8357b63bdaefaae63538dae Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 24 Aug 2021 14:58:02 -0600 Subject: [PATCH 07/11] Add timeline access --- proposals/2762-widget-event-receiving.md | 59 +++++++++++++++++++++--- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/proposals/2762-widget-event-receiving.md b/proposals/2762-widget-event-receiving.md index a5ce5243b76..fd2185cc8f4 100644 --- a/proposals/2762-widget-event-receiving.md +++ b/proposals/2762-widget-event-receiving.md @@ -10,7 +10,11 @@ have been excluded from the specification due to lack of documentation and lack implementation to influence the spec writing process. This proposal aims to bring the functionality originally proposed by MSC1236 into the widget -specification with the accuracy and implementation validation required by modern MSCs. +specification with the accuracy and implementation validation required by modern MSCs. Additionally, +this MSC explores options for widgets being able to see events/state for rooms in which they aren't +operating directly. An example usecase of this is a calendar system built on top of Matrix where a +calendar view might belong to a room but needs information from "calendar event rooms". The widget +would therefore need to query state from these other rooms. ## Prerequisite background @@ -115,13 +119,18 @@ properties of `data` are required. The client is responsible for encrypting the event before sending, if required by the room. The widget should not need to be made aware of encryption or have to encrypt events. -If the widget did not get approved for the capability required to send the event, the client MUST -send an error response (as required currently by the capabilities system for widgets). If the widget -was approved, the client MUST only send the event to the room the user is currently viewing. +The widget can add an additional `room_id` property to the `data` object if it would like to target +a specific room. This requires that the widget be approved for sending to that room, which is dicussed +later in this document. + +If the widget did not get approved for the capability/capabilities required to send the event, the +client MUST send an error response (as required currently by the capabilities system for widgets). If +the widget has permission to send to the room, defaulting to whichever room the user is currently +viewing, the client MUST try to send the event to that room. The client SHOULD NOT modify the `type`, `state_key`, or `content` of the request unless required for encryption. The widget is responsible for producing valid events - the client MUST pass through any -errors to the widget using the standard error response in the Widget API. +errors, such as permission errors, to the widget using the standard error response in the Widget API. For added clarity, the client picks either the `/send` or `/state` endpoint to use on the homeserver depending on the presence of a `state_key` in the request data. The client then forms a request using @@ -201,6 +210,9 @@ established with the widget (after the widget's capabilities are negotiated). Cl to apply the same semantics as the send event capabilities: widgets don't receive `m.emote` msgtypes unless they asked for it (and were approved), and they receive *decrypted* events. +Note that the client should also be sending the widget any events in rooms where the widget is permitted +to receive events from. The exact details of these permissions are covered later in this document. + Widgets can also read the events they were approved to receive on demand with the following `fromWidget` API action: @@ -238,8 +250,8 @@ can reject the request with an error. The recommended maximum `limit`s are: -* For `m.room.member` state events, no limit. -* For all other events, 25. +* For `m.room.member` state events, no limit per room. +* For all other events, 25 per room. The client is not required to backfill (use the `/messages` endpoint) to get more events for the client, and is able to return less than the requested amount of events. When returning state events, @@ -247,6 +259,11 @@ the client should always return the current state event (in the client's view) r of an event. For example, `{"type":"m.room.topic", "state_key": "", "limit": 5}` should return zero or one topic events, not 5, even if the topic has changed more than once. +An optional `room_ids` property may also be added to the `data` object by the widget, indicating which +room(s) to listen for events in. This is either an array of room IDs, undefined, or the special string +`"*"` to denote "any room in which the widget has permission for reading that event" (covered later). +When undefined, the client should send events sent in the user's currently viewed room only. + The client's response would look like so (note that because of how Widget API actions work, the request itself is repeated in the response - the actual response from the client is held within the `response` object): @@ -286,6 +303,27 @@ object): The `events` array is simply the array of events requested. When no matching events are found, this array must be defined but can be empty. +## Proposal (accessing other rooms) + +As mentioned earlier in this MSC, widgets are typically limited to the room in which the user is currently +viewing - they cannot typically reach out into other rooms or see what other rooms are out there. This +has limitations on certain kinds of widgets which rely on room structures to store data outside of a +single canonical room, however. + +To complement the send/receive event capabilities, a single capability is introduced to access the timelines +of other rooms: `m.timeline:`. The `` can either be an actual room ID, or a `*` to denote +all joined or invited rooms the client is able to see, current and future. The widget can limit its exposure +by simply requesting highly scoped send/receive capabilities to accompany the timeline capability. + +Do note that a widget does not need to request capabilities for all rooms if it only ever interacts with the +user's currently viewed room. Widgets such as stickerpickers will not need to request timeline capabilities +because they'll always send events to the user's currently viewed room, and the client will let them do that +without special room timeline permissions. + +There is no Widget API action exposed for listing the user's invited/joined rooms: the widget can request +permission to read/receive the `m.room.create` state event of rooms and query that way. Clients should be +aware of this trick and describe the situation appropriately to users. + ## Alternatives Widgets could be powered by a bot or some sort of backend which allows them to filter the room state @@ -308,6 +346,13 @@ and ensure that users are prompted to explicitly approve the permissions request Clients may also wish to consider putting iconography next to room messages when a widget reads them. +This MSC allows widgets to arbitrarily access/modify history in, at worst, all of the user's rooms. +Clients should apply strict limits or checks to ensure the user understands what the widget is trying +to do and isn't unreasonably accessing the user's account. For example, a large warning saying that +a room-based widget is trying to access messages in *all rooms* might be suitable. Another approach +might be to simply limit the number of rooms a widget can access, requiring the widget to know what +room IDs it specifically wants (ie: denying the `*` request on behalf of the user). + ## Unstable prefix While this MSC is not present in the spec, clients and widgets should: From 7786f95464d993ee5f3e434974aeffcdc216ee5e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 24 Aug 2021 15:00:00 -0600 Subject: [PATCH 08/11] widget --- proposals/2762-widget-event-receiving.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/2762-widget-event-receiving.md b/proposals/2762-widget-event-receiving.md index fd2185cc8f4..99dc8263b7c 100644 --- a/proposals/2762-widget-event-receiving.md +++ b/proposals/2762-widget-event-receiving.md @@ -254,7 +254,7 @@ The recommended maximum `limit`s are: * For all other events, 25 per room. The client is not required to backfill (use the `/messages` endpoint) to get more events for the -client, and is able to return less than the requested amount of events. When returning state events, +widget, and is able to return less than the requested amount of events. When returning state events, the client should always return the current state event (in the client's view) rather than the history of an event. For example, `{"type":"m.room.topic", "state_key": "", "limit": 5}` should return zero or one topic events, not 5, even if the topic has changed more than once. From 853bed215743f3e04af8683ea5e1ea2b47b7845b Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 25 Aug 2021 22:34:24 -0600 Subject: [PATCH 09/11] Special case redactions for now --- proposals/2762-widget-event-receiving.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/proposals/2762-widget-event-receiving.md b/proposals/2762-widget-event-receiving.md index 99dc8263b7c..6a560416883 100644 --- a/proposals/2762-widget-event-receiving.md +++ b/proposals/2762-widget-event-receiving.md @@ -169,6 +169,13 @@ this MSC. If this proposal is able to land in the specification before the widge release, the `m.sticker` approach described in the prerequisite background section is not to be included in the release (existing clients may still support it for legacy purposes). +### Special case: Redactions + +Due to the `redacts` key being at the top level, [at least for now](https://github.com/matrix-org/matrix-doc/pull/2174), +clients should interpret a `redacts` in the content for `m.room.redaction` events as needing to call +the [`/redact` endpoint](https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-rooms-roomid-redact-eventid-txnid) +on behalf of the widget. + ## Proposal (receiving events in a widget) In addition to being able to send events into the room, some widgets have an interest in reacting From aeadae81e2d68f76523eb61ff0ebbbd5c3202deb Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 30 Aug 2021 16:16:08 -0600 Subject: [PATCH 10/11] Remove arbitrary limits --- proposals/2762-widget-event-receiving.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/proposals/2762-widget-event-receiving.md b/proposals/2762-widget-event-receiving.md index 6a560416883..d851ac4a9c3 100644 --- a/proposals/2762-widget-event-receiving.md +++ b/proposals/2762-widget-event-receiving.md @@ -255,10 +255,9 @@ the widget requested 25, it will only ever get 5 maximum back. When `limit` is n assumed that the widget wants as many events as the client will give it. When negative, the client can reject the request with an error. -The recommended maximum `limit`s are: - -* For `m.room.member` state events, no limit per room. -* For all other events, 25 per room. +There is no recommended maximum `limit`, though clients will want to consider local limitations in +being able to send events. Web clients, for example, may be more able to send *every* event it knows +about. The default assumption is that the client will send over as much as possible as an upper limit. The client is not required to backfill (use the `/messages` endpoint) to get more events for the widget, and is able to return less than the requested amount of events. When returning state events, From 1f32d05f0f0844d7d3f094769e86fd2e36bba448 Mon Sep 17 00:00:00 2001 From: Robin Date: Wed, 15 Jan 2025 12:47:26 -0500 Subject: [PATCH 11/11] Distinguish room state and timeline events in MSC2762 (#4237) * Distinguish room state and timeline events in MSC2762 * Change update_state to an array (to replace room_state_synced) --- proposals/2762-widget-event-receiving.md | 74 +++++++++++++++++++----- 1 file changed, 61 insertions(+), 13 deletions(-) diff --git a/proposals/2762-widget-event-receiving.md b/proposals/2762-widget-event-receiving.md index d851ac4a9c3..c5869027721 100644 --- a/proposals/2762-widget-event-receiving.md +++ b/proposals/2762-widget-event-receiving.md @@ -57,7 +57,7 @@ capability of `com.example.event` and an event type of the same name). The new capabilities are: -* `m.send.event:` (eg: `m.send.event:m.room.message`) - Used for sending room messages of +* `m.send.event:` (eg: `m.send.event:m.room.message`) - Used for sending non-state events of a given type. * `m.send.state_event:` (eg: `m.send.state_event:m.room.topic`) - Used for sending state events of a given type. @@ -180,12 +180,12 @@ on behalf of the widget. In addition to being able to send events into the room, some widgets have an interest in reacting to particular events that appear in the room. Using a similar approach to the sending of events, -a new capability matching `m.receive.event:` and `m.receive.state_event:` +new capabilities matching `m.receive.event:` and `m.receive.state_event:` are introduced, with the same formatting requirements as the `m.send.event` and `m.send.state_event` capabilities above (ie: `m.receive.event:m.room.message#m.text`). -For each event type requested and approved, the client sends a `toWidget` request with action `event` -is sent to the widget with the `data` being the event itself. For example: +For each event type requested and approved, the client sends a `toWidget` request with action +`send_event` to the widget, with the `data` being the event itself. For example: ```json { @@ -220,6 +220,54 @@ unless they asked for it (and were approved), and they receive *decrypted* event Note that the client should also be sending the widget any events in rooms where the widget is permitted to receive events from. The exact details of these permissions are covered later in this document. +## Proposal (receiving room state in a widget) + +When a widget is approved to receive some state events, the client begins syncing all room state +entries matching the capabilities in rooms where the widget is permitted to receive events. It +communicates the current state by sending a `toWidget` request with action `update_state`. + +```json +{ + "api": "toWidget", + "widgetId": "20200827_WidgetExample", + "requestid": "generated-id-1234", + "action": "update_state", + "data": { + "state": [ + { + "type": "m.room.topic", + "sender": "@alice:example.org", + "event_id": "$example", + "room_id": "!room:example.org", + "state_key": "", + "origin_server_ts": 1574383781154, + "content": { + "topic": "Hello world!" + }, + "unsigned": { + "age": 12345 + } + } + ] + } +} +``` + +`data.state` is an array of state events representing the current values of the room state for each +(`room_id`, `type`, `state_key`) tuple. The widget acknowledges receipt of this request with an +empty `response` object. + +Whenever a widget is granted the ability to receive some room state (through a capability +negotiation or renegotiation), the widget may wait upon the next `update_state` action to know when +the requested room state has finished loading. Therefore, if all new room state entries that the +widget may receive are empty, the client must send an `update_state` action with an empty +`data.state` array. + +The client continues sending `update_state` actions whenever it observes a change in the relevant +room state. Each action only has to mention the events that changed. + +## Proposal (reading events in a widget) + Widgets can also read the events they were approved to receive on demand with the following `fromWidget` API action: @@ -239,9 +287,12 @@ API action: When a `state_key` is present, the client will respond with state events matching that state key. If `state_key` is instead a boolean `true`, the client will respond with state events of the given type -with any state key. For clarity, `"state_key": "@alice:example.org"` would return the state event with -the specified state key (there can only be one or zero), while `"state_key": true` would return any -state events of the type, regardless of state key. +with any state key. + +For clarity, the state events returned should *not* be understood to represent the current state of +the room. Rather, they are simply events from the room's timeline that match the requested filter, +and may or may not belong to the resolved room state. Multiple events may be returned, even when +requesting a specific state key. To support the ability to read particular msgtypes, the widget can specify a `msgtype` in place of the `state_key` for `m.room.message` requests. @@ -260,10 +311,7 @@ being able to send events. Web clients, for example, may be more able to send *e about. The default assumption is that the client will send over as much as possible as an upper limit. The client is not required to backfill (use the `/messages` endpoint) to get more events for the -widget, and is able to return less than the requested amount of events. When returning state events, -the client should always return the current state event (in the client's view) rather than the history -of an event. For example, `{"type":"m.room.topic", "state_key": "", "limit": 5}` should return zero -or one topic events, not 5, even if the topic has changed more than once. +widget, and is able to return less than the requested amount of events. An optional `room_ids` property may also be added to the `data` object by the widget, indicating which room(s) to listen for events in. This is either an array of room IDs, undefined, or the special string @@ -327,8 +375,8 @@ because they'll always send events to the user's currently viewed room, and the without special room timeline permissions. There is no Widget API action exposed for listing the user's invited/joined rooms: the widget can request -permission to read/receive the `m.room.create` state event of rooms and query that way. Clients should be -aware of this trick and describe the situation appropriately to users. +permission to receive the `m.room.create` state entries of rooms and learn about them that way. Clients +should be aware of this trick and describe the situation appropriately to users. ## Alternatives