Use abbreviated type specifiers

As per the review, this commit introduces Reddit-style type specifiers
for user ids (u/), room aliases (r/), and event ids (e/).
pull/977/head
Alexey Rusakov 4 years ago
parent 246a97e2be
commit b4269a54e8

@ -39,7 +39,7 @@ To cover the use cases above, the following scheme is proposed for Matrix URIs
```text ```text
matrix:[//{authority}/]{type}/{id without sigil}[/{type}/{id without sigil}...][?{query}][#{fragment}] matrix:[//{authority}/]{type}/{id without sigil}[/{type}/{id without sigil}...][?{query}][#{fragment}]
``` ```
with `{type}` defining the resource type (such as `user` or `roomid` - see with `{type}` defining the resource type (such as `r`, `u` or `roomid` - see
the "Path" section in the proposal) and `{query}` containing additional hints the "Path" section in the proposal) and `{query}` containing additional hints
or request details on the Matrix entity (see "Query" in the proposal). or request details on the Matrix entity (see "Query" in the proposal).
`{authority}` and `{fragment}` parts are reserved for future use; this proposal `{authority}` and `{fragment}` parts are reserved for future use; this proposal
@ -65,11 +65,11 @@ pointing to an event in a room.
Examples: Examples:
* Room `#someroom:example.org`: * Room `#someroom:example.org`:
`matrix:room/someroom:example.org` `matrix:r/someroom:example.org`
* User `@me:example.org`: * User `@me:example.org`:
`matrix:user/me:example.org` `matrix:u/me:example.org`
* Event in a room: * Event in a room:
`matrix:room/someroom:example.org/event/Arbitrary_Event_Id` `matrix:r/someroom:example.org/e/Arbitrary_Event_Id`
* [A commit like this](https://github.com/her001/steamlug.org/commit/2bd69441e1cf21f626e699f0957193f45a1d560f) * [A commit like this](https://github.com/her001/steamlug.org/commit/2bd69441e1cf21f626e699f0957193f45a1d560f)
could make use of a Matrix URI in the form of could make use of a Matrix URI in the form of
`<a href="{Matrix URI}">{Matrix identifier}</a>`. `<a href="{Matrix URI}">{Matrix identifier}</a>`.
@ -187,7 +187,7 @@ be omitted. As can be seen below, Matrix URI rely heavily on [relative
references](https://tools.ietf.org/html/rfc3986#section-4.2) and references](https://tools.ietf.org/html/rfc3986#section-4.2) and
omitting the scheme name makes them indistinguishable from a local path omitting the scheme name makes them indistinguishable from a local path
that might have nothing to do with Matrix. Clients MUST NOT try to that might have nothing to do with Matrix. Clients MUST NOT try to
parse pieces like `room/MyRoom:example.org` as Matrix URIs; instead, parse pieces like `r/MyRoom:example.org` as Matrix URIs; instead,
users should be encouraged to use Matrix identifiers for in-text references users should be encouraged to use Matrix identifiers for in-text references
(`#MyRoom:example.org`) and client applications SHOULD turn them into (`#MyRoom:example.org`) and client applications SHOULD turn them into
hyperlinks to Matrix URIs. hyperlinks to Matrix URIs.
@ -232,29 +232,38 @@ This MSC only proposes mappings along `type-qualifier id-without-sigil` syntax;
For the sake of integrity future `nonid-segment` extensions must follow For the sake of integrity future `nonid-segment` extensions must follow
[the ABNF for `segment-nz` as defined in RFC 3986](https://tools.ietf.org/html/rfc3986#appendix-A). [the ABNF for `segment-nz` as defined in RFC 3986](https://tools.ietf.org/html/rfc3986#appendix-A).
This MSC defines the following `type` specifiers: This MSC defines the following `type` specifiers: `u` (user id, sigil `@`),
`user` (user id, sigil `@`), `roomid` (room id, sigil `!`), `r` (room alias, sigil `#`), `roomid` (room id, sigil `!`), and
`room` (room alias, sigil `#`), and `event` (event id, sigil `$`). This MSC `e` (event id, sigil `$`). This MSC does not define a type specifier for sigil `+`
does not define a type specifier for sigil `+`
([groups](https://github.com/matrix-org/matrix-doc/issues/1513) aka communities ([groups](https://github.com/matrix-org/matrix-doc/issues/1513) aka communities
or, in the more recent incarnation, or, in the more recent incarnation,
[spaces](https://github.com/matrix-org/matrix-doc/pull/1772)); a separate MSC [spaces](https://github.com/matrix-org/matrix-doc/pull/1772)); a separate MSC
can introduce the specifier, along with the parsing/construction logic and can introduce the specifier, along with the parsing/construction logic and
relevant CS API invocations, following the framework of this proposal. relevant CS API invocations, following the framework of this proposal.
As of this MSC, `user`, `roomid`, and `room` can only be at the top The following type specifiers proposed in earlier editions of this MSC and
level. The type `event` can only be used on the 2nd level and only under `room` already in use in several implementations, are deprecated: `user`, `room`, and
or `roomid`; this is driven by the current shape of Client-Server API that `event`. Client applications MAY parse these specifiers as if they were
does not provide a non-deprecated way to retrieve an event without knowing `u`, `r`, and `e` respectively; they MUST NOT emit URIs with the deprecated
specifiers. The rationale behind the switch is laid out in "Alternatives".
As of this MSC, `u`, `r`, and `roomid` can only be at the top
level. The type `e` (event) can only be used on the 2nd level and only under
`r` or `roomid`; this is driven by the current shape of Client-Server API
that does not provide a non-deprecated way to retrieve an event without knowing
the room (see [MSC2695](https://github.com/matrix-org/matrix-doc/pull/2695) and the room (see [MSC2695](https://github.com/matrix-org/matrix-doc/pull/2695) and
[MSC2779](https://github.com/matrix-org/matrix-doc/issues/2779) that may [MSC2779](https://github.com/matrix-org/matrix-doc/issues/2779) that may
change this). change this).
Further MSCs may introduce navigation to more top-level as well as Further MSCs may introduce navigation to more top-level as well as
non-top-level objects; see "Further evolution" for some ideas. These new non-top-level objects; see "Further evolution" for some ideas. These new
proposals SHOULD follow the generic grammar laid out above, adding new `type` proposals SHOULD follow the generic grammar laid out above, adding new `type`
and `nonid-segment` specifiers and/or allowing them in other levels, rather and `nonid-segment` specifiers and/or allowing them in other levels, rather
than introduce a new grammar. than introduce a new grammar. It is recommended to only use abbreviated
single-letter specifiers if they are expected to be user visible and convenient
for type-in; if a URI for a given resource type is usually generated
(e.g. because the corresponding identifier is not human-friendly), it's
RECOMMENDED to use full (though short) words to avoid ambiguity and confusion.
`id-without-sigil` is defined as the `string` part of Matrix `id-without-sigil` is defined as the `string` part of Matrix
[Common identifier format](https://matrix.org/docs/spec/appendices#common-identifier-format) [Common identifier format](https://matrix.org/docs/spec/appendices#common-identifier-format)
@ -377,9 +386,9 @@ comparisons are case-INsensitive.
a. Pick the leftmost segment of `path` until `/` (path segment) and match a. Pick the leftmost segment of `path` until `/` (path segment) and match
it against the following list to produce `sigil-1`: it against the following list to produce `sigil-1`:
- `user` -> `@` - `u` (or, optionally, `user` - see "Path") -> `@`
- `r` (or, optionally, `room`) -> `#`
- `roomid` -> `!` - `roomid` -> `!`
- `room` -> `#`
- any other string, including an empty one -> fail parsing: - any other string, including an empty one -> fail parsing:
the Matrix URI is invalid. the Matrix URI is invalid.
@ -393,7 +402,7 @@ comparisons are case-INsensitive.
point to an event inside the room identified by `mxid-1`: point to an event inside the room identified by `mxid-1`:
a. Pick the next (3rd) path segment: a. Pick the next (3rd) path segment:
- if the segment is exactly `event`, proceed; - if the segment is exactly `e` (or, optionally, `event`), proceed;
- otherwise, including the case of an empty segment (trailing `/`, e.g.), - otherwise, including the case of an empty segment (trailing `/`, e.g.),
fail parsing. fail parsing.
@ -431,11 +440,11 @@ performed on behalf (using the access token) of the user `@me:example.org`:
| URI class/example | Interactive operation | Non-interactive operation / Involved CS API | | URI class/example | Interactive operation | Non-interactive operation / Involved CS API |
| ----------------- | --------------------- | --------------------------------------------- | | ----------------- | --------------------- | --------------------------------------------- |
| User Id (no `action` in URI):<br/>`matrix:user/her:example.org` | _Outside the room context_: show user profile<br/>_Inside the room context:_ mention the user in the current room (client-local operation) | No default non-interactive operation<br/>`GET /profile/@her:example.org/display_name`<br/>`GET /profile/@her:example.org/avatar_url` | | User Id (no `action` in URI):<br/>`matrix:u/her:example.org` | _Outside the room context_: show user profile<br/>_Inside the room context:_ mention the user in the current room (client-local operation) | No default non-interactive operation<br/>`GET /profile/@her:example.org/display_name`<br/>`GET /profile/@her:example.org/avatar_url` |
| User Id (`action=chat`):<br/>`matrix:user/her:example.org?action=chat` | Open a direct chat with the user (see the next column on identifying the room) | If [canonical direct chats](https://github.com/matrix-org/matrix-doc/pull/2199) are supported: `GET /_matrix/client/r0/user/@me:example.org/dm?involves=@her:example.org`<br/>Without canonical direct chats:<br/>1. `GET /user/@me:example.org/account_data/m.direct`<br/>2. Find the room id for `@her:example.org` in the event content<br/>3. if found, return this room id; if not, `POST /createRoom` with `"is_direct": true` and return id of the created room | | User Id (`action=chat`):<br/>`matrix:u/her:example.org?action=chat` | Open a direct chat with the user (see the next column on identifying the room) | If [canonical direct chats](https://github.com/matrix-org/matrix-doc/pull/2199) are supported: `GET /_matrix/client/r0/user/@me:example.org/dm?involves=@her:example.org`<br/>Without canonical direct chats:<br/>1. `GET /user/@me:example.org/account_data/m.direct`<br/>2. Find the room id for `@her:example.org` in the event content<br/>3. if found, return this room id; if not, `POST /createRoom` with `"is_direct": true` and return id of the created room |
| Room (no `action` in URI):<br/>`matrix:roomid/rid:example.org`<br/>`matrix:room/us:example.org` | Attempt to "open" (usually: display the timeline at the latest or last remembered position) the room | No default non-interactive operation<br/>API: Find the respective room in the local `/sync` cache or<br/>`GET /rooms/!rid:example.org/...`<br/> | | Room (no `action` in URI):<br/>`matrix:roomid/rid:example.org`<br/>`matrix:r/us:example.org` | Attempt to "open" (usually: display the timeline at the latest or last remembered position) the room | No default non-interactive operation<br/>API: Find the respective room in the local `/sync` cache or<br/>`GET /rooms/!rid:example.org/...`<br/> |
| Room (`action=join`):<br/>`matrix:roomid/rid:example.org?action=join&via=example2.org`<br/>`matrix:room/us:example.org?action=join` | Attempt to join the room | `POST /join/!rid:example.org?server_name=example2.org`<br/>`POST /join/#us:example.org` | | Room (`action=join`):<br/>`matrix:roomid/rid:example.org?action=join&via=example2.org`<br/>`matrix:r/us:example.org?action=join` | Attempt to join the room | `POST /join/!rid:example.org?server_name=example2.org`<br/>`POST /join/#us:example.org` |
| Event:<br/>`matrix:room/us:example.org/event/lol823y4bcp3qo4`<br/>`matrix:roomid/rid:example.org/event/lol823y4bcp3qo4?via=example2.org` | 1. For room aliases, resolve an alias to a room id (HOW?)<br/>2. Attempt to retrieve (see the next column) and display the event;<br/>3. If the event could not be retrieved due to access denial and the current user is not a member of the room, the client MAY offer the user to join the room and try to open the event again | Non-interactive operation: return event or event content, depending on context<br/>API: find the event in the local `/sync` cache or<br/>`GET /directory/room/%23us:example.org` (to resolve alias to id)<br/>`GET /rooms/!rid:example.org/event/lol823y4bcp3qo4?server_name=example2.org`<br/> | | Event:<br/>`matrix:r/us:example.org/e/lol823y4bcp3qo4`<br/>`matrix:roomid/rid:example.org/event/lol823y4bcp3qo4?via=example2.org` | 1. For room aliases, resolve an alias to a room id (HOW?)<br/>2. Attempt to retrieve (see the next column) and display the event;<br/>3. If the event could not be retrieved due to access denial and the current user is not a member of the room, the client MAY offer the user to join the room and try to open the event again | Non-interactive operation: return event or event content, depending on context<br/>API: find the event in the local `/sync` cache or<br/>`GET /directory/room/%23us:example.org` (to resolve alias to id)<br/>`GET /rooms/!rid:example.org/event/lol823y4bcp3qo4?server_name=example2.org`<br/> |
#### URI construction algorithm #### URI construction algorithm
@ -447,9 +456,9 @@ compliance of identifiers passed to this algorithm.
For room and user identifiers (including room aliases): For room and user identifiers (including room aliases):
1. Remove the sigil character from the identifier and match it against 1. Remove the sigil character from the identifier and match it against
the following list to produce `prefix-1`: the following list to produce `prefix-1`:
- `@` -> `user/` - `@` -> `u/`
- `#` -> `r/`
- `!` -> `roomid/` - `!` -> `roomid/`
- `#` -> `room/`
2. Build the Matrix URI as a concatenation of: 2. Build the Matrix URI as a concatenation of:
- literal `matrix:`; - literal `matrix:`;
- `prefix-1`; - `prefix-1`;
@ -463,7 +472,7 @@ may change this):
1. Take the event's room id or canonical alias and build a Matrix URI for them 1. Take the event's room id or canonical alias and build a Matrix URI for them
as described above. as described above.
2. Append to the result of previous step: 2. Append to the result of previous step:
- literal `event/`; - literal `e/`;
- the event id after removing the sigil (`$`) and percent-encoding. - the event id after removing the sigil (`$`) and percent-encoding.
Clients MUST implement proper percent-encoding of the identifiers; there's no Clients MUST implement proper percent-encoding of the identifiers; there's no
@ -481,7 +490,7 @@ extensions. Here are a few ideas:
* Add specifying a segment of the room timeline (`from=$evtid1&to=$evtid2`). * Add specifying a segment of the room timeline (`from=$evtid1&to=$evtid2`).
* Unlock bare event ids (`matrix:event/$event_id`) - subject to change in * Unlock bare event ids (`matrix:e/$event_id`) - subject to change in
other areas of the specification. other areas of the specification.
* Bring tangible semantics to the authority part. The main purpose of * Bring tangible semantics to the authority part. The main purpose of
@ -516,12 +525,12 @@ extensions. Here are a few ideas:
(also referred to in the previous section). (also referred to in the previous section).
* One could conceive a URI mapping of avatars in the form of * One could conceive a URI mapping of avatars in the form of
`matrix:user/uid:matrix.org/avatar/room:matrix.org` `matrix:u/uid:matrix.org/avatar/room:matrix.org`
(a users avatar for a given room). (a users avatar for a given room).
* As described in "Alternatives" and "Discussion points", respectively, one can introduce a synonymous * As described in "Alternatives", a synonymous system can be introduced that
system that uses Matrix identifiers with sigils by adding another path uses Matrix identifiers with sigils by adding another path prefix (e.g.,
prefix (e.g., `matrix:id/%23matrix:matrix.org`). `matrix:id/%23matrix:matrix.org`).
### Past discussion points and tradeoffs ### Past discussion points and tradeoffs
@ -534,8 +543,10 @@ further discussion should happen in GitHub comments.
`//` if the URI doesn't have an authority component. In other words, `//` if the URI doesn't have an authority component. In other words,
`//` implies a centre of authority, and the (public) Matrix `//` implies a centre of authority, and the (public) Matrix
federation is not supposed to have one; hence no `//` in most URIs. federation is not supposed to have one; hence no `//` in most URIs.
1. _Why do type specifiers use singular rather than plural 1. ~~_Why do type specifiers use singular rather than plural
as is common in RESTful APIs?_ as is common in RESTful APIs?_~~
This is no more relevant with single-letter type specifiers. The answer
below is provided for history only.
Unlike in actual RESTful APIs, this MSC does not see `rooms/` or Unlike in actual RESTful APIs, this MSC does not see `rooms/` or
`users/` as collections to browse. The type specifier completes `users/` as collections to browse. The type specifier completes
the id specification in the URI, defining a very specific and the id specification in the URI, defining a very specific and
@ -579,18 +590,40 @@ further discussion should happen in GitHub comments.
### Alternatives ### Alternatives
#### Reddit-style URLs #### Using full words for all types
Reddit style (`matrix:r/matrix:matrix.org`, `matrix:u/me:example.org` etc.) During its draft state, this MSC was proposing type specifiers using full words
is almost as compact as original Matrix identifiers, while still rather (`user`, `room`, `event` etc.), arguing that abbreviations can be introduced
clearly conveys the type and nicely avoids the singular vs. plural confusion separately as synonyms. Full words have several shortcomings pointed out in
described in the previous section. However, in the context of high requirements discussions across the whole period of preparation, namely:
to URL grammar stability, Reddit-style prefixes would eventually produce - The singular vs. plural choice (see also "Past discussion points")
bigger ambiguity as a primary notation; but they can be handy as shortcuts. - Using English words raises a question about eventual support of localised
As discussed in "Future evolution", the current proposal provides enough space URI variants (`matrix:benutzer/...`, `matrix:usuario/...` etc.) catering to
to define synonyms; this may need some canonicalisation service from international audience, that would add complication to the Matrix technology.
homeservers so that we don't have to enable synonyms at each client - Abbreviated forms are popularised by Reddit and make URIs shorter which is
individually. crucial for the outbound integration case (see the introduction).
Meanwhile, using `u`/`r`/`e` for users, rooms and events has the following
advantages:
1. there's a strong Reddit legacy, with users across the world quite familiar
with the abbreviated forms (and `r/` coincidentally standing for sub-Reddits
links to which have basically the same place in the Reddit ecosystem as
Matrix room aliases have in the Matrix ecosystem);
2. matrix.to links to users and room aliases are heavily used throughout Matrix,
specifically in end-user-facing contexts (see also use cases in the
introductory section of this MSC);
3. the singular vs. plural (`room` or `rooms`?) confusion is avoided;
4. it's shorter, which is crucial for typing the URI in an external medium.
The rationale behind not abbreviating `roomid/` is a better distinction between
room aliases and room ids; also, since room ids are almost never typed in
manually, the advantages (3) and (4) above don't hold.
For these reasons, it was decided in the end to use the single-letter style
for types most used in the outbound integration case. It's still possible to
reinstate full words as synonyms some time down the road, with the caveat that
a canonicalisation service from homeservers may be needed to avoid having
to enable synonyms at each client individually.
#### URNs #### URNs

Loading…
Cancel
Save