diff --git a/proposals/2312-matrix-uri.md b/proposals/2312-matrix-uri.md new file mode 100644 index 00000000..21898007 --- /dev/null +++ b/proposals/2312-matrix-uri.md @@ -0,0 +1,496 @@ +# URI scheme for Matrix + +This is a proposal of a URI scheme to identify Matrix resources in a wide +range of applications (web, desktop, or mobile) both throughout Matrix software +and (especially) outside of it. It supersedes +[MSC455](https://github.com/matrix-org/matrix-doc/issues/455) in order +to continue the discussion in the modern GFM style. + +While Matrix has its own resource naming system that allows it to identify +resources without resolving them, there is a common need to provide URIs +to Matrix resources (e.g., rooms, users, PDUs) that could be transferred +outside of Matrix and then resolved in a uniform way - matching URLs +in World Wide Web. + +Specific use cases include: +1. Representation in UI: as a Matrix user I want to refer to Matrix entities + in the same way as for web pages, so that others could “follow the link” + I sent them (not necessarily through Matrix, it can be, e.g., a web page or + email) in order to access the referred resource. +1. Inbound integration: as an author of Matrix software, I want to have a way + to invoke my software from the operating environment to resolve a Matrix URI + passed from another program. This is a case of, e.g., + opening a Matrix client by clicking on a link in an email program. +1. Outbound integration: as an author of Matrix software, I want to have a way + to export identifiers of Matrix resources as URIs to non-Matrix environment + so that they could be resolved in another time-place in a uniform way. + This is a case of "Share via…" action in a mobile Matrix client. + +https://matrix.to somehow compensates for the lack of dedicated URIs; however: +* it addresses use case (1) in a somewhat clumsy way (resolving a link needs + two interactions with the user instead of one), and +* it can only deal with (2) within a web browser (basically limiting + first-class support to browser-based clients). + +To cover the use cases above, the following scheme is proposed for Matrix URIs +(`[]` enclose optional parts, `{}` enclose variables): +```text +matrix:[//{authority}/]{type}/{id without sigil}[/{more type/id pairs}][?{query}] +``` +with `type` being one of: `user`, `room`, `roomid`, `event` and +`group`; and the only `query` defined for now is `action=join`. The Matrix +identifier (or identifiers) can be reconstructed from the URI by taking +the sigil that corresponds to `type` and appending `id without sigil` to it. +The query may alter the kind of request with respect to the Matrix resource; +and Matrix resources can be contained in each other, in which care the +`more type/id pairs` series is used to reconstruct inner identifiers. + +This proposal defines initial mapping of URIs to Matrix identifiers and actions +on corresponding resources; the scheme and mapping are subject +to further extension. + +Examples: +* Room `#someroom:example.org`: + `matrix:room/someroom:example.org` +* Unfederated room `#internalroom:internal.example.org`: + `matrix://internal.example.org/room/internalroom:internal.example.org` +* Event in a room: + `matrix:room/someroom:example.org/event/Arbitrary_Event_Id` +* [A commit like this](https://github.com/her001/steamlug.org/commit/2bd69441e1cf21f626e699f0957193f45a1d560f) + could make use of a Matrix URI in the form of + `{Matrix identifier}`. + +This MSC does not introduce new Matrix entities, nor API endpoints - +it merely defines a mapping from a URI with the scheme name `matrix:` +to Matrix identifiers and actions on them. It is deemed sufficient to +produce an implementation that would convert Matrix URIs to a series +of CS API calls, entirely on the client side. It is recognised, +however, that most of URI processing logic can and should (eventually) +be on the server side in order to facilitate adoption of Matrix URIs; +further MSCs are needed to define details for that. + +## Proposal + +Further text uses “Matrix identifier” with a meaning of identifiers +as described by [Matrix Specification](https://matrix.org/docs/spec/), +and “Matrix URI” with a meaning of an identifier following +the RFC-compliant URI format proposed hereby. + +### Requirements + +The following considerations drive the requirements for Matrix URIs: +1. Follow existing standards and practices. +1. Endorse the principle of least surprise. +1. Humans first, machines second. +1. Cover as many entities as practical. +1. URIs are expected to be extremely portable and stable; + you cannot rewrite them once they are released to the world. +1. Ease of implementation, allowing reuse of existing codes. + +The following requirements resulted from these drivers +(see [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt) for conventions +around MUST/SHOULD/MAY): +1. Matrix URI MUST comply with + [RFC 3986](https://tools.ietf.org/html/rfc3986) and + [RFC 7595](https://tools.ietf.org/html/rfc7595). +1. By definition, Matrix URI MUST unambiguously identify a resource + in a Matrix network, across servers and types of resources. + This means, in particular, that two Matrix identifiers distinct by + [Matrix Specification](https://matrix.org/docs/spec/appendices.html#identifier-grammar) + MUST NOT have Matrix URIs that are equal in + [RFC 3986](https://tools.ietf.org/html/rfc3986) sense + (but two distinct Matrix URIs MAY map to the same Matrix identifier). +1. The following classes MUST be supported: + 1. User IDs (`@user:example.org`) + 1. Room IDs (`!roomid:example.org`) + 1. Room aliases (`#roomalias:example.org`) + 1. Event IDs (`$eventid:example.org`) +1. The mapping MUST take into account that some identifiers + (e.g. aliases) can have non-ASCII characters - reusing + [RFC 3987](https://tools.ietf.org/html/rfc3987) is RECOMMENDED + but an alternative encoding can be used if there are reasons + for that. +1. The mapping between Matrix identifiers and Matrix URIs MUST + be extensible (without invalidating previous URIs) to: + 1. new classes of identifiers (there MUST be a meta-rule to produce + a new mapping for IDs following the `&somethingnew:example.org` + pattern assumed for Matrix identifiers); + 1. new ways to navigate to and interact with objects in Matrix + (e.g., we might eventually want to have a mapping for + room-specific user profiles). +1. The mapping MUST support decentralised as well as centralised IDs. + This basically means that the URI scheme MUST have provisions + for mapping of `:` but it MUST NOT require + `:` to be there. +1. Matrix URI SHOULD allow encoding of action requests such as joining + a room. +1. Matrix URI SHOULD have a human-readable, if not necessarily + human-friendly, representation - to allow visual sanity-checks. + In particular, characters escaping/encoding should be reduced + to bare minimum in that representation. As a food for thought, see + [Wikipedia: Clean URL, aka SEF URL](https://en.wikipedia.org/wiki/Clean_URL) and + [a very relevant use case from RFC 3986](https://tools.ietf.org/html/rfc3986#section-1.2.1). +1. It SHOULD be easy to parse Matrix URI in popular programming + languages: e.g., one should be able to use `parseUri()` + to dissect a Matrix URI in JavaScript. +1. The mapping SHOULD be consistent across different classes of + Matrix identifiers. +1. The mapping SHOULD support linking to unfederated servers/networks + (see also + [matrix-doc#2309](https://github.com/matrix-org/matrix-doc/issues/2309) + that calls for such linking). + +The syntax and mapping discussed below meet all these requirements. +Further extensions MUST comply to them as well. + +### Syntax and high-level processing + +The proposed generic Matrix URI syntax is a subset of the generic +URI syntax +[defined by RFC 3986](https://tools.ietf.org/html/rfc3986#section-3): +```text +MatrixURI = “matrix:” hier-part [ “?” query ] [ “#” fragment ] +hier-part = [ “//” authority “/” ] path +``` +As mentioned above, this MSC assumes client-side URI processing +(i.e. mapping to Matrix identifiers and CS API requests). +However, even when URI processing is shifted to the server side +the client will still have to parse the URI at least to find +the authority part (or lack of it) and remove the fragment part +before sending the request to the server (more on that below). + +#### Scheme name +The proposed scheme name is `matrix`. +[RFC 7595](https://tools.ietf.org/html/rfc7595) states: + + if there’s one-to-one correspondence between a service name and + a scheme name then the scheme name should be the same as + the service name. + +Other considered options were `mx` and `web+matrix`; +[comments to MSC455](https://github.com/matrix-org/matrix-doc/issues/455) +mention two scheme names proposed and one more has been mentioned +in `#matrix-core:matrix.org`). + +The scheme name is a definitive indication of a Matrix URI and MUST NOT +be omitted. As can be seen below, Matrix URI rely heavily on [relative +references](https://tools.ietf.org/html/rfc3986#section-4.2) and +omitting the scheme name makes them indistinguishable from a local path +that might have nothing to do with Matrix. Clients MUST NOT try to +parse pieces like `room/MyRoom:example.org` as Matrix URIs; instead, +users should be encouraged to use Matrix IDs for in-text references +(`#MyRoom:example.org`) and client applications should do +the heavy-lifting of turning them into hyperlinks to Matrix URIs. + +#### Authority + +The authority part is used for the specific case of getting access +to a Matrix resource (such as a room or a user) through a given server. +```text +authority = host [ “:” port ] +``` + +Here's an example of a Matrix URI with an authority part +(the authority part is `example.org:682` here): +`matrix://example.org:682/roomid/Internal_Room_Id:example2.org`. + +The main purpose of the authority part, +[as per RFC 3986](https://tools.ietf.org/html/rfc3986#section-3.2), +is to identify the authority governing the namespace for the rest +of the URI. This MSC reuses the RFC definitions for +[`host`](https://tools.ietf.org/html/rfc3986#section-3.2.2) and +[`port`](https://tools.ietf.org/html/rfc3986#section-3.2.3). +RFC 3986 also includes provisions for user information - +this MSC explicitly omits them. If providing a user identity +in the authority part is found to be of value in some case, +this can be addressed in a separate MSC. + +When a Matrix client needs to obtain the resource specified by a URI +with an authority part, the client MUST, +_instead of engaging the current user's homeserver connection_: +1. resolve the homeserver (further referred to as authority server) using + [the standard process for server discovery](https://matrix.org/docs/spec/client_server/latest#server-discovery) + from the authority part; +1. interrogate the resolved authority server using Matrix APIs in order + to get or interact with the resource encoded in the URI. + +Importantly, the authority part is _not_ intended for usage in routing +over federation; rather, it is for cases when a given Matrix +entity is not expected to be reachable through federation (such as +unfederated rooms or non-public Matrix networks). Sending requests +to the server resolved from the authority part means that the client +should be, as the name implies, _authorised_ by the authority server +to access the requested resource. That implies that the resource +is either available to guests on the authority server, or the end user +must be authenticated (and their access rights checked) +on that server in order to access the resource. + +#### Path +Unlike the very wide definition of path in RFC 3986, this MSC +restricts the path component of a Matrix URI to a simple +pattern that allows to easily reconstruct a Matrix identifier or +a chain of identifiers: +```text +path = type “/” id-without-sigil “/” path +type = “user” / “roomid” / “room” / “event” / “group” +id-without-sigil = string ; see below +``` +The path component consists of 1 or more type-id pairs separated +by slash character both inside the pair and between pairs. While most +of the URIs considered in this MSC do not need any sort of hierarchy, +one case is standing out: as of now, events require rooms to be +resolved so an event URI for `$eventid` in the room +`!roomid:example2.org` would be +`matrix:room/roomid:example2.org/event/eventid`. + +This MSC defines the following type specifiers: +`user` (user id, sigil `@`), `roomid` (room id, sigil `!`), +`room` (room alias, sigil `#`), and `event` (event id, sigil `$`). +The type `group` (group/community id, sigil `+`) is reserved for future use. + +`id-without-sigil` is defined as the `string` part of Matrix +[Common identifier format](https://matrix.org/docs/spec/appendices#common-identifier-format) with percent-encoded characters that are NEITHER +unreserved, sub-delimiters, `:` nor `@`, +[as per RFC 3986 rule for pchar](https://tools.ietf.org/html/rfc3986#appendix-A). +This notably exempts `:` from percent-encoding but includes `/`. + +See subsections of the same section for details on each type of +identifier. + +The rationale behind dropping sigils is two-fold: +* (most important) Semantic and grammatic clashes with RFC. + `#` delimits a fragment; `@` delimits user information. Percent-encoding + these is not a reasonable option, given that the two are the most used sigils. +* The namespace for sigils is extremely limited. Not that Matrix showed + any tendency to produce plenty of various classes of objects but + that cannot be ruled out; so rather then hit the wall together + with the institute of sigils, this MSC proposes + a more extensible solution from the outset. + +Clients MUST resolve all Matrix entities that they support. + +Further MSCs may introduce navigation to more top-level as well as +non-top-level objects; e.g., one could conceive a URI mapping of avatars +in the form of +`matrix:user/uid:matrix.org/avatar/room:matrix.org` +(a user’s avatar for a given room). + +#### Query + +Matrix URI can optionally have +[the query part](https://tools.ietf.org/html/rfc3986#section-3.4). +This MSC defines only two specific forms for the query; further MSCs +may add to this as long as RFC 3986 is followed. +```text +query = query-element *( “&” query-element ) +query-element = action / routing +action = “action=join” +routing = “via=” authority +``` +The join action can only be used with a URI resolving to a room; applications +MUST ignore it if found and MUST NOT generate it for other Matrix resources. +This action means that rather than just navigate to the room +client applications SHOULD attempt to join it using the standard CS API means. +Client applications SHOULD ask for user confirmation before joining if the user +is not a joined member in the room nor in its immediate +[successor or predecessor](https://matrix.org/docs/spec/client_server/latest#module-room-upgrades). + +The routing query is only allowed with URIs that have path starting with +`roomid/` to indicate servers that are likely involved in the room (cf. +[the feature of matrix.to](https://matrix.org/docs/spec/appendices#routing)). +For other URIs it SHOULD be ignored and clients SHOULD NOT generate it. Note +that `authority` here only represents the server grammar as defined in +the respective section above; it doesn't need to coincide with the actual +authority server if it's also supplied in the URI. + + +### URI semantics + +The main purpose of a Matrix URI is accessing the resource specified by the +identifier, and the primary action is loading the contents of a document +corresponding to a given resource. This MSC defines the "default" action +that a client application SHOULD perform when the user activates +(e.g. clicks on) a URI; further MSCs may introduce additional actions enabled +either by passing an `action` value in the query part, or by other means. +The classes of URIs and corresponding default actions (along with relevant +CS API calls) are collected as follows: + +* User ID: + - URI example: `matrix:user/me:example.org` or + (decentralised user id, future) `matrix:user/me_in_matrix` + - Default action: Show user profile (`GET /profile/@me:example.org/...`, + `GET /profile/@me_in_matrix/...`) +* Room ID: + - URI example: `matrix:roomid/rid:example.org` or + (decentralised id, future) `!lol823y4bcp3qo4` + - Default action: attempt to "open" the room (usually means the client + at least displays the room timeline at the latest or + the last remembered position - `GET /rooms/!rid:example.org/...`, + `GET /rooms/!lol823y4bcp3qo4/...`) +* Joining by Room ID: + - URI example: `matrix:roomid/rid:example.org?action=join&via=example2.org` + - Default action: if needed (see the section about the query part) ask + the user to confirm the intention; then join the room + (`POST /join/!rid:example.org?server_name=example2.org`) +* Room alias: + - URI example: `matrix:room/us:example.org` + - Default action: resolve the alias to room id + (`GET /directory/room/#us:example.org` if needed) and attempt to "open" + the room (same as above) +* Joining by Room alias: + - URI example: `matrix:room/us:example.org?action=join` + - Default action: if needed (see the section about the query part) ask + the user to confirm the intention; then join the room + (`POST /join/#us:example.org`) +* Event ID (as used in + [room version 3](https://matrix.org/docs/spec/rooms/v3) and later): + - URI example (aka "event permalink"): + `matrix:room/us:example.org/event/UnpaddedBase64` or + (additional routing is needed for room id) + `matrix:roomid/rid:example.org/event/UnpaddedBase64?via=example2.org` + - Default action: + 1. For room aliases, resolve an alias to a room id (see above) + 1. If the event is in the room that the user has joined, retrieve + the event contents (`GET /rooms/!rid:example.org/event/UnpaddedBase64` or + `GET /rooms/!rid:example.org/context/UnpaddedBase64`) and display them + to the user + 1. Otherwise try to retrieve the event in the same way but in case of + access failure the client MAY offer the user to join the room; if + the user agrees and joining succeeds, retry the step above. +* Group ID: + - URI example: `matrix:group/them:matrix.org` + - Default action: reserved for future use + + +## Discussion points and tradeoffs + +The below documents the discussion and outcomes in various prior forums; +further discussion should happen in GitHub comments. +1. _Why no double-slashes in a typical URI?_ + Because `//` is used to mark the beginning of an authority + part. RFC 3986 explicitly forbids to start the path component with + `//` if the URI doesn't have an authority component. In other words, + `//` implies a centre of authority, and the (public) Matrix + federation is not supposed to have one; hence no `//` in most URIs. +1. _Why type specifiers use singular rather than plural + as is common in RESTful APIs?_ + Unlike in actual RESTful APIs, this MSC does not see `rooms/` or + `users/` as collections to browse. The type specifier completes + the id specification in the URI, defining a very specific and + easy to parse syntax for that. Future MSCs may certainly add + collection URIs, but it is recommended to use more distinct naming + for such collections. In particular, `rooms/` is ambiguous, as + different sets of rooms are available to any user at any time + (e.g., all rooms known to the user; or all routable rooms; or + public rooms known to the user's homeserver). +1. (still open) + _Should we advise using the query part for collections then?_ +1. (still open) + _Why not Reddit-style single-letter type specifiers? That's almost + as compact as a sigil, still pretty clearly conveys the type, + and nicely avoids the confusion described in the previous question._ + Readability? +1. _Why event URI cannot use the fragment part for the event id?_ + Because fragment is a part processed exclusively by the client + in order to navigate within a larger document, and room cannot + be considered a "document". Each event can be retrieved from the server + individually, so each event can be viewed as a self-contained document. + When URI processing is shifted to the server-side, servers are not even + going to receive fragments (as per RFC 3986). +1. _Interoperability with + [Linked Data](https://en.wikipedia.org/wiki/Linked_data)_ is out of + scope of this MSC but worth being considered separately. +1. _How does this MSC work with closed federations?_ If you need to + communicate a URI to the bigger world where you cannot expect + the consumer to know in advance which federation they should use - + supply any server of the closed federation in the authority part. + Users inside the closed federation can omit the authority part if + they know the URI is not going to be used outside this federation. + Clients can facilitate that by having an option to always add or omit + the authority part in generated URIs for a given user account. + + +## Further evolution + +This MSC is obviously just the first step, opening the door for a range of +extensions, especially to the query part. Possible actions may include +opening the +[canonical direct chat](https://github.com/matrix-org/matrix-doc/pull/2199) +(`action=chat`, following the existing convention in Riot), +leaving a room (`action=leave`); bounds for a segment of the room timeline +(`from=$evtid1&to=$evtid2`) also look worthwhile. + + +## Alternatives + +The discussion in +[MSC455](https://github.com/matrix-org/matrix-doc/issues/455) +mentions an option to standardise URNs rather than URLs/URIs, +with the list of resolvers being user-specific. While a URN namespace +such as `urn:matrix:`, along with a URN scheme, might be deemed useful +once we shift to (even) more decentralised structure of the network, +`urn:` URIs must be managed entities (see +[RFC 8141](https://tools.ietf.org/html/rfc8141)) which is not always +the case in Matrix (consider room aliases, e.g.). + +With that said, a URN-styled (`matrix:room:example.org:roomalias`) +option was considered. However, Matrix already uses colon (`:`) as +a delimiter of id parts and, as can be seen above, reversing the parts +to meet the URN's hierarchical order would look confusing for Matrix +users. + +Yet another alternative considered was to go "full REST" and build +a more traditional looking URL structure with serverparts coming first +followed by type grouping (sic - not specifiers) and then by localparts, +i.e. `matrix://example.org/rooms/roomalias`. This is even more +difficult to comprehend for a Matrix user than the previous alternative +and besides it conflates the notion of an authority server with +that of a namespace (`example.org` above is a server part of an alias, +not the name of a homeserver that should be used to resolve the URI). + + +## Potential issues + +Despite the limited functionality of URIs as proposed in this MSC, +Matrix authors are advised to use tools that would process URIs just +like an http(s) URI instead of making a quick home-baked +parsers/emitters. Even with that in mind, not all tools normalise and +sanitise all cases in a fully RFC-compliant way. This MSC tries to keep +the required transformations to the minimum and will likely not bring +much grief even with naive implementations; however, as functionality +of Matrix URI grows, the number of corner cases will increase. + + +## Security considerations + +This MSC mostly builds on RFC 3986 but tries to reduce the scope +as much as possible. Notably, it avoids introducing complex traversable +structures and defines the URI grammar quite strictly. Notably, +dot path segments (`.` and `..`), while potentially useful when URIs +become richer, would come too much ahead of time for now. Care is taken +to not make essential parts of the URI omittable to avoid +even accidental misrepresentation of a local resource for a remote one +in Matrix and vice versa. One part still omittable - authority - can +result in leaks if a non-fully-qualified URI from one closed federation +is sent to another closed federation; this is considered an extremely +corner case. + +The MSC intentionally doesn't support conveying any kind of user +information in URIs. + + +## Conclusion + +A dedicated URI scheme is well overdue for Matrix. Many other networks +already have got one for themselves, benefiting both in terms of +branding (compare `matrix:room/weruletheworld:example.org` vs. +`#weruletheworld:example.org` from the standpoint of someone who +hasn't been to Matrix) and interoperability (`matrix.to` requires +opening a browser while clicking a `tg:` link dumped to the terminal +application will open the correct application for Telegram without +user intervention or can even offer to install one, if needed). +The proposed syntax makes conversion between Matrix URIs +and Matrix identifiers as easy as a bunch of regular expressions; so +even though client-side processing of URIs might not be optimal +longer-term, it's a very simple and quick way that allows plenty +of experimentation early on.